1package api
2
3import (
4	"testing"
5	"time"
6
7	"github.com/stretchr/testify/require"
8)
9
10// testClusterID is the Consul cluster ID for testing.
11//
12// NOTE: this is explicitly duplicated from agent/connect:TestClusterID
13const testClusterID = "11111111-2222-3333-4444-555555555555"
14
15func TestAPI_DiscoveryChain_Get(t *testing.T) {
16	t.Parallel()
17	c, s := makeClient(t)
18	defer s.Stop()
19
20	config_entries := c.ConfigEntries()
21	discoverychain := c.DiscoveryChain()
22
23	s.WaitForActiveCARoot(t)
24
25	require.True(t, t.Run("read default chain", func(t *testing.T) {
26		resp, _, err := discoverychain.Get("web", nil, nil)
27		require.NoError(t, err)
28
29		expect := &DiscoveryChainResponse{
30			Chain: &CompiledDiscoveryChain{
31				ServiceName: "web",
32				Namespace:   "default",
33				Datacenter:  "dc1",
34				Protocol:    "tcp",
35				StartNode:   "resolver:web.default.dc1",
36				Nodes: map[string]*DiscoveryGraphNode{
37					"resolver:web.default.dc1": {
38						Type: DiscoveryGraphNodeTypeResolver,
39						Name: "web.default.dc1",
40						Resolver: &DiscoveryResolver{
41							Default:        true,
42							ConnectTimeout: 5 * time.Second,
43							Target:         "web.default.dc1",
44						},
45					},
46				},
47				Targets: map[string]*DiscoveryTarget{
48					"web.default.dc1": {
49						ID:         "web.default.dc1",
50						Service:    "web",
51						Namespace:  "default",
52						Datacenter: "dc1",
53						SNI:        "web.default.dc1.internal." + testClusterID + ".consul",
54						Name:       "web.default.dc1.internal." + testClusterID + ".consul",
55					},
56				},
57			},
58		}
59		require.Equal(t, expect, resp)
60	}))
61
62	require.True(t, t.Run("read default chain; evaluate in dc2", func(t *testing.T) {
63		opts := &DiscoveryChainOptions{
64			EvaluateInDatacenter: "dc2",
65		}
66		resp, _, err := discoverychain.Get("web", opts, nil)
67		require.NoError(t, err)
68
69		expect := &DiscoveryChainResponse{
70			Chain: &CompiledDiscoveryChain{
71				ServiceName: "web",
72				Namespace:   "default",
73				Datacenter:  "dc2",
74				Protocol:    "tcp",
75				StartNode:   "resolver:web.default.dc2",
76				Nodes: map[string]*DiscoveryGraphNode{
77					"resolver:web.default.dc2": {
78						Type: DiscoveryGraphNodeTypeResolver,
79						Name: "web.default.dc2",
80						Resolver: &DiscoveryResolver{
81							Default:        true,
82							ConnectTimeout: 5 * time.Second,
83							Target:         "web.default.dc2",
84						},
85					},
86				},
87				Targets: map[string]*DiscoveryTarget{
88					"web.default.dc2": {
89						ID:         "web.default.dc2",
90						Service:    "web",
91						Namespace:  "default",
92						Datacenter: "dc2",
93						SNI:        "web.default.dc2.internal." + testClusterID + ".consul",
94						Name:       "web.default.dc2.internal." + testClusterID + ".consul",
95					},
96				},
97			},
98		}
99		require.Equal(t, expect, resp)
100	}))
101
102	{ // Now create one config entry.
103		ok, _, err := config_entries.Set(&ServiceResolverConfigEntry{
104			Kind:           ServiceResolver,
105			Name:           "web",
106			ConnectTimeout: 33 * time.Second,
107		}, nil)
108		require.NoError(t, err)
109		require.True(t, ok)
110	}
111
112	require.True(t, t.Run("read modified chain", func(t *testing.T) {
113		resp, _, err := discoverychain.Get("web", nil, nil)
114		require.NoError(t, err)
115
116		expect := &DiscoveryChainResponse{
117			Chain: &CompiledDiscoveryChain{
118				ServiceName: "web",
119				Namespace:   "default",
120				Datacenter:  "dc1",
121				Protocol:    "tcp",
122				StartNode:   "resolver:web.default.dc1",
123				Nodes: map[string]*DiscoveryGraphNode{
124					"resolver:web.default.dc1": {
125						Type: DiscoveryGraphNodeTypeResolver,
126						Name: "web.default.dc1",
127						Resolver: &DiscoveryResolver{
128							ConnectTimeout: 33 * time.Second,
129							Target:         "web.default.dc1",
130						},
131					},
132				},
133				Targets: map[string]*DiscoveryTarget{
134					"web.default.dc1": {
135						ID:         "web.default.dc1",
136						Service:    "web",
137						Namespace:  "default",
138						Datacenter: "dc1",
139						SNI:        "web.default.dc1.internal." + testClusterID + ".consul",
140						Name:       "web.default.dc1.internal." + testClusterID + ".consul",
141					},
142				},
143			},
144		}
145		require.Equal(t, expect, resp)
146	}))
147
148	require.True(t, t.Run("read modified chain in dc2 with overrides", func(t *testing.T) {
149		opts := &DiscoveryChainOptions{
150			EvaluateInDatacenter: "dc2",
151			OverrideMeshGateway: MeshGatewayConfig{
152				Mode: MeshGatewayModeLocal,
153			},
154			OverrideProtocol:       "grpc",
155			OverrideConnectTimeout: 22 * time.Second,
156		}
157		resp, _, err := discoverychain.Get("web", opts, nil)
158		require.NoError(t, err)
159
160		expect := &DiscoveryChainResponse{
161			Chain: &CompiledDiscoveryChain{
162				ServiceName:       "web",
163				Namespace:         "default",
164				Datacenter:        "dc2",
165				Protocol:          "grpc",
166				CustomizationHash: "98809527",
167				StartNode:         "resolver:web.default.dc2",
168				Nodes: map[string]*DiscoveryGraphNode{
169					"resolver:web.default.dc2": {
170						Type: DiscoveryGraphNodeTypeResolver,
171						Name: "web.default.dc2",
172						Resolver: &DiscoveryResolver{
173							ConnectTimeout: 22 * time.Second,
174							Target:         "web.default.dc2",
175						},
176					},
177				},
178				Targets: map[string]*DiscoveryTarget{
179					"web.default.dc2": {
180						ID:         "web.default.dc2",
181						Service:    "web",
182						Namespace:  "default",
183						Datacenter: "dc2",
184						MeshGateway: MeshGatewayConfig{
185							Mode: MeshGatewayModeLocal,
186						},
187						SNI:  "web.default.dc2.internal." + testClusterID + ".consul",
188						Name: "web.default.dc2.internal." + testClusterID + ".consul",
189					},
190				},
191			},
192		}
193		require.Equal(t, expect, resp)
194	}))
195}
196