1package agent
2
3import (
4	"encoding/json"
5	"io/ioutil"
6	"os"
7	"path/filepath"
8	"reflect"
9	"strings"
10	"testing"
11
12	"github.com/hashicorp/serf/serf"
13	"github.com/hashicorp/serf/testutil"
14)
15
16func TestAgent_eventHandler(t *testing.T) {
17	ip1, returnFn1 := testutil.TakeIP()
18	defer returnFn1()
19
20	a1 := testAgent(t, ip1, nil)
21	defer a1.Shutdown()
22	defer a1.Leave()
23
24	handler := new(MockEventHandler)
25	a1.RegisterEventHandler(handler)
26
27	if err := a1.Start(); err != nil {
28		t.Fatalf("err: %v", err)
29	}
30
31	testutil.Yield()
32
33	if len(handler.Events) != 1 {
34		t.Fatalf("bad: %#v", handler.Events)
35	}
36
37	if handler.Events[0].EventType() != serf.EventMemberJoin {
38		t.Fatalf("bad: %#v", handler.Events[0])
39	}
40}
41
42func TestAgentShutdown_multiple(t *testing.T) {
43	ip1, returnFn1 := testutil.TakeIP()
44	defer returnFn1()
45
46	a := testAgent(t, ip1, nil)
47	if err := a.Start(); err != nil {
48		t.Fatalf("err: %v", err)
49	}
50
51	for i := 0; i < 5; i++ {
52		if err := a.Shutdown(); err != nil {
53			t.Fatalf("err: %v", err)
54		}
55	}
56}
57
58func TestAgentUserEvent(t *testing.T) {
59	ip1, returnFn1 := testutil.TakeIP()
60	defer returnFn1()
61
62	a1 := testAgent(t, ip1, nil)
63	defer a1.Shutdown()
64	defer a1.Leave()
65
66	handler := new(MockEventHandler)
67	a1.RegisterEventHandler(handler)
68
69	if err := a1.Start(); err != nil {
70		t.Fatalf("err: %v", err)
71	}
72
73	testutil.Yield()
74
75	if err := a1.UserEvent("deploy", []byte("foo"), false); err != nil {
76		t.Fatalf("err: %v", err)
77	}
78
79	testutil.Yield()
80
81	handler.Lock()
82	defer handler.Unlock()
83
84	if len(handler.Events) == 0 {
85		t.Fatal("no events")
86	}
87
88	e, ok := handler.Events[len(handler.Events)-1].(serf.UserEvent)
89	if !ok {
90		t.Fatalf("bad: %#v", e)
91	}
92
93	if e.Name != "deploy" {
94		t.Fatalf("bad: %#v", e)
95	}
96
97	if string(e.Payload) != "foo" {
98		t.Fatalf("bad: %#v", e)
99	}
100}
101
102func TestAgentQuery_BadPrefix(t *testing.T) {
103	ip1, returnFn1 := testutil.TakeIP()
104	defer returnFn1()
105
106	a1 := testAgent(t, ip1, nil)
107	defer a1.Shutdown()
108	defer a1.Leave()
109
110	if err := a1.Start(); err != nil {
111		t.Fatalf("err: %v", err)
112	}
113
114	testutil.Yield()
115
116	_, err := a1.Query("_serf_test", nil, nil)
117	if err == nil || !strings.Contains(err.Error(), "cannot contain") {
118		t.Fatalf("err: %v", err)
119	}
120}
121
122func TestAgentTagsFile(t *testing.T) {
123	tags := map[string]string{
124		"role":       "webserver",
125		"datacenter": "us-east",
126	}
127
128	td, err := ioutil.TempDir("", "serf")
129	if err != nil {
130		t.Fatalf("err: %v", err)
131	}
132	defer os.RemoveAll(td)
133
134	ip1, returnFn1 := testutil.TakeIP()
135	defer returnFn1()
136
137	ip2, returnFn2 := testutil.TakeIP()
138	defer returnFn2()
139
140	agentConfig := DefaultConfig()
141	agentConfig.TagsFile = filepath.Join(td, "tags.json")
142
143	a1 := testAgentWithConfig(t, ip1, agentConfig, serf.DefaultConfig(), nil)
144
145	if err := a1.Start(); err != nil {
146		t.Fatalf("err: %v", err)
147	}
148	defer a1.Shutdown()
149	defer a1.Leave()
150
151	testutil.Yield()
152
153	err = a1.SetTags(tags)
154
155	if err != nil {
156		t.Fatalf("err: %v", err)
157	}
158
159	testutil.Yield()
160
161	a2 := testAgentWithConfig(t, ip2, agentConfig, serf.DefaultConfig(), nil)
162
163	if err := a2.Start(); err != nil {
164		t.Fatalf("err: %v", err)
165	}
166	defer a2.Shutdown()
167	defer a2.Leave()
168
169	testutil.Yield()
170
171	m := a2.Serf().LocalMember()
172
173	if !reflect.DeepEqual(m.Tags, tags) {
174		t.Fatalf("tags not restored: %#v", m.Tags)
175	}
176}
177
178func TestAgentTagsFile_BadOptions(t *testing.T) {
179	agentConfig := DefaultConfig()
180	agentConfig.TagsFile = "/some/path"
181	agentConfig.Tags = map[string]string{
182		"tag1": "val1",
183	}
184
185	_, err := Create(agentConfig, serf.DefaultConfig(), nil)
186	if err == nil || !strings.Contains(err.Error(), "not allowed") {
187		t.Fatalf("err: %v", err)
188	}
189}
190
191func TestAgent_MarshalTags(t *testing.T) {
192	tags := map[string]string{
193		"tag1": "val1",
194		"tag2": "val2",
195	}
196
197	tagPairs := MarshalTags(tags)
198
199	if !containsKey(tagPairs, "tag1=val1") {
200		t.Fatalf("bad: %v", tagPairs)
201	}
202	if !containsKey(tagPairs, "tag2=val2") {
203		t.Fatalf("bad: %v", tagPairs)
204	}
205}
206
207func TestAgent_UnmarshalTags(t *testing.T) {
208	tagPairs := []string{
209		"tag1=val1",
210		"tag2=val2",
211	}
212
213	tags, err := UnmarshalTags(tagPairs)
214
215	if err != nil {
216		t.Fatalf("err: %v", err)
217	}
218
219	if v, ok := tags["tag1"]; !ok || v != "val1" {
220		t.Fatalf("bad: %v", tags)
221	}
222	if v, ok := tags["tag2"]; !ok || v != "val2" {
223		t.Fatalf("bad: %v", tags)
224	}
225}
226
227func TestAgent_UnmarshalTagsError(t *testing.T) {
228	tagSets := [][]string{
229		[]string{"="},
230		[]string{"=x"},
231		[]string{""},
232		[]string{"x"},
233	}
234	for _, tagPairs := range tagSets {
235		if _, err := UnmarshalTags(tagPairs); err == nil {
236			t.Fatalf("Expected tag error: %s", tagPairs[0])
237		}
238	}
239}
240
241func TestAgentKeyringFile(t *testing.T) {
242	keys := []string{
243		"HvY8ubRZMgafUOWvrOadwOckVa1wN3QWAo46FVKbVN8=",
244		"T9jncgl9mbLus+baTTa7q7nPSUrXwbDi2dhbtqir37s=",
245		"5K9OtfP7efFrNKe5WCQvXvnaXJ5cWP0SvXiwe0kkjM4=",
246	}
247
248	td, err := ioutil.TempDir("", "serf")
249	if err != nil {
250		t.Fatalf("err: %v", err)
251	}
252	defer os.RemoveAll(td)
253
254	keyringFile := filepath.Join(td, "keyring.json")
255
256	serfConfig := serf.DefaultConfig()
257	agentConfig := DefaultConfig()
258	agentConfig.KeyringFile = keyringFile
259
260	encodedKeys, err := json.Marshal(keys)
261	if err != nil {
262		t.Fatalf("err: %v", err)
263	}
264
265	if err := ioutil.WriteFile(keyringFile, encodedKeys, 0600); err != nil {
266		t.Fatalf("err: %v", err)
267	}
268
269	ip1, returnFn1 := testutil.TakeIP()
270	defer returnFn1()
271
272	a1 := testAgentWithConfig(t, ip1, agentConfig, serfConfig, nil)
273
274	if err := a1.Start(); err != nil {
275		t.Fatalf("err: %v", err)
276	}
277	defer a1.Shutdown()
278
279	testutil.Yield()
280
281	totalLoadedKeys := len(serfConfig.MemberlistConfig.Keyring.GetKeys())
282	if totalLoadedKeys != 3 {
283		t.Fatalf("Expected to load 3 keys but got %d", totalLoadedKeys)
284	}
285}
286
287func TestAgentKeyringFile_BadOptions(t *testing.T) {
288	agentConfig := DefaultConfig()
289	agentConfig.KeyringFile = "/some/path"
290	agentConfig.EncryptKey = "5K9OtfP7efFrNKe5WCQvXvnaXJ5cWP0SvXiwe0kkjM4="
291
292	_, err := Create(agentConfig, serf.DefaultConfig(), nil)
293	if err == nil || !strings.Contains(err.Error(), "not allowed") {
294		t.Fatalf("err: %v", err)
295	}
296}
297
298func TestAgentKeyringFile_NoKeys(t *testing.T) {
299	dir, err := ioutil.TempDir("", "serf")
300	if err != nil {
301		t.Fatalf("err: %v", err)
302	}
303	defer os.RemoveAll(dir)
304
305	keysFile := filepath.Join(dir, "keyring")
306	if err := ioutil.WriteFile(keysFile, []byte("[]"), 0600); err != nil {
307		t.Fatalf("err: %v", err)
308	}
309
310	agentConfig := DefaultConfig()
311	agentConfig.KeyringFile = keysFile
312
313	_, err = Create(agentConfig, serf.DefaultConfig(), nil)
314	if err == nil {
315		t.Fatalf("should have errored")
316	}
317	if !strings.Contains(err.Error(), "contains no keys") {
318		t.Fatalf("bad: %s", err)
319	}
320}
321