1package api
2
3import (
4	"reflect"
5	"testing"
6
7	"github.com/hashicorp/consul/sdk/testutil/retry"
8)
9
10func TestAPI_PreparedQuery(t *testing.T) {
11	t.Parallel()
12	c, s := makeClient(t)
13	defer s.Stop()
14
15	// Set up a node and a service.
16	reg := &CatalogRegistration{
17		Datacenter: "dc1",
18		Node:       "foobar",
19		Address:    "192.168.10.10",
20		TaggedAddresses: map[string]string{
21			"wan": "127.0.0.1",
22		},
23		NodeMeta: map[string]string{"somekey": "somevalue"},
24		Service: &AgentService{
25			ID:      "redis1",
26			Service: "redis",
27			Tags:    []string{"master", "v1"},
28			Meta:    map[string]string{"redis-version": "4.0"},
29			Port:    8000,
30		},
31	}
32
33	catalog := c.Catalog()
34	retry.Run(t, func(r *retry.R) {
35		if _, err := catalog.Register(reg, nil); err != nil {
36			r.Fatal(err)
37		}
38		if _, _, err := catalog.Node("foobar", nil); err != nil {
39			r.Fatal(err)
40		}
41	})
42
43	// Create a simple prepared query.
44	def := &PreparedQueryDefinition{
45		Name: "test",
46		Service: ServiceQuery{
47			Service:     "redis",
48			NodeMeta:    map[string]string{"somekey": "somevalue"},
49			ServiceMeta: map[string]string{"redis-version": "4.0"},
50		},
51	}
52
53	query := c.PreparedQuery()
54	var err error
55	def.ID, _, err = query.Create(def, nil)
56	if err != nil {
57		t.Fatalf("err: %s", err)
58	}
59
60	// Read it back.
61	defs, _, err := query.Get(def.ID, nil)
62	if err != nil {
63		t.Fatalf("err: %s", err)
64	}
65	if len(defs) != 1 || !reflect.DeepEqual(defs[0], def) {
66		t.Fatalf("bad: %v", defs)
67	}
68
69	// List them all.
70	defs, _, err = query.List(nil)
71	if err != nil {
72		t.Fatalf("err: %s", err)
73	}
74	if len(defs) != 1 || !reflect.DeepEqual(defs[0], def) {
75		t.Fatalf("bad: %v", defs)
76	}
77
78	// Make an update.
79	def.Name = "my-query"
80	_, err = query.Update(def, nil)
81	if err != nil {
82		t.Fatalf("err: %s", err)
83	}
84
85	// Read it back again to verify the update worked.
86	defs, _, err = query.Get(def.ID, nil)
87	if err != nil {
88		t.Fatalf("err: %s", err)
89	}
90	if len(defs) != 1 || !reflect.DeepEqual(defs[0], def) {
91		t.Fatalf("bad: %v", defs)
92	}
93
94	// Execute by ID.
95	results, _, err := query.Execute(def.ID, nil)
96	if err != nil {
97		t.Fatalf("err: %s", err)
98	}
99	if len(results.Nodes) != 1 || results.Nodes[0].Node.Node != "foobar" {
100		t.Fatalf("bad: %v", results)
101	}
102	if wan, ok := results.Nodes[0].Node.TaggedAddresses["wan"]; !ok || wan != "127.0.0.1" {
103		t.Fatalf("bad: %v", results)
104	}
105
106	// Execute by name.
107	results, _, err = query.Execute("my-query", nil)
108	if err != nil {
109		t.Fatalf("err: %s", err)
110	}
111	if len(results.Nodes) != 1 || results.Nodes[0].Node.Node != "foobar" {
112		t.Fatalf("bad: %v", results)
113	}
114	if wan, ok := results.Nodes[0].Node.TaggedAddresses["wan"]; !ok || wan != "127.0.0.1" {
115		t.Fatalf("bad: %v", results)
116	}
117	if results.Nodes[0].Node.Datacenter != "dc1" {
118		t.Fatalf("bad datacenter: %v", results)
119	}
120
121	// Add new node with failing health check.
122	reg2 := reg
123	reg2.Node = "failingnode"
124	reg2.Check = &AgentCheck{
125		Node:        "failingnode",
126		ServiceID:   "redis1",
127		ServiceName: "redis",
128		Name:        "failingcheck",
129		Status:      "critical",
130	}
131	retry.Run(t, func(r *retry.R) {
132		if _, err := catalog.Register(reg2, nil); err != nil {
133			r.Fatal(err)
134		}
135		if _, _, err := catalog.Node("failingnode", nil); err != nil {
136			r.Fatal(err)
137		}
138	})
139
140	// Execute by ID. Should return only healthy node.
141	results, _, err = query.Execute(def.ID, nil)
142	if err != nil {
143		t.Fatalf("err: %s", err)
144	}
145	if len(results.Nodes) != 1 || results.Nodes[0].Node.Node != "foobar" {
146		t.Fatalf("bad: %v", results)
147	}
148	if wan, ok := results.Nodes[0].Node.TaggedAddresses["wan"]; !ok || wan != "127.0.0.1" {
149		t.Fatalf("bad: %v", results)
150	}
151
152	// Update PQ with ignore rule for the failing check
153	def.Service.IgnoreCheckIDs = []string{"failingcheck"}
154	_, err = query.Update(def, nil)
155	if err != nil {
156		t.Fatalf("err: %s", err)
157	}
158
159	// Execute by ID. Should return BOTH nodes ignoring the failing check.
160	results, _, err = query.Execute(def.ID, nil)
161	if err != nil {
162		t.Fatalf("err: %s", err)
163	}
164	if len(results.Nodes) != 2 {
165		t.Fatalf("got %d nodes, want 2", len(results.Nodes))
166	}
167
168	// Delete it.
169	_, err = query.Delete(def.ID, nil)
170	if err != nil {
171		t.Fatalf("err: %s", err)
172	}
173
174	// Make sure there are no longer any queries.
175	defs, _, err = query.List(nil)
176	if err != nil {
177		t.Fatalf("err: %s", err)
178	}
179	if len(defs) != 0 {
180		t.Fatalf("bad: %v", defs)
181	}
182}
183