1package api
2
3import (
4	"encoding/json"
5	"fmt"
6	"time"
7)
8
9// DiscoveryChain can be used to query the discovery-chain endpoints
10type DiscoveryChain struct {
11	c *Client
12}
13
14// DiscoveryChain returns a handle to the discovery-chain endpoints
15func (c *Client) DiscoveryChain() *DiscoveryChain {
16	return &DiscoveryChain{c}
17}
18
19func (d *DiscoveryChain) Get(name string, opts *DiscoveryChainOptions, q *QueryOptions) (*DiscoveryChainResponse, *QueryMeta, error) {
20	if name == "" {
21		return nil, nil, fmt.Errorf("Name parameter must not be empty")
22	}
23
24	method := "GET"
25	if opts != nil && opts.requiresPOST() {
26		method = "POST"
27	}
28
29	r := d.c.newRequest(method, fmt.Sprintf("/v1/discovery-chain/%s", name))
30	r.setQueryOptions(q)
31
32	if opts != nil {
33		if opts.EvaluateInDatacenter != "" {
34			r.params.Set("compile-dc", opts.EvaluateInDatacenter)
35		}
36	}
37
38	if method == "POST" {
39		r.obj = opts
40	}
41
42	rtt, resp, err := requireOK(d.c.doRequest(r))
43	if err != nil {
44		return nil, nil, err
45	}
46	defer closeResponseBody(resp)
47
48	qm := &QueryMeta{}
49	parseQueryMeta(resp, qm)
50	qm.RequestTime = rtt
51
52	var out DiscoveryChainResponse
53
54	if err := decodeBody(resp, &out); err != nil {
55		return nil, nil, err
56	}
57
58	return &out, qm, nil
59}
60
61type DiscoveryChainOptions struct {
62	EvaluateInDatacenter string `json:"-"`
63
64	// OverrideMeshGateway allows for the mesh gateway setting to be overridden
65	// for any resolver in the compiled chain.
66	OverrideMeshGateway MeshGatewayConfig `json:",omitempty"`
67
68	// OverrideProtocol allows for the final protocol for the chain to be
69	// altered.
70	//
71	// - If the chain ordinarily would be TCP and an L7 protocol is passed here
72	// the chain will not include Routers or Splitters.
73	//
74	// - If the chain ordinarily would be L7 and TCP is passed here the chain
75	// will not include Routers or Splitters.
76	OverrideProtocol string `json:",omitempty"`
77
78	// OverrideConnectTimeout allows for the ConnectTimeout setting to be
79	// overridden for any resolver in the compiled chain.
80	OverrideConnectTimeout time.Duration `json:",omitempty"`
81}
82
83func (o *DiscoveryChainOptions) requiresPOST() bool {
84	if o == nil {
85		return false
86	}
87	return o.OverrideMeshGateway.Mode != "" ||
88		o.OverrideProtocol != "" ||
89		o.OverrideConnectTimeout != 0
90}
91
92type DiscoveryChainResponse struct {
93	Chain *CompiledDiscoveryChain
94}
95
96type CompiledDiscoveryChain struct {
97	ServiceName string
98	Namespace   string
99	Datacenter  string
100
101	// CustomizationHash is a unique hash of any data that affects the
102	// compilation of the discovery chain other than config entries or the
103	// name/namespace/datacenter evaluation criteria.
104	//
105	// If set, this value should be used to prefix/suffix any generated load
106	// balancer data plane objects to avoid sharing customized and
107	// non-customized versions.
108	CustomizationHash string
109
110	// Protocol is the overall protocol shared by everything in the chain.
111	Protocol string
112
113	// StartNode is the first key into the Nodes map that should be followed
114	// when walking the discovery chain.
115	StartNode string
116
117	// Nodes contains all nodes available for traversal in the chain keyed by a
118	// unique name.  You can walk this by starting with StartNode.
119	//
120	// NOTE: The names should be treated as opaque values and are only
121	// guaranteed to be consistent within a single compilation.
122	Nodes map[string]*DiscoveryGraphNode
123
124	// Targets is a list of all targets used in this chain.
125	//
126	// NOTE: The names should be treated as opaque values and are only
127	// guaranteed to be consistent within a single compilation.
128	Targets map[string]*DiscoveryTarget
129}
130
131const (
132	DiscoveryGraphNodeTypeRouter   = "router"
133	DiscoveryGraphNodeTypeSplitter = "splitter"
134	DiscoveryGraphNodeTypeResolver = "resolver"
135)
136
137// DiscoveryGraphNode is a single node in the compiled discovery chain.
138type DiscoveryGraphNode struct {
139	Type string
140	Name string // this is NOT necessarily a service
141
142	// fields for Type==router
143	Routes []*DiscoveryRoute
144
145	// fields for Type==splitter
146	Splits []*DiscoverySplit
147
148	// fields for Type==resolver
149	Resolver *DiscoveryResolver
150
151	// shared by Type==resolver || Type==splitter
152	LoadBalancer *LoadBalancer `json:",omitempty"`
153}
154
155// compiled form of ServiceRoute
156type DiscoveryRoute struct {
157	Definition *ServiceRoute
158	NextNode   string
159}
160
161// compiled form of ServiceSplit
162type DiscoverySplit struct {
163	Weight   float32
164	NextNode string
165}
166
167// compiled form of ServiceResolverConfigEntry
168type DiscoveryResolver struct {
169	Default        bool
170	ConnectTimeout time.Duration
171	Target         string
172	Failover       *DiscoveryFailover
173}
174
175func (r *DiscoveryResolver) MarshalJSON() ([]byte, error) {
176	type Alias DiscoveryResolver
177	exported := &struct {
178		ConnectTimeout string `json:",omitempty"`
179		*Alias
180	}{
181		ConnectTimeout: r.ConnectTimeout.String(),
182		Alias:          (*Alias)(r),
183	}
184	if r.ConnectTimeout == 0 {
185		exported.ConnectTimeout = ""
186	}
187
188	return json.Marshal(exported)
189}
190
191func (r *DiscoveryResolver) UnmarshalJSON(data []byte) error {
192	type Alias DiscoveryResolver
193	aux := &struct {
194		ConnectTimeout string
195		*Alias
196	}{
197		Alias: (*Alias)(r),
198	}
199	if err := json.Unmarshal(data, &aux); err != nil {
200		return err
201	}
202	var err error
203	if aux.ConnectTimeout != "" {
204		if r.ConnectTimeout, err = time.ParseDuration(aux.ConnectTimeout); err != nil {
205			return err
206		}
207	}
208	return nil
209}
210
211// compiled form of ServiceResolverFailover
212type DiscoveryFailover struct {
213	Targets []string
214}
215
216// DiscoveryTarget represents all of the inputs necessary to use a resolver
217// config entry to execute a catalog query to generate a list of service
218// instances during discovery.
219type DiscoveryTarget struct {
220	ID string
221
222	Service       string
223	ServiceSubset string
224	Namespace     string
225	Datacenter    string
226
227	MeshGateway MeshGatewayConfig
228	Subset      ServiceResolverSubset
229	External    bool
230	SNI         string
231	Name        string
232}
233