2015-11-12 01:27:51 +00:00
|
|
|
package agent
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2022-12-14 15:24:22 +00:00
|
|
|
"context"
|
2015-11-12 01:27:51 +00:00
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"net/http"
|
|
|
|
"net/http/httptest"
|
|
|
|
"reflect"
|
2018-09-06 10:34:28 +00:00
|
|
|
"sync/atomic"
|
2015-11-12 01:27:51 +00:00
|
|
|
"testing"
|
|
|
|
|
2018-09-10 15:58:53 +00:00
|
|
|
"github.com/hashicorp/consul/testrpc"
|
|
|
|
|
2023-02-17 20:04:12 +00:00
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
|
2017-07-06 10:34:00 +00:00
|
|
|
"github.com/hashicorp/consul/agent/structs"
|
2018-04-10 12:28:27 +00:00
|
|
|
"github.com/hashicorp/consul/types"
|
2015-11-12 01:27:51 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// MockPreparedQuery is a fake endpoint that we inject into the Consul server
|
|
|
|
// in order to observe the RPC calls made by these HTTP endpoints. This lets
|
|
|
|
// us make sure that the request is being formed properly without having to
|
|
|
|
// set up a realistic environment for prepared queries, which is a huge task and
|
|
|
|
// already done in detail inside the prepared query endpoint's unit tests. If we
|
|
|
|
// can prove this formats proper requests into that then we should be good to
|
|
|
|
// go. We will do a single set of end-to-end tests in here to make sure that the
|
|
|
|
// server is wired up to the right endpoint when not "injected".
|
|
|
|
type MockPreparedQuery struct {
|
|
|
|
applyFn func(*structs.PreparedQueryRequest, *string) error
|
|
|
|
getFn func(*structs.PreparedQuerySpecificRequest, *structs.IndexedPreparedQueries) error
|
|
|
|
listFn func(*structs.DCSpecificRequest, *structs.IndexedPreparedQueries) error
|
|
|
|
executeFn func(*structs.PreparedQueryExecuteRequest, *structs.PreparedQueryExecuteResponse) error
|
2016-03-04 01:30:36 +00:00
|
|
|
explainFn func(*structs.PreparedQueryExecuteRequest, *structs.PreparedQueryExplainResponse) error
|
2015-11-12 01:27:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (m *MockPreparedQuery) Apply(args *structs.PreparedQueryRequest,
|
|
|
|
reply *string) (err error) {
|
|
|
|
if m.applyFn != nil {
|
|
|
|
return m.applyFn(args, reply)
|
|
|
|
}
|
|
|
|
return fmt.Errorf("should not have called Apply")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *MockPreparedQuery) Get(args *structs.PreparedQuerySpecificRequest,
|
|
|
|
reply *structs.IndexedPreparedQueries) error {
|
|
|
|
if m.getFn != nil {
|
|
|
|
return m.getFn(args, reply)
|
|
|
|
}
|
|
|
|
return fmt.Errorf("should not have called Get")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *MockPreparedQuery) List(args *structs.DCSpecificRequest,
|
|
|
|
reply *structs.IndexedPreparedQueries) error {
|
|
|
|
if m.listFn != nil {
|
|
|
|
return m.listFn(args, reply)
|
|
|
|
}
|
|
|
|
return fmt.Errorf("should not have called List")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *MockPreparedQuery) Execute(args *structs.PreparedQueryExecuteRequest,
|
|
|
|
reply *structs.PreparedQueryExecuteResponse) error {
|
|
|
|
if m.executeFn != nil {
|
|
|
|
return m.executeFn(args, reply)
|
|
|
|
}
|
|
|
|
return fmt.Errorf("should not have called Execute")
|
|
|
|
}
|
|
|
|
|
2016-03-04 01:30:36 +00:00
|
|
|
func (m *MockPreparedQuery) Explain(args *structs.PreparedQueryExecuteRequest,
|
|
|
|
reply *structs.PreparedQueryExplainResponse) error {
|
|
|
|
if m.explainFn != nil {
|
|
|
|
return m.explainFn(args, reply)
|
2016-03-03 09:04:12 +00:00
|
|
|
}
|
2016-03-04 01:30:36 +00:00
|
|
|
return fmt.Errorf("should not have called Explain")
|
2016-03-03 09:04:12 +00:00
|
|
|
}
|
|
|
|
|
2015-11-12 01:27:51 +00:00
|
|
|
func TestPreparedQuery_Create(t *testing.T) {
|
2020-12-07 18:42:55 +00:00
|
|
|
if testing.Short() {
|
|
|
|
t.Skip("too slow for testing.Short")
|
|
|
|
}
|
|
|
|
|
2017-05-21 07:54:40 +00:00
|
|
|
t.Parallel()
|
2020-03-31 19:59:56 +00:00
|
|
|
a := NewTestAgent(t, "")
|
2017-05-21 18:31:20 +00:00
|
|
|
defer a.Shutdown()
|
2015-11-12 01:27:51 +00:00
|
|
|
|
2017-07-05 10:38:11 +00:00
|
|
|
m := MockPreparedQuery{
|
|
|
|
applyFn: func(args *structs.PreparedQueryRequest, reply *string) error {
|
|
|
|
expected := &structs.PreparedQueryRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Op: structs.PreparedQueryCreate,
|
|
|
|
Query: &structs.PreparedQuery{
|
|
|
|
Name: "my-query",
|
|
|
|
Session: "my-session",
|
|
|
|
Service: structs.ServiceQuery{
|
|
|
|
Service: "my-service",
|
2022-07-22 13:14:43 +00:00
|
|
|
Failover: structs.QueryFailoverOptions{
|
2017-07-05 10:38:11 +00:00
|
|
|
NearestN: 4,
|
|
|
|
Datacenters: []string{"dc1", "dc2"},
|
|
|
|
},
|
2018-04-10 12:28:27 +00:00
|
|
|
IgnoreCheckIDs: []types.CheckID{"broken_check"},
|
|
|
|
OnlyPassing: true,
|
|
|
|
Tags: []string{"foo", "bar"},
|
|
|
|
NodeMeta: map[string]string{"somekey": "somevalue"},
|
Improve Connect with Prepared Queries (#5291)
Given a query like:
```
{
"Name": "tagged-connect-query",
"Service": {
"Service": "foo",
"Tags": ["tag"],
"Connect": true
}
}
```
And a Consul configuration like:
```
{
"services": [
"name": "foo",
"port": 8080,
"connect": { "sidecar_service": {} },
"tags": ["tag"]
]
}
```
If you executed the query it would always turn up with 0 results. This was because the sidecar service was being created without any tags. You could instead make your config look like:
```
{
"services": [
"name": "foo",
"port": 8080,
"connect": { "sidecar_service": {
"tags": ["tag"]
} },
"tags": ["tag"]
]
}
```
However that is a bit redundant for most cases. This PR ensures that the tags and service meta of the parent service get copied to the sidecar service. If there are any tags or service meta set in the sidecar service definition then this copying does not take place. After the changes, the query will now return the expected results.
A second change was made to prepared queries in this PR which is to allow filtering on ServiceMeta just like we allow for filtering on NodeMeta.
2019-02-04 14:36:51 +00:00
|
|
|
ServiceMeta: map[string]string{"env": "prod"},
|
2017-07-05 10:38:11 +00:00
|
|
|
},
|
|
|
|
DNS: structs.QueryDNSOptions{
|
|
|
|
TTL: "10s",
|
2015-11-12 01:27:51 +00:00
|
|
|
},
|
|
|
|
},
|
2017-07-05 10:38:11 +00:00
|
|
|
WriteRequest: structs.WriteRequest{
|
|
|
|
Token: "my-token",
|
2015-11-12 01:27:51 +00:00
|
|
|
},
|
2017-07-05 10:38:11 +00:00
|
|
|
}
|
|
|
|
if !reflect.DeepEqual(args, expected) {
|
|
|
|
t.Fatalf("bad: %v", args)
|
|
|
|
}
|
2015-11-12 01:27:51 +00:00
|
|
|
|
2017-07-05 10:38:11 +00:00
|
|
|
*reply = "my-id"
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
if err := a.registerEndpoint("PreparedQuery", &m); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
2017-05-21 18:31:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
body := bytes.NewBuffer(nil)
|
|
|
|
enc := json.NewEncoder(body)
|
|
|
|
raw := map[string]interface{}{
|
|
|
|
"Name": "my-query",
|
|
|
|
"Session": "my-session",
|
|
|
|
"Service": map[string]interface{}{
|
|
|
|
"Service": "my-service",
|
|
|
|
"Failover": map[string]interface{}{
|
|
|
|
"NearestN": 4,
|
|
|
|
"Datacenters": []string{"dc1", "dc2"},
|
|
|
|
},
|
2018-04-10 12:28:27 +00:00
|
|
|
"IgnoreCheckIDs": []string{"broken_check"},
|
|
|
|
"OnlyPassing": true,
|
|
|
|
"Tags": []string{"foo", "bar"},
|
|
|
|
"NodeMeta": map[string]string{"somekey": "somevalue"},
|
Improve Connect with Prepared Queries (#5291)
Given a query like:
```
{
"Name": "tagged-connect-query",
"Service": {
"Service": "foo",
"Tags": ["tag"],
"Connect": true
}
}
```
And a Consul configuration like:
```
{
"services": [
"name": "foo",
"port": 8080,
"connect": { "sidecar_service": {} },
"tags": ["tag"]
]
}
```
If you executed the query it would always turn up with 0 results. This was because the sidecar service was being created without any tags. You could instead make your config look like:
```
{
"services": [
"name": "foo",
"port": 8080,
"connect": { "sidecar_service": {
"tags": ["tag"]
} },
"tags": ["tag"]
]
}
```
However that is a bit redundant for most cases. This PR ensures that the tags and service meta of the parent service get copied to the sidecar service. If there are any tags or service meta set in the sidecar service definition then this copying does not take place. After the changes, the query will now return the expected results.
A second change was made to prepared queries in this PR which is to allow filtering on ServiceMeta just like we allow for filtering on NodeMeta.
2019-02-04 14:36:51 +00:00
|
|
|
"ServiceMeta": map[string]string{"env": "prod"},
|
2017-05-21 18:31:20 +00:00
|
|
|
},
|
|
|
|
"DNS": map[string]interface{}{
|
|
|
|
"TTL": "10s",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
if err := enc.Encode(raw); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
2023-01-24 16:21:41 +00:00
|
|
|
req, _ := http.NewRequest("POST", "/v1/query", body)
|
|
|
|
req.Header.Add("X-Consul-Token", "my-token")
|
2017-05-21 18:31:20 +00:00
|
|
|
resp := httptest.NewRecorder()
|
|
|
|
obj, err := a.srv.PreparedQueryGeneral(resp, req)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
if resp.Code != 200 {
|
|
|
|
t.Fatalf("bad code: %d", resp.Code)
|
|
|
|
}
|
|
|
|
r, ok := obj.(preparedQueryCreateResponse)
|
|
|
|
if !ok {
|
|
|
|
t.Fatalf("unexpected: %T", obj)
|
|
|
|
}
|
|
|
|
if r.ID != "my-id" {
|
|
|
|
t.Fatalf("bad ID: %s", r.ID)
|
|
|
|
}
|
2015-11-12 01:27:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestPreparedQuery_List(t *testing.T) {
|
2020-12-07 18:42:55 +00:00
|
|
|
if testing.Short() {
|
|
|
|
t.Skip("too slow for testing.Short")
|
|
|
|
}
|
|
|
|
|
2017-05-21 07:54:40 +00:00
|
|
|
t.Parallel()
|
2017-05-21 18:31:20 +00:00
|
|
|
t.Run("", func(t *testing.T) {
|
2020-03-31 19:59:56 +00:00
|
|
|
a := NewTestAgent(t, "")
|
2017-05-21 18:31:20 +00:00
|
|
|
defer a.Shutdown()
|
|
|
|
|
2017-07-05 10:38:11 +00:00
|
|
|
m := MockPreparedQuery{
|
|
|
|
listFn: func(args *structs.DCSpecificRequest, reply *structs.IndexedPreparedQueries) error {
|
|
|
|
// Return an empty response.
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
}
|
2017-06-19 14:36:09 +00:00
|
|
|
if err := a.registerEndpoint("PreparedQuery", &m); err != nil {
|
2015-11-15 05:59:23 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
body := bytes.NewBuffer(nil)
|
2017-05-09 11:38:05 +00:00
|
|
|
req, _ := http.NewRequest("GET", "/v1/query", body)
|
2015-11-15 05:59:23 +00:00
|
|
|
resp := httptest.NewRecorder()
|
2017-05-21 18:31:20 +00:00
|
|
|
obj, err := a.srv.PreparedQueryGeneral(resp, req)
|
2015-11-15 05:59:23 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
if resp.Code != 200 {
|
|
|
|
t.Fatalf("bad code: %d", resp.Code)
|
|
|
|
}
|
|
|
|
r, ok := obj.(structs.PreparedQueries)
|
|
|
|
if !ok {
|
|
|
|
t.Fatalf("unexpected: %T", obj)
|
|
|
|
}
|
|
|
|
if r == nil || len(r) != 0 {
|
|
|
|
t.Fatalf("bad: %v", r)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2017-05-21 18:31:20 +00:00
|
|
|
t.Run("", func(t *testing.T) {
|
2020-03-31 19:59:56 +00:00
|
|
|
a := NewTestAgent(t, "")
|
2017-05-21 18:31:20 +00:00
|
|
|
defer a.Shutdown()
|
|
|
|
|
2017-07-05 10:38:11 +00:00
|
|
|
m := MockPreparedQuery{
|
|
|
|
listFn: func(args *structs.DCSpecificRequest, reply *structs.IndexedPreparedQueries) error {
|
|
|
|
expected := &structs.DCSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryOptions: structs.QueryOptions{
|
|
|
|
Token: "my-token",
|
|
|
|
RequireConsistent: true,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
if !reflect.DeepEqual(args, expected) {
|
|
|
|
t.Fatalf("bad: %v", args)
|
|
|
|
}
|
|
|
|
|
|
|
|
query := &structs.PreparedQuery{
|
|
|
|
ID: "my-id",
|
|
|
|
}
|
|
|
|
reply.Queries = append(reply.Queries, query)
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
}
|
2017-06-19 14:36:09 +00:00
|
|
|
if err := a.registerEndpoint("PreparedQuery", &m); err != nil {
|
2015-11-12 01:27:51 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
body := bytes.NewBuffer(nil)
|
2023-01-24 16:21:41 +00:00
|
|
|
req, _ := http.NewRequest("GET", "/v1/query?consistent=true", body)
|
|
|
|
req.Header.Add("X-Consul-Token", "my-token")
|
2015-11-12 01:27:51 +00:00
|
|
|
resp := httptest.NewRecorder()
|
2017-05-21 18:31:20 +00:00
|
|
|
obj, err := a.srv.PreparedQueryGeneral(resp, req)
|
2015-11-12 01:27:51 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
if resp.Code != 200 {
|
|
|
|
t.Fatalf("bad code: %d", resp.Code)
|
|
|
|
}
|
|
|
|
r, ok := obj.(structs.PreparedQueries)
|
|
|
|
if !ok {
|
|
|
|
t.Fatalf("unexpected: %T", obj)
|
|
|
|
}
|
|
|
|
if len(r) != 1 || r[0].ID != "my-id" {
|
|
|
|
t.Fatalf("bad: %v", r)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestPreparedQuery_Execute(t *testing.T) {
|
2020-12-07 18:42:55 +00:00
|
|
|
if testing.Short() {
|
|
|
|
t.Skip("too slow for testing.Short")
|
|
|
|
}
|
|
|
|
|
2017-05-21 07:54:40 +00:00
|
|
|
t.Parallel()
|
2017-05-21 18:31:20 +00:00
|
|
|
t.Run("", func(t *testing.T) {
|
2020-03-31 19:59:56 +00:00
|
|
|
a := NewTestAgent(t, "")
|
2017-05-21 18:31:20 +00:00
|
|
|
defer a.Shutdown()
|
|
|
|
|
2017-07-05 10:38:11 +00:00
|
|
|
m := MockPreparedQuery{
|
|
|
|
executeFn: func(args *structs.PreparedQueryExecuteRequest, reply *structs.PreparedQueryExecuteResponse) error {
|
|
|
|
// Just return an empty response.
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
}
|
2017-06-19 14:36:09 +00:00
|
|
|
if err := a.registerEndpoint("PreparedQuery", &m); err != nil {
|
2015-11-15 05:59:23 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
body := bytes.NewBuffer(nil)
|
2017-05-09 11:38:05 +00:00
|
|
|
req, _ := http.NewRequest("GET", "/v1/query/my-id/execute", body)
|
2015-11-15 05:59:23 +00:00
|
|
|
resp := httptest.NewRecorder()
|
2017-05-21 18:31:20 +00:00
|
|
|
obj, err := a.srv.PreparedQuerySpecific(resp, req)
|
2015-11-15 05:59:23 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
if resp.Code != 200 {
|
|
|
|
t.Fatalf("bad code: %d", resp.Code)
|
|
|
|
}
|
|
|
|
r, ok := obj.(structs.PreparedQueryExecuteResponse)
|
|
|
|
if !ok {
|
|
|
|
t.Fatalf("unexpected: %T", obj)
|
|
|
|
}
|
|
|
|
if r.Nodes == nil || len(r.Nodes) != 0 {
|
|
|
|
t.Fatalf("bad: %v", r)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2017-05-21 18:31:20 +00:00
|
|
|
t.Run("", func(t *testing.T) {
|
2020-03-31 19:59:56 +00:00
|
|
|
a := NewTestAgent(t, "")
|
2017-05-21 18:31:20 +00:00
|
|
|
defer a.Shutdown()
|
|
|
|
|
2017-07-05 10:38:11 +00:00
|
|
|
m := MockPreparedQuery{
|
|
|
|
executeFn: func(args *structs.PreparedQueryExecuteRequest, reply *structs.PreparedQueryExecuteResponse) error {
|
|
|
|
expected := &structs.PreparedQueryExecuteRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryIDOrName: "my-id",
|
|
|
|
Limit: 5,
|
|
|
|
Source: structs.QuerySource{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Node: "my-node",
|
|
|
|
},
|
|
|
|
Agent: structs.QuerySource{
|
|
|
|
Datacenter: a.Config.Datacenter,
|
|
|
|
Node: a.Config.NodeName,
|
|
|
|
},
|
|
|
|
QueryOptions: structs.QueryOptions{
|
|
|
|
Token: "my-token",
|
|
|
|
RequireConsistent: true,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
if !reflect.DeepEqual(args, expected) {
|
|
|
|
t.Fatalf("bad: %v", args)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Just set something so we can tell this is returned.
|
|
|
|
reply.Failovers = 99
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
}
|
2017-06-19 14:36:09 +00:00
|
|
|
if err := a.registerEndpoint("PreparedQuery", &m); err != nil {
|
2015-11-12 01:27:51 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
body := bytes.NewBuffer(nil)
|
2023-01-24 16:21:41 +00:00
|
|
|
req, _ := http.NewRequest("GET", "/v1/query/my-id/execute?consistent=true&near=my-node&limit=5", body)
|
|
|
|
req.Header.Add("X-Consul-Token", "my-token")
|
2015-11-12 01:27:51 +00:00
|
|
|
resp := httptest.NewRecorder()
|
2017-05-21 18:31:20 +00:00
|
|
|
obj, err := a.srv.PreparedQuerySpecific(resp, req)
|
2015-11-12 01:27:51 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
if resp.Code != 200 {
|
|
|
|
t.Fatalf("bad code: %d", resp.Code)
|
|
|
|
}
|
|
|
|
r, ok := obj.(structs.PreparedQueryExecuteResponse)
|
2018-04-10 19:35:54 +00:00
|
|
|
if !ok {
|
|
|
|
t.Fatalf("unexpected: %T", obj)
|
|
|
|
}
|
|
|
|
if r.Failovers != 99 {
|
|
|
|
t.Fatalf("bad: %v", r)
|
|
|
|
}
|
|
|
|
})
|
2018-04-13 16:57:25 +00:00
|
|
|
|
2018-04-10 19:35:54 +00:00
|
|
|
t.Run("", func(t *testing.T) {
|
2020-03-31 19:59:56 +00:00
|
|
|
a := NewTestAgent(t, "")
|
2018-04-10 19:35:54 +00:00
|
|
|
defer a.Shutdown()
|
|
|
|
|
|
|
|
m := MockPreparedQuery{
|
|
|
|
executeFn: func(args *structs.PreparedQueryExecuteRequest, reply *structs.PreparedQueryExecuteResponse) error {
|
|
|
|
expected := &structs.PreparedQueryExecuteRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryIDOrName: "my-id",
|
|
|
|
Limit: 5,
|
|
|
|
Source: structs.QuerySource{
|
|
|
|
Datacenter: "dc1",
|
2018-04-13 16:57:25 +00:00
|
|
|
Node: "_ip",
|
2018-04-10 19:35:54 +00:00
|
|
|
Ip: "127.0.0.1",
|
|
|
|
},
|
|
|
|
Agent: structs.QuerySource{
|
|
|
|
Datacenter: a.Config.Datacenter,
|
|
|
|
Node: a.Config.NodeName,
|
|
|
|
},
|
|
|
|
QueryOptions: structs.QueryOptions{
|
|
|
|
Token: "my-token",
|
|
|
|
RequireConsistent: true,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
if !reflect.DeepEqual(args, expected) {
|
|
|
|
t.Fatalf("bad: %v", args)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Just set something so we can tell this is returned.
|
|
|
|
reply.Failovers = 99
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
if err := a.registerEndpoint("PreparedQuery", &m); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
body := bytes.NewBuffer(nil)
|
2023-01-24 16:21:41 +00:00
|
|
|
req, _ := http.NewRequest("GET", "/v1/query/my-id/execute?consistent=true&near=_ip&limit=5", body)
|
|
|
|
req.Header.Add("X-Consul-Token", "my-token")
|
2018-04-10 19:35:54 +00:00
|
|
|
req.Header.Add("X-Forwarded-For", "127.0.0.1")
|
|
|
|
resp := httptest.NewRecorder()
|
|
|
|
obj, err := a.srv.PreparedQuerySpecific(resp, req)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
if resp.Code != 200 {
|
|
|
|
t.Fatalf("bad code: %d", resp.Code)
|
|
|
|
}
|
|
|
|
r, ok := obj.(structs.PreparedQueryExecuteResponse)
|
2018-04-12 14:10:37 +00:00
|
|
|
if !ok {
|
|
|
|
t.Fatalf("unexpected: %T", obj)
|
|
|
|
}
|
|
|
|
if r.Failovers != 99 {
|
|
|
|
t.Fatalf("bad: %v", r)
|
|
|
|
}
|
2018-04-12 14:40:46 +00:00
|
|
|
})
|
2018-04-13 16:57:25 +00:00
|
|
|
|
2018-04-12 14:40:46 +00:00
|
|
|
t.Run("", func(t *testing.T) {
|
2020-03-31 19:59:56 +00:00
|
|
|
a := NewTestAgent(t, "")
|
2018-04-12 14:40:46 +00:00
|
|
|
defer a.Shutdown()
|
|
|
|
|
|
|
|
m := MockPreparedQuery{
|
|
|
|
executeFn: func(args *structs.PreparedQueryExecuteRequest, reply *structs.PreparedQueryExecuteResponse) error {
|
|
|
|
expected := &structs.PreparedQueryExecuteRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryIDOrName: "my-id",
|
|
|
|
Limit: 5,
|
|
|
|
Source: structs.QuerySource{
|
|
|
|
Datacenter: "dc1",
|
2018-04-13 16:57:25 +00:00
|
|
|
Node: "_ip",
|
2018-04-12 14:40:46 +00:00
|
|
|
Ip: "198.18.0.1",
|
|
|
|
},
|
|
|
|
Agent: structs.QuerySource{
|
|
|
|
Datacenter: a.Config.Datacenter,
|
|
|
|
Node: a.Config.NodeName,
|
|
|
|
},
|
|
|
|
QueryOptions: structs.QueryOptions{
|
|
|
|
Token: "my-token",
|
|
|
|
RequireConsistent: true,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
if !reflect.DeepEqual(args, expected) {
|
|
|
|
t.Fatalf("bad: %v", args)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Just set something so we can tell this is returned.
|
|
|
|
reply.Failovers = 99
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
if err := a.registerEndpoint("PreparedQuery", &m); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
body := bytes.NewBuffer(nil)
|
2023-01-24 16:21:41 +00:00
|
|
|
req, _ := http.NewRequest("GET", "/v1/query/my-id/execute?consistent=true&near=_ip&limit=5", body)
|
|
|
|
req.Header.Add("X-Consul-Token", "my-token")
|
2018-04-12 14:40:46 +00:00
|
|
|
req.Header.Add("X-Forwarded-For", "198.18.0.1")
|
|
|
|
resp := httptest.NewRecorder()
|
|
|
|
obj, err := a.srv.PreparedQuerySpecific(resp, req)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
if resp.Code != 200 {
|
|
|
|
t.Fatalf("bad code: %d", resp.Code)
|
|
|
|
}
|
|
|
|
r, ok := obj.(structs.PreparedQueryExecuteResponse)
|
|
|
|
if !ok {
|
|
|
|
t.Fatalf("unexpected: %T", obj)
|
|
|
|
}
|
|
|
|
if r.Failovers != 99 {
|
|
|
|
t.Fatalf("bad: %v", r)
|
|
|
|
}
|
2018-04-13 16:57:25 +00:00
|
|
|
|
2023-01-24 16:21:41 +00:00
|
|
|
req, _ = http.NewRequest("GET", "/v1/query/my-id/execute?consistent=true&near=_ip&limit=5", body)
|
|
|
|
req.Header.Add("X-Consul-Token", "my-token")
|
2018-04-12 14:40:46 +00:00
|
|
|
req.Header.Add("X-Forwarded-For", "198.18.0.1, 198.19.0.1")
|
2018-04-12 14:10:37 +00:00
|
|
|
resp = httptest.NewRecorder()
|
|
|
|
obj, err = a.srv.PreparedQuerySpecific(resp, req)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
if resp.Code != 200 {
|
|
|
|
t.Fatalf("bad code: %d", resp.Code)
|
|
|
|
}
|
|
|
|
r, ok = obj.(structs.PreparedQueryExecuteResponse)
|
2015-11-12 01:27:51 +00:00
|
|
|
if !ok {
|
|
|
|
t.Fatalf("unexpected: %T", obj)
|
|
|
|
}
|
|
|
|
if r.Failovers != 99 {
|
|
|
|
t.Fatalf("bad: %v", r)
|
|
|
|
}
|
|
|
|
})
|
2015-11-13 20:57:06 +00:00
|
|
|
|
2016-06-30 19:11:48 +00:00
|
|
|
// Ensure the proper params are set when no special args are passed
|
2017-05-21 18:31:20 +00:00
|
|
|
t.Run("", func(t *testing.T) {
|
2020-03-31 19:59:56 +00:00
|
|
|
a := NewTestAgent(t, "")
|
2017-05-21 18:31:20 +00:00
|
|
|
defer a.Shutdown()
|
|
|
|
|
2017-07-05 10:38:11 +00:00
|
|
|
m := MockPreparedQuery{
|
|
|
|
executeFn: func(args *structs.PreparedQueryExecuteRequest, reply *structs.PreparedQueryExecuteResponse) error {
|
|
|
|
if args.Source.Node != "" {
|
|
|
|
t.Fatalf("expect node to be empty, got %q", args.Source.Node)
|
|
|
|
}
|
|
|
|
expect := structs.QuerySource{
|
|
|
|
Datacenter: a.Config.Datacenter,
|
|
|
|
Node: a.Config.NodeName,
|
|
|
|
}
|
|
|
|
if !reflect.DeepEqual(args.Agent, expect) {
|
|
|
|
t.Fatalf("expect: %#v\nactual: %#v", expect, args.Agent)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
}
|
2017-06-19 14:36:09 +00:00
|
|
|
if err := a.registerEndpoint("PreparedQuery", &m); err != nil {
|
2016-06-30 19:11:48 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
2017-05-09 11:38:05 +00:00
|
|
|
req, _ := http.NewRequest("GET", "/v1/query/my-id/execute", nil)
|
2016-06-30 19:11:48 +00:00
|
|
|
resp := httptest.NewRecorder()
|
2017-05-21 18:31:20 +00:00
|
|
|
if _, err := a.srv.PreparedQuerySpecific(resp, req); err != nil {
|
2016-06-30 19:11:48 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2016-08-15 22:34:11 +00:00
|
|
|
// Ensure WAN translation occurs for a response outside of the local DC.
|
2017-05-21 18:31:20 +00:00
|
|
|
t.Run("", func(t *testing.T) {
|
2020-03-31 19:59:56 +00:00
|
|
|
a := NewTestAgent(t, `
|
2017-09-25 18:40:42 +00:00
|
|
|
datacenter = "dc1"
|
|
|
|
translate_wan_addrs = true
|
|
|
|
`)
|
2017-05-21 18:31:20 +00:00
|
|
|
defer a.Shutdown()
|
|
|
|
|
2017-07-05 10:38:11 +00:00
|
|
|
m := MockPreparedQuery{
|
|
|
|
executeFn: func(args *structs.PreparedQueryExecuteRequest, reply *structs.PreparedQueryExecuteResponse) error {
|
|
|
|
nodesResponse := make(structs.CheckServiceNodes, 1)
|
|
|
|
nodesResponse[0].Node = &structs.Node{
|
|
|
|
Node: "foo", Address: "127.0.0.1",
|
|
|
|
TaggedAddresses: map[string]string{
|
|
|
|
"wan": "127.0.0.2",
|
|
|
|
},
|
|
|
|
}
|
2019-06-17 14:51:50 +00:00
|
|
|
nodesResponse[0].Service = &structs.NodeService{
|
|
|
|
Service: "foo",
|
|
|
|
Address: "10.0.1.1",
|
|
|
|
Port: 8080,
|
|
|
|
TaggedAddresses: map[string]structs.ServiceAddress{
|
2020-06-16 17:19:31 +00:00
|
|
|
"wan": {
|
2019-06-17 14:51:50 +00:00
|
|
|
Address: "198.18.0.1",
|
|
|
|
Port: 80,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
2017-07-05 10:38:11 +00:00
|
|
|
reply.Nodes = nodesResponse
|
|
|
|
reply.Datacenter = "dc2"
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
}
|
2019-06-17 14:51:50 +00:00
|
|
|
require.NoError(t, a.registerEndpoint("PreparedQuery", &m))
|
2016-06-15 18:02:51 +00:00
|
|
|
|
|
|
|
body := bytes.NewBuffer(nil)
|
2017-05-09 11:38:05 +00:00
|
|
|
req, _ := http.NewRequest("GET", "/v1/query/my-id/execute?dc=dc2", body)
|
2016-06-15 18:02:51 +00:00
|
|
|
resp := httptest.NewRecorder()
|
2017-05-21 18:31:20 +00:00
|
|
|
obj, err := a.srv.PreparedQuerySpecific(resp, req)
|
2019-06-17 14:51:50 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, 200, resp.Code)
|
2016-06-15 18:02:51 +00:00
|
|
|
r, ok := obj.(structs.PreparedQueryExecuteResponse)
|
2019-06-17 14:51:50 +00:00
|
|
|
require.True(t, ok, "unexpected: %T", obj)
|
|
|
|
require.NotNil(t, r.Nodes)
|
|
|
|
require.Len(t, r.Nodes, 1)
|
2016-06-15 18:02:51 +00:00
|
|
|
|
|
|
|
node := r.Nodes[0]
|
2019-06-17 14:51:50 +00:00
|
|
|
require.NotNil(t, node.Node)
|
|
|
|
require.Equal(t, "127.0.0.2", node.Node.Address)
|
|
|
|
require.NotNil(t, node.Service)
|
|
|
|
require.Equal(t, "198.18.0.1", node.Service.Address)
|
|
|
|
require.Equal(t, 80, node.Service.Port)
|
2016-06-15 18:02:51 +00:00
|
|
|
})
|
|
|
|
|
2016-08-15 22:34:11 +00:00
|
|
|
// Ensure WAN translation doesn't occur for the local DC.
|
2017-05-21 18:31:20 +00:00
|
|
|
t.Run("", func(t *testing.T) {
|
2020-03-31 19:59:56 +00:00
|
|
|
a := NewTestAgent(t, `
|
2017-09-25 18:40:42 +00:00
|
|
|
datacenter = "dc1"
|
|
|
|
translate_wan_addrs = true
|
|
|
|
`)
|
2017-05-21 18:31:20 +00:00
|
|
|
defer a.Shutdown()
|
|
|
|
|
2017-07-05 10:38:11 +00:00
|
|
|
m := MockPreparedQuery{
|
|
|
|
executeFn: func(args *structs.PreparedQueryExecuteRequest, reply *structs.PreparedQueryExecuteResponse) error {
|
|
|
|
nodesResponse := make(structs.CheckServiceNodes, 1)
|
|
|
|
nodesResponse[0].Node = &structs.Node{
|
|
|
|
Node: "foo", Address: "127.0.0.1",
|
|
|
|
TaggedAddresses: map[string]string{
|
|
|
|
"wan": "127.0.0.2",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
reply.Nodes = nodesResponse
|
|
|
|
reply.Datacenter = "dc1"
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
}
|
2017-06-19 14:36:09 +00:00
|
|
|
if err := a.registerEndpoint("PreparedQuery", &m); err != nil {
|
2016-08-15 22:34:11 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
body := bytes.NewBuffer(nil)
|
2017-05-09 11:38:05 +00:00
|
|
|
req, _ := http.NewRequest("GET", "/v1/query/my-id/execute?dc=dc2", body)
|
2016-08-15 22:34:11 +00:00
|
|
|
resp := httptest.NewRecorder()
|
2017-05-21 18:31:20 +00:00
|
|
|
obj, err := a.srv.PreparedQuerySpecific(resp, req)
|
2016-08-15 22:34:11 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
if resp.Code != 200 {
|
|
|
|
t.Fatalf("bad code: %d", resp.Code)
|
|
|
|
}
|
|
|
|
r, ok := obj.(structs.PreparedQueryExecuteResponse)
|
|
|
|
if !ok {
|
|
|
|
t.Fatalf("unexpected: %T", obj)
|
|
|
|
}
|
|
|
|
if r.Nodes == nil || len(r.Nodes) != 1 {
|
|
|
|
t.Fatalf("bad: %v", r)
|
|
|
|
}
|
|
|
|
|
|
|
|
node := r.Nodes[0]
|
|
|
|
if node.Node.Address != "127.0.0.1" {
|
|
|
|
t.Fatalf("bad: %v", node.Node)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2017-05-21 18:31:20 +00:00
|
|
|
t.Run("", func(t *testing.T) {
|
2020-03-31 19:59:56 +00:00
|
|
|
a := NewTestAgent(t, "")
|
2017-05-21 18:31:20 +00:00
|
|
|
defer a.Shutdown()
|
|
|
|
|
2015-11-13 20:57:06 +00:00
|
|
|
body := bytes.NewBuffer(nil)
|
2017-05-09 11:38:05 +00:00
|
|
|
req, _ := http.NewRequest("GET", "/v1/query/not-there/execute", body)
|
2015-11-13 20:57:06 +00:00
|
|
|
resp := httptest.NewRecorder()
|
2022-01-31 16:17:35 +00:00
|
|
|
_, err := a.srv.PreparedQuerySpecific(resp, req)
|
2023-02-17 20:04:12 +00:00
|
|
|
if httpErr, ok := err.(HTTPError); ok {
|
|
|
|
if httpErr.StatusCode != 404 {
|
|
|
|
t.Fatalf("expected status 404 but got %d", httpErr.StatusCode)
|
2022-04-29 17:42:49 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
t.Fatalf("expected HTTP error but got %v", err)
|
2015-11-13 20:57:06 +00:00
|
|
|
}
|
|
|
|
})
|
2015-11-12 01:27:51 +00:00
|
|
|
}
|
|
|
|
|
2018-09-06 10:34:28 +00:00
|
|
|
func TestPreparedQuery_ExecuteCached(t *testing.T) {
|
2020-12-07 18:42:55 +00:00
|
|
|
if testing.Short() {
|
|
|
|
t.Skip("too slow for testing.Short")
|
|
|
|
}
|
|
|
|
|
2018-09-06 10:34:28 +00:00
|
|
|
t.Parallel()
|
|
|
|
|
2020-03-31 19:59:56 +00:00
|
|
|
a := NewTestAgent(t, "")
|
2018-09-06 10:34:28 +00:00
|
|
|
defer a.Shutdown()
|
|
|
|
|
|
|
|
failovers := int32(99)
|
|
|
|
|
|
|
|
m := MockPreparedQuery{
|
|
|
|
executeFn: func(args *structs.PreparedQueryExecuteRequest, reply *structs.PreparedQueryExecuteResponse) error {
|
|
|
|
// Just set something so we can tell this is returned.
|
|
|
|
reply.Failovers = int(atomic.LoadInt32(&failovers))
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
if err := a.registerEndpoint("PreparedQuery", &m); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
doRequest := func(expectFailovers int, expectCache string, revalidate bool) {
|
|
|
|
body := bytes.NewBuffer(nil)
|
|
|
|
req, _ := http.NewRequest("GET", "/v1/query/my-id/execute?cached", body)
|
|
|
|
|
|
|
|
if revalidate {
|
|
|
|
req.Header.Set("Cache-Control", "must-revalidate")
|
|
|
|
}
|
|
|
|
|
|
|
|
resp := httptest.NewRecorder()
|
|
|
|
obj, err := a.srv.PreparedQuerySpecific(resp, req)
|
|
|
|
|
bulk rewrite using this script
set -euo pipefail
unset CDPATH
cd "$(dirname "$0")"
for f in $(git grep '\brequire := require\.New(' | cut -d':' -f1 | sort -u); do
echo "=== require: $f ==="
sed -i '/require := require.New(t)/d' $f
# require.XXX(blah) but not require.XXX(tblah) or require.XXX(rblah)
sed -i 's/\brequire\.\([a-zA-Z0-9_]*\)(\([^tr]\)/require.\1(t,\2/g' $f
# require.XXX(tblah) but not require.XXX(t, blah)
sed -i 's/\brequire\.\([a-zA-Z0-9_]*\)(\(t[^,]\)/require.\1(t,\2/g' $f
# require.XXX(rblah) but not require.XXX(r, blah)
sed -i 's/\brequire\.\([a-zA-Z0-9_]*\)(\(r[^,]\)/require.\1(t,\2/g' $f
gofmt -s -w $f
done
for f in $(git grep '\bassert := assert\.New(' | cut -d':' -f1 | sort -u); do
echo "=== assert: $f ==="
sed -i '/assert := assert.New(t)/d' $f
# assert.XXX(blah) but not assert.XXX(tblah) or assert.XXX(rblah)
sed -i 's/\bassert\.\([a-zA-Z0-9_]*\)(\([^tr]\)/assert.\1(t,\2/g' $f
# assert.XXX(tblah) but not assert.XXX(t, blah)
sed -i 's/\bassert\.\([a-zA-Z0-9_]*\)(\(t[^,]\)/assert.\1(t,\2/g' $f
# assert.XXX(rblah) but not assert.XXX(r, blah)
sed -i 's/\bassert\.\([a-zA-Z0-9_]*\)(\(r[^,]\)/assert.\1(t,\2/g' $f
gofmt -s -w $f
done
2022-01-20 16:46:23 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, 200, resp.Code)
|
2018-09-06 10:34:28 +00:00
|
|
|
|
|
|
|
r, ok := obj.(structs.PreparedQueryExecuteResponse)
|
bulk rewrite using this script
set -euo pipefail
unset CDPATH
cd "$(dirname "$0")"
for f in $(git grep '\brequire := require\.New(' | cut -d':' -f1 | sort -u); do
echo "=== require: $f ==="
sed -i '/require := require.New(t)/d' $f
# require.XXX(blah) but not require.XXX(tblah) or require.XXX(rblah)
sed -i 's/\brequire\.\([a-zA-Z0-9_]*\)(\([^tr]\)/require.\1(t,\2/g' $f
# require.XXX(tblah) but not require.XXX(t, blah)
sed -i 's/\brequire\.\([a-zA-Z0-9_]*\)(\(t[^,]\)/require.\1(t,\2/g' $f
# require.XXX(rblah) but not require.XXX(r, blah)
sed -i 's/\brequire\.\([a-zA-Z0-9_]*\)(\(r[^,]\)/require.\1(t,\2/g' $f
gofmt -s -w $f
done
for f in $(git grep '\bassert := assert\.New(' | cut -d':' -f1 | sort -u); do
echo "=== assert: $f ==="
sed -i '/assert := assert.New(t)/d' $f
# assert.XXX(blah) but not assert.XXX(tblah) or assert.XXX(rblah)
sed -i 's/\bassert\.\([a-zA-Z0-9_]*\)(\([^tr]\)/assert.\1(t,\2/g' $f
# assert.XXX(tblah) but not assert.XXX(t, blah)
sed -i 's/\bassert\.\([a-zA-Z0-9_]*\)(\(t[^,]\)/assert.\1(t,\2/g' $f
# assert.XXX(rblah) but not assert.XXX(r, blah)
sed -i 's/\bassert\.\([a-zA-Z0-9_]*\)(\(r[^,]\)/assert.\1(t,\2/g' $f
gofmt -s -w $f
done
2022-01-20 16:46:23 +00:00
|
|
|
require.True(t, ok)
|
|
|
|
require.Equal(t, expectFailovers, r.Failovers)
|
2018-09-06 10:34:28 +00:00
|
|
|
|
bulk rewrite using this script
set -euo pipefail
unset CDPATH
cd "$(dirname "$0")"
for f in $(git grep '\brequire := require\.New(' | cut -d':' -f1 | sort -u); do
echo "=== require: $f ==="
sed -i '/require := require.New(t)/d' $f
# require.XXX(blah) but not require.XXX(tblah) or require.XXX(rblah)
sed -i 's/\brequire\.\([a-zA-Z0-9_]*\)(\([^tr]\)/require.\1(t,\2/g' $f
# require.XXX(tblah) but not require.XXX(t, blah)
sed -i 's/\brequire\.\([a-zA-Z0-9_]*\)(\(t[^,]\)/require.\1(t,\2/g' $f
# require.XXX(rblah) but not require.XXX(r, blah)
sed -i 's/\brequire\.\([a-zA-Z0-9_]*\)(\(r[^,]\)/require.\1(t,\2/g' $f
gofmt -s -w $f
done
for f in $(git grep '\bassert := assert\.New(' | cut -d':' -f1 | sort -u); do
echo "=== assert: $f ==="
sed -i '/assert := assert.New(t)/d' $f
# assert.XXX(blah) but not assert.XXX(tblah) or assert.XXX(rblah)
sed -i 's/\bassert\.\([a-zA-Z0-9_]*\)(\([^tr]\)/assert.\1(t,\2/g' $f
# assert.XXX(tblah) but not assert.XXX(t, blah)
sed -i 's/\bassert\.\([a-zA-Z0-9_]*\)(\(t[^,]\)/assert.\1(t,\2/g' $f
# assert.XXX(rblah) but not assert.XXX(r, blah)
sed -i 's/\bassert\.\([a-zA-Z0-9_]*\)(\(r[^,]\)/assert.\1(t,\2/g' $f
gofmt -s -w $f
done
2022-01-20 16:46:23 +00:00
|
|
|
require.Equal(t, expectCache, resp.Header().Get("X-Cache"))
|
2018-09-06 10:34:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Should be a miss at first
|
|
|
|
doRequest(99, "MISS", false)
|
|
|
|
|
|
|
|
// Change the actual response
|
|
|
|
atomic.StoreInt32(&failovers, 66)
|
|
|
|
|
|
|
|
// Request again, should be a cache hit and have the cached (not current)
|
|
|
|
// value.
|
|
|
|
doRequest(99, "HIT", false)
|
|
|
|
|
|
|
|
// Request with max age that should invalidate cache. note that this will be
|
|
|
|
// sent as max-age=0 as that uses seconds but that should cause immediate
|
|
|
|
// invalidation rather than being ignored as an unset value.
|
|
|
|
doRequest(66, "MISS", true)
|
|
|
|
}
|
|
|
|
|
2016-03-04 01:30:36 +00:00
|
|
|
func TestPreparedQuery_Explain(t *testing.T) {
|
2020-12-07 18:42:55 +00:00
|
|
|
if testing.Short() {
|
|
|
|
t.Skip("too slow for testing.Short")
|
|
|
|
}
|
|
|
|
|
2017-05-21 07:54:40 +00:00
|
|
|
t.Parallel()
|
2017-05-21 18:31:20 +00:00
|
|
|
t.Run("", func(t *testing.T) {
|
2020-03-31 19:59:56 +00:00
|
|
|
a := NewTestAgent(t, "")
|
2017-05-21 18:31:20 +00:00
|
|
|
defer a.Shutdown()
|
|
|
|
|
2017-07-05 10:38:11 +00:00
|
|
|
m := MockPreparedQuery{
|
|
|
|
explainFn: func(args *structs.PreparedQueryExecuteRequest, reply *structs.PreparedQueryExplainResponse) error {
|
|
|
|
expected := &structs.PreparedQueryExecuteRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryIDOrName: "my-id",
|
|
|
|
Limit: 5,
|
|
|
|
Source: structs.QuerySource{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Node: "my-node",
|
|
|
|
},
|
|
|
|
Agent: structs.QuerySource{
|
|
|
|
Datacenter: a.Config.Datacenter,
|
|
|
|
Node: a.Config.NodeName,
|
|
|
|
},
|
|
|
|
QueryOptions: structs.QueryOptions{
|
|
|
|
Token: "my-token",
|
|
|
|
RequireConsistent: true,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
if !reflect.DeepEqual(args, expected) {
|
|
|
|
t.Fatalf("bad: %v", args)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Just set something so we can tell this is returned.
|
|
|
|
reply.Query.Name = "hello"
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
}
|
2017-06-19 14:36:09 +00:00
|
|
|
if err := a.registerEndpoint("PreparedQuery", &m); err != nil {
|
2016-03-03 09:04:12 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
body := bytes.NewBuffer(nil)
|
2023-01-24 16:21:41 +00:00
|
|
|
req, _ := http.NewRequest("GET", "/v1/query/my-id/explain?consistent=true&near=my-node&limit=5", body)
|
|
|
|
req.Header.Add("X-Consul-Token", "my-token")
|
2016-03-03 09:04:12 +00:00
|
|
|
resp := httptest.NewRecorder()
|
2017-05-21 18:31:20 +00:00
|
|
|
obj, err := a.srv.PreparedQuerySpecific(resp, req)
|
2016-03-03 09:04:12 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
if resp.Code != 200 {
|
|
|
|
t.Fatalf("bad code: %d", resp.Code)
|
|
|
|
}
|
2016-03-04 01:30:36 +00:00
|
|
|
r, ok := obj.(structs.PreparedQueryExplainResponse)
|
2016-03-03 09:04:12 +00:00
|
|
|
if !ok {
|
|
|
|
t.Fatalf("unexpected: %T", obj)
|
|
|
|
}
|
|
|
|
if r.Query.Name != "hello" {
|
|
|
|
t.Fatalf("bad: %v", r)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2017-05-21 18:31:20 +00:00
|
|
|
t.Run("", func(t *testing.T) {
|
2020-03-31 19:59:56 +00:00
|
|
|
a := NewTestAgent(t, "")
|
2017-05-21 18:31:20 +00:00
|
|
|
defer a.Shutdown()
|
|
|
|
|
2016-03-03 09:04:12 +00:00
|
|
|
body := bytes.NewBuffer(nil)
|
2017-05-09 11:38:05 +00:00
|
|
|
req, _ := http.NewRequest("GET", "/v1/query/not-there/explain", body)
|
2016-03-03 09:04:12 +00:00
|
|
|
resp := httptest.NewRecorder()
|
2022-01-31 16:17:35 +00:00
|
|
|
_, err := a.srv.PreparedQuerySpecific(resp, req)
|
2023-02-17 20:04:12 +00:00
|
|
|
if httpErr, ok := err.(HTTPError); ok {
|
|
|
|
if httpErr.StatusCode != 404 {
|
|
|
|
t.Fatalf("expected status 404 but got %d", httpErr.StatusCode)
|
2022-04-29 17:42:49 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
t.Fatalf("expected HTTP error but got %v", err)
|
2016-03-03 09:04:12 +00:00
|
|
|
}
|
|
|
|
})
|
2018-06-05 23:04:50 +00:00
|
|
|
|
|
|
|
// Ensure that Connect is passed through
|
|
|
|
t.Run("", func(t *testing.T) {
|
2020-03-31 19:59:56 +00:00
|
|
|
a := NewTestAgent(t, "")
|
2018-06-05 23:04:50 +00:00
|
|
|
defer a.Shutdown()
|
|
|
|
|
|
|
|
m := MockPreparedQuery{
|
|
|
|
executeFn: func(args *structs.PreparedQueryExecuteRequest, reply *structs.PreparedQueryExecuteResponse) error {
|
bulk rewrite using this script
set -euo pipefail
unset CDPATH
cd "$(dirname "$0")"
for f in $(git grep '\brequire := require\.New(' | cut -d':' -f1 | sort -u); do
echo "=== require: $f ==="
sed -i '/require := require.New(t)/d' $f
# require.XXX(blah) but not require.XXX(tblah) or require.XXX(rblah)
sed -i 's/\brequire\.\([a-zA-Z0-9_]*\)(\([^tr]\)/require.\1(t,\2/g' $f
# require.XXX(tblah) but not require.XXX(t, blah)
sed -i 's/\brequire\.\([a-zA-Z0-9_]*\)(\(t[^,]\)/require.\1(t,\2/g' $f
# require.XXX(rblah) but not require.XXX(r, blah)
sed -i 's/\brequire\.\([a-zA-Z0-9_]*\)(\(r[^,]\)/require.\1(t,\2/g' $f
gofmt -s -w $f
done
for f in $(git grep '\bassert := assert\.New(' | cut -d':' -f1 | sort -u); do
echo "=== assert: $f ==="
sed -i '/assert := assert.New(t)/d' $f
# assert.XXX(blah) but not assert.XXX(tblah) or assert.XXX(rblah)
sed -i 's/\bassert\.\([a-zA-Z0-9_]*\)(\([^tr]\)/assert.\1(t,\2/g' $f
# assert.XXX(tblah) but not assert.XXX(t, blah)
sed -i 's/\bassert\.\([a-zA-Z0-9_]*\)(\(t[^,]\)/assert.\1(t,\2/g' $f
# assert.XXX(rblah) but not assert.XXX(r, blah)
sed -i 's/\bassert\.\([a-zA-Z0-9_]*\)(\(r[^,]\)/assert.\1(t,\2/g' $f
gofmt -s -w $f
done
2022-01-20 16:46:23 +00:00
|
|
|
require.True(t, args.Connect)
|
2018-06-05 23:04:50 +00:00
|
|
|
return nil
|
|
|
|
},
|
|
|
|
}
|
bulk rewrite using this script
set -euo pipefail
unset CDPATH
cd "$(dirname "$0")"
for f in $(git grep '\brequire := require\.New(' | cut -d':' -f1 | sort -u); do
echo "=== require: $f ==="
sed -i '/require := require.New(t)/d' $f
# require.XXX(blah) but not require.XXX(tblah) or require.XXX(rblah)
sed -i 's/\brequire\.\([a-zA-Z0-9_]*\)(\([^tr]\)/require.\1(t,\2/g' $f
# require.XXX(tblah) but not require.XXX(t, blah)
sed -i 's/\brequire\.\([a-zA-Z0-9_]*\)(\(t[^,]\)/require.\1(t,\2/g' $f
# require.XXX(rblah) but not require.XXX(r, blah)
sed -i 's/\brequire\.\([a-zA-Z0-9_]*\)(\(r[^,]\)/require.\1(t,\2/g' $f
gofmt -s -w $f
done
for f in $(git grep '\bassert := assert\.New(' | cut -d':' -f1 | sort -u); do
echo "=== assert: $f ==="
sed -i '/assert := assert.New(t)/d' $f
# assert.XXX(blah) but not assert.XXX(tblah) or assert.XXX(rblah)
sed -i 's/\bassert\.\([a-zA-Z0-9_]*\)(\([^tr]\)/assert.\1(t,\2/g' $f
# assert.XXX(tblah) but not assert.XXX(t, blah)
sed -i 's/\bassert\.\([a-zA-Z0-9_]*\)(\(t[^,]\)/assert.\1(t,\2/g' $f
# assert.XXX(rblah) but not assert.XXX(r, blah)
sed -i 's/\bassert\.\([a-zA-Z0-9_]*\)(\(r[^,]\)/assert.\1(t,\2/g' $f
gofmt -s -w $f
done
2022-01-20 16:46:23 +00:00
|
|
|
require.NoError(t, a.registerEndpoint("PreparedQuery", &m))
|
2018-06-05 23:04:50 +00:00
|
|
|
|
|
|
|
body := bytes.NewBuffer(nil)
|
|
|
|
req, _ := http.NewRequest("GET", "/v1/query/my-id/execute?connect=true", body)
|
|
|
|
resp := httptest.NewRecorder()
|
|
|
|
_, err := a.srv.PreparedQuerySpecific(resp, req)
|
bulk rewrite using this script
set -euo pipefail
unset CDPATH
cd "$(dirname "$0")"
for f in $(git grep '\brequire := require\.New(' | cut -d':' -f1 | sort -u); do
echo "=== require: $f ==="
sed -i '/require := require.New(t)/d' $f
# require.XXX(blah) but not require.XXX(tblah) or require.XXX(rblah)
sed -i 's/\brequire\.\([a-zA-Z0-9_]*\)(\([^tr]\)/require.\1(t,\2/g' $f
# require.XXX(tblah) but not require.XXX(t, blah)
sed -i 's/\brequire\.\([a-zA-Z0-9_]*\)(\(t[^,]\)/require.\1(t,\2/g' $f
# require.XXX(rblah) but not require.XXX(r, blah)
sed -i 's/\brequire\.\([a-zA-Z0-9_]*\)(\(r[^,]\)/require.\1(t,\2/g' $f
gofmt -s -w $f
done
for f in $(git grep '\bassert := assert\.New(' | cut -d':' -f1 | sort -u); do
echo "=== assert: $f ==="
sed -i '/assert := assert.New(t)/d' $f
# assert.XXX(blah) but not assert.XXX(tblah) or assert.XXX(rblah)
sed -i 's/\bassert\.\([a-zA-Z0-9_]*\)(\([^tr]\)/assert.\1(t,\2/g' $f
# assert.XXX(tblah) but not assert.XXX(t, blah)
sed -i 's/\bassert\.\([a-zA-Z0-9_]*\)(\(t[^,]\)/assert.\1(t,\2/g' $f
# assert.XXX(rblah) but not assert.XXX(r, blah)
sed -i 's/\bassert\.\([a-zA-Z0-9_]*\)(\(r[^,]\)/assert.\1(t,\2/g' $f
gofmt -s -w $f
done
2022-01-20 16:46:23 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, 200, resp.Code)
|
2018-06-05 23:04:50 +00:00
|
|
|
})
|
2016-03-03 09:04:12 +00:00
|
|
|
}
|
|
|
|
|
2015-11-12 01:27:51 +00:00
|
|
|
func TestPreparedQuery_Get(t *testing.T) {
|
2020-12-07 18:42:55 +00:00
|
|
|
if testing.Short() {
|
|
|
|
t.Skip("too slow for testing.Short")
|
|
|
|
}
|
|
|
|
|
2017-05-21 07:54:40 +00:00
|
|
|
t.Parallel()
|
2017-05-21 18:31:20 +00:00
|
|
|
t.Run("", func(t *testing.T) {
|
2020-03-31 19:59:56 +00:00
|
|
|
a := NewTestAgent(t, "")
|
2017-05-21 18:31:20 +00:00
|
|
|
defer a.Shutdown()
|
|
|
|
|
2017-07-05 10:38:11 +00:00
|
|
|
m := MockPreparedQuery{
|
|
|
|
getFn: func(args *structs.PreparedQuerySpecificRequest, reply *structs.IndexedPreparedQueries) error {
|
|
|
|
expected := &structs.PreparedQuerySpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryID: "my-id",
|
|
|
|
QueryOptions: structs.QueryOptions{
|
|
|
|
Token: "my-token",
|
|
|
|
RequireConsistent: true,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
if !reflect.DeepEqual(args, expected) {
|
|
|
|
t.Fatalf("bad: %v", args)
|
|
|
|
}
|
|
|
|
|
|
|
|
query := &structs.PreparedQuery{
|
|
|
|
ID: "my-id",
|
|
|
|
}
|
|
|
|
reply.Queries = append(reply.Queries, query)
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
}
|
2017-06-19 14:36:09 +00:00
|
|
|
if err := a.registerEndpoint("PreparedQuery", &m); err != nil {
|
2015-11-12 01:27:51 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
body := bytes.NewBuffer(nil)
|
2023-01-24 16:21:41 +00:00
|
|
|
req, _ := http.NewRequest("GET", "/v1/query/my-id?consistent=true", body)
|
|
|
|
req.Header.Add("X-Consul-Token", "my-token")
|
2015-11-12 01:27:51 +00:00
|
|
|
resp := httptest.NewRecorder()
|
2017-05-21 18:31:20 +00:00
|
|
|
obj, err := a.srv.PreparedQuerySpecific(resp, req)
|
2015-11-12 01:27:51 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
if resp.Code != 200 {
|
|
|
|
t.Fatalf("bad code: %d", resp.Code)
|
|
|
|
}
|
|
|
|
r, ok := obj.(structs.PreparedQueries)
|
|
|
|
if !ok {
|
|
|
|
t.Fatalf("unexpected: %T", obj)
|
|
|
|
}
|
|
|
|
if len(r) != 1 || r[0].ID != "my-id" {
|
|
|
|
t.Fatalf("bad: %v", r)
|
|
|
|
}
|
|
|
|
})
|
2015-11-13 20:57:06 +00:00
|
|
|
|
2017-05-21 18:31:20 +00:00
|
|
|
t.Run("", func(t *testing.T) {
|
2020-03-31 19:59:56 +00:00
|
|
|
a := NewTestAgent(t, "")
|
2017-05-21 18:31:20 +00:00
|
|
|
defer a.Shutdown()
|
|
|
|
|
2015-11-13 20:57:06 +00:00
|
|
|
body := bytes.NewBuffer(nil)
|
2017-05-09 11:38:05 +00:00
|
|
|
req, _ := http.NewRequest("GET", "/v1/query/f004177f-2c28-83b7-4229-eacc25fe55d1", body)
|
2015-11-13 20:57:06 +00:00
|
|
|
resp := httptest.NewRecorder()
|
2022-01-31 16:17:35 +00:00
|
|
|
_, err := a.srv.PreparedQuerySpecific(resp, req)
|
2023-02-17 20:04:12 +00:00
|
|
|
if httpErr, ok := err.(HTTPError); ok {
|
|
|
|
if httpErr.StatusCode != 404 {
|
|
|
|
t.Fatalf("expected status 404 but got %d", httpErr.StatusCode)
|
2022-04-29 17:42:49 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
t.Fatalf("expected HTTP error but got %v", err)
|
2015-11-13 20:57:06 +00:00
|
|
|
}
|
|
|
|
})
|
2015-11-12 01:27:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestPreparedQuery_Update(t *testing.T) {
|
2020-12-07 18:42:55 +00:00
|
|
|
if testing.Short() {
|
|
|
|
t.Skip("too slow for testing.Short")
|
|
|
|
}
|
|
|
|
|
2017-05-21 07:54:40 +00:00
|
|
|
t.Parallel()
|
2020-03-31 19:59:56 +00:00
|
|
|
a := NewTestAgent(t, "")
|
2017-05-21 18:31:20 +00:00
|
|
|
defer a.Shutdown()
|
2015-11-12 01:27:51 +00:00
|
|
|
|
2017-07-05 10:38:11 +00:00
|
|
|
m := MockPreparedQuery{
|
|
|
|
applyFn: func(args *structs.PreparedQueryRequest, reply *string) error {
|
|
|
|
expected := &structs.PreparedQueryRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Op: structs.PreparedQueryUpdate,
|
|
|
|
Query: &structs.PreparedQuery{
|
|
|
|
ID: "my-id",
|
|
|
|
Name: "my-query",
|
|
|
|
Session: "my-session",
|
|
|
|
Service: structs.ServiceQuery{
|
|
|
|
Service: "my-service",
|
2022-07-22 13:14:43 +00:00
|
|
|
Failover: structs.QueryFailoverOptions{
|
2017-07-05 10:38:11 +00:00
|
|
|
NearestN: 4,
|
|
|
|
Datacenters: []string{"dc1", "dc2"},
|
|
|
|
},
|
|
|
|
OnlyPassing: true,
|
|
|
|
Tags: []string{"foo", "bar"},
|
|
|
|
NodeMeta: map[string]string{"somekey": "somevalue"},
|
|
|
|
},
|
|
|
|
DNS: structs.QueryDNSOptions{
|
|
|
|
TTL: "10s",
|
2015-11-12 01:27:51 +00:00
|
|
|
},
|
|
|
|
},
|
2017-07-05 10:38:11 +00:00
|
|
|
WriteRequest: structs.WriteRequest{
|
|
|
|
Token: "my-token",
|
2015-11-12 01:27:51 +00:00
|
|
|
},
|
2017-07-05 10:38:11 +00:00
|
|
|
}
|
|
|
|
if !reflect.DeepEqual(args, expected) {
|
|
|
|
t.Fatalf("bad: %v", args)
|
|
|
|
}
|
2015-11-12 01:27:51 +00:00
|
|
|
|
2017-07-05 10:38:11 +00:00
|
|
|
*reply = "don't care"
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
if err := a.registerEndpoint("PreparedQuery", &m); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
2017-05-21 18:31:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
body := bytes.NewBuffer(nil)
|
|
|
|
enc := json.NewEncoder(body)
|
|
|
|
raw := map[string]interface{}{
|
|
|
|
"ID": "this should get ignored",
|
|
|
|
"Name": "my-query",
|
|
|
|
"Session": "my-session",
|
|
|
|
"Service": map[string]interface{}{
|
|
|
|
"Service": "my-service",
|
|
|
|
"Failover": map[string]interface{}{
|
|
|
|
"NearestN": 4,
|
|
|
|
"Datacenters": []string{"dc1", "dc2"},
|
|
|
|
},
|
|
|
|
"OnlyPassing": true,
|
|
|
|
"Tags": []string{"foo", "bar"},
|
|
|
|
"NodeMeta": map[string]string{"somekey": "somevalue"},
|
|
|
|
},
|
|
|
|
"DNS": map[string]interface{}{
|
|
|
|
"TTL": "10s",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
if err := enc.Encode(raw); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
2023-01-24 16:21:41 +00:00
|
|
|
req, _ := http.NewRequest("PUT", "/v1/query/my-id", body)
|
|
|
|
req.Header.Add("X-Consul-Token", "my-token")
|
2017-05-21 18:31:20 +00:00
|
|
|
resp := httptest.NewRecorder()
|
|
|
|
if _, err := a.srv.PreparedQuerySpecific(resp, req); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
if resp.Code != 200 {
|
|
|
|
t.Fatalf("bad code: %d", resp.Code)
|
|
|
|
}
|
2015-11-12 01:27:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestPreparedQuery_Delete(t *testing.T) {
|
2020-12-07 18:42:55 +00:00
|
|
|
if testing.Short() {
|
|
|
|
t.Skip("too slow for testing.Short")
|
|
|
|
}
|
|
|
|
|
2017-05-21 07:54:40 +00:00
|
|
|
t.Parallel()
|
2020-03-31 19:59:56 +00:00
|
|
|
a := NewTestAgent(t, "")
|
2017-05-21 18:31:20 +00:00
|
|
|
defer a.Shutdown()
|
2015-11-12 01:27:51 +00:00
|
|
|
|
2017-07-05 10:38:11 +00:00
|
|
|
m := MockPreparedQuery{
|
|
|
|
applyFn: func(args *structs.PreparedQueryRequest, reply *string) error {
|
|
|
|
expected := &structs.PreparedQueryRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Op: structs.PreparedQueryDelete,
|
|
|
|
Query: &structs.PreparedQuery{
|
|
|
|
ID: "my-id",
|
|
|
|
},
|
|
|
|
WriteRequest: structs.WriteRequest{
|
|
|
|
Token: "my-token",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
if !reflect.DeepEqual(args, expected) {
|
|
|
|
t.Fatalf("bad: %v", args)
|
|
|
|
}
|
|
|
|
|
|
|
|
*reply = "don't care"
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
}
|
2017-06-19 14:36:09 +00:00
|
|
|
if err := a.registerEndpoint("PreparedQuery", &m); err != nil {
|
2017-05-21 18:31:20 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2015-11-12 01:27:51 +00:00
|
|
|
|
2017-05-21 18:31:20 +00:00
|
|
|
body := bytes.NewBuffer(nil)
|
|
|
|
enc := json.NewEncoder(body)
|
|
|
|
raw := map[string]interface{}{
|
|
|
|
"ID": "this should get ignored",
|
|
|
|
}
|
|
|
|
if err := enc.Encode(raw); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
2023-01-24 16:21:41 +00:00
|
|
|
req, _ := http.NewRequest("DELETE", "/v1/query/my-id", body)
|
|
|
|
req.Header.Add("X-Consul-Token", "my-token")
|
2017-05-21 18:31:20 +00:00
|
|
|
resp := httptest.NewRecorder()
|
|
|
|
if _, err := a.srv.PreparedQuerySpecific(resp, req); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
if resp.Code != 200 {
|
|
|
|
t.Fatalf("bad code: %d", resp.Code)
|
|
|
|
}
|
2015-11-12 01:27:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestPreparedQuery_parseLimit(t *testing.T) {
|
2017-05-21 07:54:40 +00:00
|
|
|
t.Parallel()
|
2015-11-12 01:27:51 +00:00
|
|
|
body := bytes.NewBuffer(nil)
|
2017-05-09 11:38:05 +00:00
|
|
|
req, _ := http.NewRequest("GET", "/v1/query", body)
|
2015-11-12 01:27:51 +00:00
|
|
|
limit := 99
|
|
|
|
if err := parseLimit(req, &limit); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
if limit != 0 {
|
|
|
|
t.Fatalf("bad limit: %d", limit)
|
|
|
|
}
|
|
|
|
|
2017-05-09 11:38:05 +00:00
|
|
|
req, _ = http.NewRequest("GET", "/v1/query?limit=11", body)
|
2015-11-12 01:27:51 +00:00
|
|
|
if err := parseLimit(req, &limit); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
if limit != 11 {
|
|
|
|
t.Fatalf("bad limit: %d", limit)
|
|
|
|
}
|
|
|
|
|
2017-05-09 11:38:05 +00:00
|
|
|
req, _ = http.NewRequest("GET", "/v1/query?limit=bob", body)
|
2015-11-12 01:27:51 +00:00
|
|
|
if err := parseLimit(req, &limit); err == nil {
|
|
|
|
t.Fatalf("bad: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Since we've done exhaustive testing of the calls into the endpoints above
|
|
|
|
// this is just a basic end-to-end sanity check to make sure things are wired
|
|
|
|
// correctly when calling through to the real endpoints.
|
|
|
|
func TestPreparedQuery_Integration(t *testing.T) {
|
2020-12-07 18:42:55 +00:00
|
|
|
if testing.Short() {
|
|
|
|
t.Skip("too slow for testing.Short")
|
|
|
|
}
|
|
|
|
|
2017-05-21 07:54:40 +00:00
|
|
|
t.Parallel()
|
2020-03-31 19:59:56 +00:00
|
|
|
a := NewTestAgent(t, "")
|
2017-05-21 18:31:20 +00:00
|
|
|
defer a.Shutdown()
|
2018-09-10 15:58:53 +00:00
|
|
|
testrpc.WaitForTestAgent(t, a.RPC, "dc1")
|
2017-05-21 18:31:20 +00:00
|
|
|
|
|
|
|
// Register a node and a service.
|
|
|
|
{
|
|
|
|
args := &structs.RegisterRequest{
|
|
|
|
Datacenter: "dc1",
|
2017-05-21 18:32:48 +00:00
|
|
|
Node: a.Config.NodeName,
|
2017-05-21 18:31:20 +00:00
|
|
|
Address: "127.0.0.1",
|
|
|
|
Service: &structs.NodeService{
|
|
|
|
Service: "my-service",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
var out struct{}
|
2022-12-14 15:24:22 +00:00
|
|
|
if err := a.RPC(context.Background(), "Catalog.Register", args, &out); err != nil {
|
2017-05-21 18:31:20 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
2015-11-12 01:27:51 +00:00
|
|
|
}
|
2017-05-21 18:31:20 +00:00
|
|
|
}
|
2015-11-12 01:27:51 +00:00
|
|
|
|
2017-05-21 18:31:20 +00:00
|
|
|
// Create a query.
|
|
|
|
var id string
|
|
|
|
{
|
|
|
|
body := bytes.NewBuffer(nil)
|
|
|
|
enc := json.NewEncoder(body)
|
|
|
|
raw := map[string]interface{}{
|
|
|
|
"Name": "my-query",
|
|
|
|
"Service": map[string]interface{}{
|
|
|
|
"Service": "my-service",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
if err := enc.Encode(raw); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2015-11-12 01:27:51 +00:00
|
|
|
|
2017-05-21 18:31:20 +00:00
|
|
|
req, _ := http.NewRequest("POST", "/v1/query", body)
|
|
|
|
resp := httptest.NewRecorder()
|
|
|
|
obj, err := a.srv.PreparedQueryGeneral(resp, req)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
2015-11-12 01:27:51 +00:00
|
|
|
}
|
2017-05-21 18:31:20 +00:00
|
|
|
if resp.Code != 200 {
|
|
|
|
t.Fatalf("bad code: %d", resp.Code)
|
|
|
|
}
|
|
|
|
r, ok := obj.(preparedQueryCreateResponse)
|
|
|
|
if !ok {
|
|
|
|
t.Fatalf("unexpected: %T", obj)
|
|
|
|
}
|
|
|
|
id = r.ID
|
|
|
|
}
|
2015-11-12 01:27:51 +00:00
|
|
|
|
2017-05-21 18:31:20 +00:00
|
|
|
// List them all.
|
|
|
|
{
|
|
|
|
body := bytes.NewBuffer(nil)
|
2023-01-24 16:21:41 +00:00
|
|
|
req, _ := http.NewRequest("GET", "/v1/query", body)
|
|
|
|
req.Header.Add("X-Consul-Token", "root")
|
2017-05-21 18:31:20 +00:00
|
|
|
resp := httptest.NewRecorder()
|
|
|
|
obj, err := a.srv.PreparedQueryGeneral(resp, req)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
2015-11-12 01:27:51 +00:00
|
|
|
}
|
2017-05-21 18:31:20 +00:00
|
|
|
if resp.Code != 200 {
|
|
|
|
t.Fatalf("bad code: %d", resp.Code)
|
|
|
|
}
|
|
|
|
r, ok := obj.(structs.PreparedQueries)
|
|
|
|
if !ok {
|
|
|
|
t.Fatalf("unexpected: %T", obj)
|
|
|
|
}
|
|
|
|
if len(r) != 1 {
|
|
|
|
t.Fatalf("bad: %v", r)
|
|
|
|
}
|
|
|
|
}
|
2015-11-12 01:27:51 +00:00
|
|
|
|
2017-05-21 18:31:20 +00:00
|
|
|
// Execute it.
|
|
|
|
{
|
|
|
|
body := bytes.NewBuffer(nil)
|
|
|
|
req, _ := http.NewRequest("GET", "/v1/query/"+id+"/execute", body)
|
|
|
|
resp := httptest.NewRecorder()
|
|
|
|
obj, err := a.srv.PreparedQuerySpecific(resp, req)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
if resp.Code != 200 {
|
|
|
|
t.Fatalf("bad code: %d", resp.Code)
|
|
|
|
}
|
|
|
|
r, ok := obj.(structs.PreparedQueryExecuteResponse)
|
|
|
|
if !ok {
|
|
|
|
t.Fatalf("unexpected: %T", obj)
|
|
|
|
}
|
|
|
|
if len(r.Nodes) != 1 {
|
|
|
|
t.Fatalf("bad: %v", r)
|
2015-11-12 01:27:51 +00:00
|
|
|
}
|
2017-05-21 18:31:20 +00:00
|
|
|
}
|
2015-11-12 01:27:51 +00:00
|
|
|
|
2017-05-21 18:31:20 +00:00
|
|
|
// Read it back.
|
|
|
|
{
|
|
|
|
body := bytes.NewBuffer(nil)
|
|
|
|
req, _ := http.NewRequest("GET", "/v1/query/"+id, body)
|
|
|
|
resp := httptest.NewRecorder()
|
|
|
|
obj, err := a.srv.PreparedQuerySpecific(resp, req)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
2015-11-12 01:27:51 +00:00
|
|
|
}
|
2017-05-21 18:31:20 +00:00
|
|
|
if resp.Code != 200 {
|
|
|
|
t.Fatalf("bad code: %d", resp.Code)
|
|
|
|
}
|
|
|
|
r, ok := obj.(structs.PreparedQueries)
|
|
|
|
if !ok {
|
|
|
|
t.Fatalf("unexpected: %T", obj)
|
|
|
|
}
|
|
|
|
if len(r) != 1 {
|
|
|
|
t.Fatalf("bad: %v", r)
|
|
|
|
}
|
|
|
|
}
|
2015-11-12 01:27:51 +00:00
|
|
|
|
2017-05-21 18:31:20 +00:00
|
|
|
// Make an update to it.
|
|
|
|
{
|
|
|
|
body := bytes.NewBuffer(nil)
|
|
|
|
enc := json.NewEncoder(body)
|
|
|
|
raw := map[string]interface{}{
|
|
|
|
"Name": "my-query",
|
|
|
|
"Service": map[string]interface{}{
|
|
|
|
"Service": "my-service",
|
|
|
|
"OnlyPassing": true,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
if err := enc.Encode(raw); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2015-11-12 01:27:51 +00:00
|
|
|
|
2017-05-21 18:31:20 +00:00
|
|
|
req, _ := http.NewRequest("PUT", "/v1/query/"+id, body)
|
|
|
|
resp := httptest.NewRecorder()
|
|
|
|
if _, err := a.srv.PreparedQuerySpecific(resp, req); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
if resp.Code != 200 {
|
|
|
|
t.Fatalf("bad code: %d", resp.Code)
|
2015-11-12 01:27:51 +00:00
|
|
|
}
|
2017-05-21 18:31:20 +00:00
|
|
|
}
|
2015-11-12 01:27:51 +00:00
|
|
|
|
2017-05-21 18:31:20 +00:00
|
|
|
// Delete it.
|
|
|
|
{
|
|
|
|
body := bytes.NewBuffer(nil)
|
|
|
|
req, _ := http.NewRequest("DELETE", "/v1/query/"+id, body)
|
|
|
|
resp := httptest.NewRecorder()
|
|
|
|
if _, err := a.srv.PreparedQuerySpecific(resp, req); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
2015-11-12 01:27:51 +00:00
|
|
|
}
|
2017-05-21 18:31:20 +00:00
|
|
|
if resp.Code != 200 {
|
|
|
|
t.Fatalf("bad code: %d", resp.Code)
|
|
|
|
}
|
|
|
|
}
|
2015-11-12 01:27:51 +00:00
|
|
|
}
|