1package agent
2
3import (
4	"context"
5	"testing"
6	"time"
7
8	"github.com/stretchr/testify/require"
9
10	"github.com/hashicorp/consul/agent/cache"
11	cachetype "github.com/hashicorp/consul/agent/cache-types"
12	"github.com/hashicorp/consul/agent/checks"
13	"github.com/hashicorp/consul/agent/structs"
14	"github.com/hashicorp/consul/testrpc"
15)
16
17// Integration test for ServiceHTTPBasedChecks cache-type
18// Placed in agent pkg rather than cache-types to avoid circular dependency when importing agent.TestAgent
19func TestAgent_ServiceHTTPChecksNotification(t *testing.T) {
20	if testing.Short() {
21		t.Skip("too slow for testing.Short")
22	}
23
24	t.Parallel()
25
26	a := NewTestAgent(t, "")
27	defer a.Shutdown()
28	testrpc.WaitForTestAgent(t, a.RPC, "dc1")
29
30	service := structs.NodeService{
31		ID:      "web",
32		Service: "web",
33	}
34
35	ctx, cancel := context.WithCancel(context.Background())
36	defer cancel()
37
38	ch := make(chan cache.UpdateEvent)
39
40	// Watch for service check updates
41	err := a.cache.Notify(ctx, cachetype.ServiceHTTPChecksName, &cachetype.ServiceHTTPChecksRequest{
42		ServiceID: service.ID,
43	}, "service-checks:"+service.ID, ch)
44	if err != nil {
45		t.Fatalf("failed to set cache notification: %v", err)
46	}
47
48	chkTypes := []*structs.CheckType{
49		{
50			CheckID:       "http-check",
51			HTTP:          "localhost:8080/health",
52			Interval:      5 * time.Second,
53			OutputMaxSize: checks.DefaultBufSize,
54		},
55		{
56			CheckID:  "grpc-check",
57			GRPC:     "localhost:9090/v1.Health",
58			Interval: 5 * time.Second,
59		},
60		{
61			CheckID: "ttl-check",
62			TTL:     10 * time.Second,
63		},
64	}
65	// Adding TTL type should lead to a timeout, since only HTTP-based checks are watched
66	if err := a.addServiceFromSource(&service, chkTypes[2:], false, "", ConfigSourceLocal); err != nil {
67		t.Fatalf("failed to add service: %v", err)
68	}
69
70	var val cache.UpdateEvent
71	select {
72	case val = <-ch:
73		t.Fatal("got cache update for TTL check, expected timeout")
74	case <-time.After(100 * time.Millisecond):
75	}
76
77	// Adding service with HTTP checks should lead notification for them
78	if err := a.addServiceFromSource(&service, chkTypes[0:2], false, "", ConfigSourceLocal); err != nil {
79		t.Fatalf("failed to add service: %v", err)
80	}
81
82	select {
83	case val = <-ch:
84	case <-time.After(100 * time.Millisecond):
85		t.Fatal("didn't get cache update event")
86	}
87
88	got, ok := val.Result.([]structs.CheckType)
89	if !ok {
90		t.Fatalf("notified of result of wrong type, got %T, want []structs.CheckType", got)
91	}
92	want := chkTypes[0:2]
93	for i, c := range want {
94		require.Equal(t, *c, got[i])
95	}
96
97	// Removing the GRPC check should leave only the HTTP check
98	if err := a.RemoveCheck(structs.NewCheckID(chkTypes[1].CheckID, nil), false); err != nil {
99		t.Fatalf("failed to remove check: %v", err)
100	}
101
102	select {
103	case val = <-ch:
104	case <-time.After(100 * time.Millisecond):
105		t.Fatal("didn't get cache update event")
106	}
107
108	got, ok = val.Result.([]structs.CheckType)
109	if !ok {
110		t.Fatalf("notified of result of wrong type, got %T, want []structs.CheckType", got)
111	}
112	want = chkTypes[0:1]
113	for i, c := range want {
114		require.Equal(t, *c, got[i])
115	}
116}
117