blob: 1ef331247fd96aeca2455bbb075832c0e0ee16de [file] [log] [blame]
William Kurkianea869482019-04-09 15:16:11 -04001package api
2
3import (
4 "bufio"
Abhilash S.L3b494632019-07-16 15:51:09 +05305 "bytes"
William Kurkianea869482019-04-09 15:16:11 -04006 "fmt"
Abhilash S.L3b494632019-07-16 15:51:09 +05307 "io"
William Kurkianea869482019-04-09 15:16:11 -04008 "net/http"
9 "net/url"
10)
11
12// ServiceKind is the kind of service being registered.
13type ServiceKind string
14
15const (
16 // ServiceKindTypical is a typical, classic Consul service. This is
17 // represented by the absence of a value. This was chosen for ease of
18 // backwards compatibility: existing services in the catalog would
19 // default to the typical service.
20 ServiceKindTypical ServiceKind = ""
21
22 // ServiceKindConnectProxy is a proxy for the Connect feature. This
23 // service proxies another service within Consul and speaks the connect
24 // protocol.
25 ServiceKindConnectProxy ServiceKind = "connect-proxy"
William Kurkianea869482019-04-09 15:16:11 -040026
David Bainbridge788e5202019-10-21 18:49:40 +000027 // ServiceKindMeshGateway is a Mesh Gateway for the Connect feature. This
28 // service will proxy connections based off the SNI header set by other
29 // connect proxies
30 ServiceKindMeshGateway ServiceKind = "mesh-gateway"
William Kurkianea869482019-04-09 15:16:11 -040031)
32
33// UpstreamDestType is the type of upstream discovery mechanism.
34type UpstreamDestType string
35
36const (
37 // UpstreamDestTypeService discovers instances via healthy service lookup.
38 UpstreamDestTypeService UpstreamDestType = "service"
39
40 // UpstreamDestTypePreparedQuery discovers instances via prepared query
41 // execution.
42 UpstreamDestTypePreparedQuery UpstreamDestType = "prepared_query"
43)
44
45// AgentCheck represents a check known to the agent
46type AgentCheck struct {
47 Node string
48 CheckID string
49 Name string
50 Status string
51 Notes string
52 Output string
53 ServiceID string
54 ServiceName string
55 Definition HealthCheckDefinition
56}
57
58// AgentWeights represent optional weights for a service
59type AgentWeights struct {
60 Passing int
61 Warning int
62}
63
64// AgentService represents a service known to the agent
65type AgentService struct {
66 Kind ServiceKind `json:",omitempty"`
67 ID string
68 Service string
69 Tags []string
70 Meta map[string]string
71 Port int
72 Address string
David Bainbridge788e5202019-10-21 18:49:40 +000073 TaggedAddresses map[string]ServiceAddress `json:",omitempty"`
William Kurkianea869482019-04-09 15:16:11 -040074 Weights AgentWeights
75 EnableTagOverride bool
David Bainbridge788e5202019-10-21 18:49:40 +000076 CreateIndex uint64 `json:",omitempty" bexpr:"-"`
77 ModifyIndex uint64 `json:",omitempty" bexpr:"-"`
78 ContentHash string `json:",omitempty" bexpr:"-"`
79 Proxy *AgentServiceConnectProxyConfig `json:",omitempty"`
80 Connect *AgentServiceConnect `json:",omitempty"`
William Kurkianea869482019-04-09 15:16:11 -040081}
82
83// AgentServiceChecksInfo returns information about a Service and its checks
84type AgentServiceChecksInfo struct {
85 AggregatedStatus string
86 Service *AgentService
87 Checks HealthChecks
88}
89
90// AgentServiceConnect represents the Connect configuration of a service.
91type AgentServiceConnect struct {
92 Native bool `json:",omitempty"`
Abhilash S.L3b494632019-07-16 15:51:09 +053093 SidecarService *AgentServiceRegistration `json:",omitempty" bexpr:"-"`
William Kurkianea869482019-04-09 15:16:11 -040094}
95
William Kurkianea869482019-04-09 15:16:11 -040096// AgentServiceConnectProxyConfig is the proxy configuration in a connect-proxy
97// ServiceDefinition or response.
98type AgentServiceConnectProxyConfig struct {
David Bainbridge788e5202019-10-21 18:49:40 +000099 DestinationServiceName string `json:",omitempty"`
William Kurkianea869482019-04-09 15:16:11 -0400100 DestinationServiceID string `json:",omitempty"`
101 LocalServiceAddress string `json:",omitempty"`
102 LocalServicePort int `json:",omitempty"`
Abhilash S.L3b494632019-07-16 15:51:09 +0530103 Config map[string]interface{} `json:",omitempty" bexpr:"-"`
David Bainbridge788e5202019-10-21 18:49:40 +0000104 Upstreams []Upstream `json:",omitempty"`
105 MeshGateway MeshGatewayConfig `json:",omitempty"`
William Kurkianea869482019-04-09 15:16:11 -0400106}
107
108// AgentMember represents a cluster member known to the agent
109type AgentMember struct {
110 Name string
111 Addr string
112 Port uint16
113 Tags map[string]string
114 Status int
115 ProtocolMin uint8
116 ProtocolMax uint8
117 ProtocolCur uint8
118 DelegateMin uint8
119 DelegateMax uint8
120 DelegateCur uint8
121}
122
123// AllSegments is used to select for all segments in MembersOpts.
124const AllSegments = "_all"
125
126// MembersOpts is used for querying member information.
127type MembersOpts struct {
128 // WAN is whether to show members from the WAN.
129 WAN bool
130
131 // Segment is the LAN segment to show members for. Setting this to the
132 // AllSegments value above will show members in all segments.
133 Segment string
134}
135
136// AgentServiceRegistration is used to register a new service
137type AgentServiceRegistration struct {
David Bainbridge788e5202019-10-21 18:49:40 +0000138 Kind ServiceKind `json:",omitempty"`
139 ID string `json:",omitempty"`
140 Name string `json:",omitempty"`
141 Tags []string `json:",omitempty"`
142 Port int `json:",omitempty"`
143 Address string `json:",omitempty"`
144 TaggedAddresses map[string]ServiceAddress `json:",omitempty"`
145 EnableTagOverride bool `json:",omitempty"`
146 Meta map[string]string `json:",omitempty"`
147 Weights *AgentWeights `json:",omitempty"`
William Kurkianea869482019-04-09 15:16:11 -0400148 Check *AgentServiceCheck
149 Checks AgentServiceChecks
David Bainbridge788e5202019-10-21 18:49:40 +0000150 Proxy *AgentServiceConnectProxyConfig `json:",omitempty"`
151 Connect *AgentServiceConnect `json:",omitempty"`
William Kurkianea869482019-04-09 15:16:11 -0400152}
153
154// AgentCheckRegistration is used to register a new check
155type AgentCheckRegistration struct {
156 ID string `json:",omitempty"`
157 Name string `json:",omitempty"`
158 Notes string `json:",omitempty"`
159 ServiceID string `json:",omitempty"`
160 AgentServiceCheck
161}
162
163// AgentServiceCheck is used to define a node or service level check
164type AgentServiceCheck struct {
165 CheckID string `json:",omitempty"`
166 Name string `json:",omitempty"`
167 Args []string `json:"ScriptArgs,omitempty"`
168 DockerContainerID string `json:",omitempty"`
169 Shell string `json:",omitempty"` // Only supported for Docker.
170 Interval string `json:",omitempty"`
171 Timeout string `json:",omitempty"`
172 TTL string `json:",omitempty"`
173 HTTP string `json:",omitempty"`
174 Header map[string][]string `json:",omitempty"`
175 Method string `json:",omitempty"`
176 TCP string `json:",omitempty"`
177 Status string `json:",omitempty"`
178 Notes string `json:",omitempty"`
179 TLSSkipVerify bool `json:",omitempty"`
180 GRPC string `json:",omitempty"`
181 GRPCUseTLS bool `json:",omitempty"`
182 AliasNode string `json:",omitempty"`
183 AliasService string `json:",omitempty"`
184
185 // In Consul 0.7 and later, checks that are associated with a service
186 // may also contain this optional DeregisterCriticalServiceAfter field,
187 // which is a timeout in the same Go time format as Interval and TTL. If
188 // a check is in the critical state for more than this configured value,
189 // then its associated service (and all of its associated checks) will
190 // automatically be deregistered.
191 DeregisterCriticalServiceAfter string `json:",omitempty"`
192}
193type AgentServiceChecks []*AgentServiceCheck
194
195// AgentToken is used when updating ACL tokens for an agent.
196type AgentToken struct {
197 Token string
198}
199
200// Metrics info is used to store different types of metric values from the agent.
201type MetricsInfo struct {
202 Timestamp string
203 Gauges []GaugeValue
204 Points []PointValue
205 Counters []SampledValue
206 Samples []SampledValue
207}
208
209// GaugeValue stores one value that is updated as time goes on, such as
210// the amount of memory allocated.
211type GaugeValue struct {
212 Name string
213 Value float32
214 Labels map[string]string
215}
216
217// PointValue holds a series of points for a metric.
218type PointValue struct {
219 Name string
220 Points []float32
221}
222
223// SampledValue stores info about a metric that is incremented over time,
224// such as the number of requests to an HTTP endpoint.
225type SampledValue struct {
226 Name string
227 Count int
228 Sum float64
229 Min float64
230 Max float64
231 Mean float64
232 Stddev float64
233 Labels map[string]string
234}
235
236// AgentAuthorizeParams are the request parameters for authorizing a request.
237type AgentAuthorizeParams struct {
238 Target string
239 ClientCertURI string
240 ClientCertSerial string
241}
242
243// AgentAuthorize is the response structure for Connect authorization.
244type AgentAuthorize struct {
245 Authorized bool
246 Reason string
247}
248
249// ConnectProxyConfig is the response structure for agent-local proxy
250// configuration.
251type ConnectProxyConfig struct {
252 ProxyServiceID string
253 TargetServiceID string
254 TargetServiceName string
255 ContentHash string
David Bainbridge788e5202019-10-21 18:49:40 +0000256 Config map[string]interface{} `bexpr:"-"`
257 Upstreams []Upstream
William Kurkianea869482019-04-09 15:16:11 -0400258}
259
260// Upstream is the response structure for a proxy upstream configuration.
261type Upstream struct {
262 DestinationType UpstreamDestType `json:",omitempty"`
263 DestinationNamespace string `json:",omitempty"`
264 DestinationName string
265 Datacenter string `json:",omitempty"`
266 LocalBindAddress string `json:",omitempty"`
267 LocalBindPort int `json:",omitempty"`
Abhilash S.L3b494632019-07-16 15:51:09 +0530268 Config map[string]interface{} `json:",omitempty" bexpr:"-"`
David Bainbridge788e5202019-10-21 18:49:40 +0000269 MeshGateway MeshGatewayConfig `json:",omitempty"`
William Kurkianea869482019-04-09 15:16:11 -0400270}
271
272// Agent can be used to query the Agent endpoints
273type Agent struct {
274 c *Client
275
276 // cache the node name
277 nodeName string
278}
279
280// Agent returns a handle to the agent endpoints
281func (c *Client) Agent() *Agent {
282 return &Agent{c: c}
283}
284
285// Self is used to query the agent we are speaking to for
286// information about itself
287func (a *Agent) Self() (map[string]map[string]interface{}, error) {
288 r := a.c.newRequest("GET", "/v1/agent/self")
289 _, resp, err := requireOK(a.c.doRequest(r))
290 if err != nil {
291 return nil, err
292 }
293 defer resp.Body.Close()
294
295 var out map[string]map[string]interface{}
296 if err := decodeBody(resp, &out); err != nil {
297 return nil, err
298 }
299 return out, nil
300}
301
302// Host is used to retrieve information about the host the
303// agent is running on such as CPU, memory, and disk. Requires
304// a operator:read ACL token.
305func (a *Agent) Host() (map[string]interface{}, error) {
306 r := a.c.newRequest("GET", "/v1/agent/host")
307 _, resp, err := requireOK(a.c.doRequest(r))
308 if err != nil {
309 return nil, err
310 }
311 defer resp.Body.Close()
312
313 var out map[string]interface{}
314 if err := decodeBody(resp, &out); err != nil {
315 return nil, err
316 }
317 return out, nil
318}
319
320// Metrics is used to query the agent we are speaking to for
321// its current internal metric data
322func (a *Agent) Metrics() (*MetricsInfo, error) {
323 r := a.c.newRequest("GET", "/v1/agent/metrics")
324 _, resp, err := requireOK(a.c.doRequest(r))
325 if err != nil {
326 return nil, err
327 }
328 defer resp.Body.Close()
329
330 var out *MetricsInfo
331 if err := decodeBody(resp, &out); err != nil {
332 return nil, err
333 }
334 return out, nil
335}
336
337// Reload triggers a configuration reload for the agent we are connected to.
338func (a *Agent) Reload() error {
339 r := a.c.newRequest("PUT", "/v1/agent/reload")
340 _, resp, err := requireOK(a.c.doRequest(r))
341 if err != nil {
342 return err
343 }
344 resp.Body.Close()
345 return nil
346}
347
348// NodeName is used to get the node name of the agent
349func (a *Agent) NodeName() (string, error) {
350 if a.nodeName != "" {
351 return a.nodeName, nil
352 }
353 info, err := a.Self()
354 if err != nil {
355 return "", err
356 }
357 name := info["Config"]["NodeName"].(string)
358 a.nodeName = name
359 return name, nil
360}
361
362// Checks returns the locally registered checks
363func (a *Agent) Checks() (map[string]*AgentCheck, error) {
Abhilash S.L3b494632019-07-16 15:51:09 +0530364 return a.ChecksWithFilter("")
365}
366
367// ChecksWithFilter returns a subset of the locally registered checks that match
368// the given filter expression
369func (a *Agent) ChecksWithFilter(filter string) (map[string]*AgentCheck, error) {
William Kurkianea869482019-04-09 15:16:11 -0400370 r := a.c.newRequest("GET", "/v1/agent/checks")
Abhilash S.L3b494632019-07-16 15:51:09 +0530371 r.filterQuery(filter)
William Kurkianea869482019-04-09 15:16:11 -0400372 _, resp, err := requireOK(a.c.doRequest(r))
373 if err != nil {
374 return nil, err
375 }
376 defer resp.Body.Close()
377
378 var out map[string]*AgentCheck
379 if err := decodeBody(resp, &out); err != nil {
380 return nil, err
381 }
382 return out, nil
383}
384
385// Services returns the locally registered services
386func (a *Agent) Services() (map[string]*AgentService, error) {
Abhilash S.L3b494632019-07-16 15:51:09 +0530387 return a.ServicesWithFilter("")
388}
389
390// ServicesWithFilter returns a subset of the locally registered services that match
391// the given filter expression
392func (a *Agent) ServicesWithFilter(filter string) (map[string]*AgentService, error) {
William Kurkianea869482019-04-09 15:16:11 -0400393 r := a.c.newRequest("GET", "/v1/agent/services")
Abhilash S.L3b494632019-07-16 15:51:09 +0530394 r.filterQuery(filter)
William Kurkianea869482019-04-09 15:16:11 -0400395 _, resp, err := requireOK(a.c.doRequest(r))
396 if err != nil {
397 return nil, err
398 }
399 defer resp.Body.Close()
400
401 var out map[string]*AgentService
402 if err := decodeBody(resp, &out); err != nil {
403 return nil, err
404 }
405
406 return out, nil
407}
408
409// AgentHealthServiceByID returns for a given serviceID: the aggregated health status, the service definition or an error if any
410// - If the service is not found, will return status (critical, nil, nil)
411// - If the service is found, will return (critical|passing|warning), AgentServiceChecksInfo, nil)
412// - In all other cases, will return an error
413func (a *Agent) AgentHealthServiceByID(serviceID string) (string, *AgentServiceChecksInfo, error) {
414 path := fmt.Sprintf("/v1/agent/health/service/id/%v", url.PathEscape(serviceID))
415 r := a.c.newRequest("GET", path)
416 r.params.Add("format", "json")
417 r.header.Set("Accept", "application/json")
418 _, resp, err := a.c.doRequest(r)
419 if err != nil {
420 return "", nil, err
421 }
422 defer resp.Body.Close()
423 // Service not Found
424 if resp.StatusCode == http.StatusNotFound {
425 return HealthCritical, nil, nil
426 }
427 var out *AgentServiceChecksInfo
428 if err := decodeBody(resp, &out); err != nil {
429 return HealthCritical, out, err
430 }
431 switch resp.StatusCode {
432 case http.StatusOK:
433 return HealthPassing, out, nil
434 case http.StatusTooManyRequests:
435 return HealthWarning, out, nil
436 case http.StatusServiceUnavailable:
437 return HealthCritical, out, nil
438 }
439 return HealthCritical, out, fmt.Errorf("Unexpected Error Code %v for %s", resp.StatusCode, path)
440}
441
442// AgentHealthServiceByName returns for a given service name: the aggregated health status for all services
443// having the specified name.
444// - If no service is not found, will return status (critical, [], nil)
445// - If the service is found, will return (critical|passing|warning), []api.AgentServiceChecksInfo, nil)
446// - In all other cases, will return an error
447func (a *Agent) AgentHealthServiceByName(service string) (string, []AgentServiceChecksInfo, error) {
448 path := fmt.Sprintf("/v1/agent/health/service/name/%v", url.PathEscape(service))
449 r := a.c.newRequest("GET", path)
450 r.params.Add("format", "json")
451 r.header.Set("Accept", "application/json")
452 _, resp, err := a.c.doRequest(r)
453 if err != nil {
454 return "", nil, err
455 }
456 defer resp.Body.Close()
457 // Service not Found
458 if resp.StatusCode == http.StatusNotFound {
459 return HealthCritical, nil, nil
460 }
461 var out []AgentServiceChecksInfo
462 if err := decodeBody(resp, &out); err != nil {
463 return HealthCritical, out, err
464 }
465 switch resp.StatusCode {
466 case http.StatusOK:
467 return HealthPassing, out, nil
468 case http.StatusTooManyRequests:
469 return HealthWarning, out, nil
470 case http.StatusServiceUnavailable:
471 return HealthCritical, out, nil
472 }
473 return HealthCritical, out, fmt.Errorf("Unexpected Error Code %v for %s", resp.StatusCode, path)
474}
475
476// Service returns a locally registered service instance and allows for
477// hash-based blocking.
478//
479// Note that this uses an unconventional blocking mechanism since it's
480// agent-local state. That means there is no persistent raft index so we block
481// based on object hash instead.
482func (a *Agent) Service(serviceID string, q *QueryOptions) (*AgentService, *QueryMeta, error) {
483 r := a.c.newRequest("GET", "/v1/agent/service/"+serviceID)
484 r.setQueryOptions(q)
485 rtt, resp, err := requireOK(a.c.doRequest(r))
486 if err != nil {
487 return nil, nil, err
488 }
489 defer resp.Body.Close()
490
491 qm := &QueryMeta{}
492 parseQueryMeta(resp, qm)
493 qm.RequestTime = rtt
494
495 var out *AgentService
496 if err := decodeBody(resp, &out); err != nil {
497 return nil, nil, err
498 }
499
500 return out, qm, nil
501}
502
503// Members returns the known gossip members. The WAN
504// flag can be used to query a server for WAN members.
505func (a *Agent) Members(wan bool) ([]*AgentMember, error) {
506 r := a.c.newRequest("GET", "/v1/agent/members")
507 if wan {
508 r.params.Set("wan", "1")
509 }
510 _, resp, err := requireOK(a.c.doRequest(r))
511 if err != nil {
512 return nil, err
513 }
514 defer resp.Body.Close()
515
516 var out []*AgentMember
517 if err := decodeBody(resp, &out); err != nil {
518 return nil, err
519 }
520 return out, nil
521}
522
523// MembersOpts returns the known gossip members and can be passed
524// additional options for WAN/segment filtering.
525func (a *Agent) MembersOpts(opts MembersOpts) ([]*AgentMember, error) {
526 r := a.c.newRequest("GET", "/v1/agent/members")
527 r.params.Set("segment", opts.Segment)
528 if opts.WAN {
529 r.params.Set("wan", "1")
530 }
531
532 _, resp, err := requireOK(a.c.doRequest(r))
533 if err != nil {
534 return nil, err
535 }
536 defer resp.Body.Close()
537
538 var out []*AgentMember
539 if err := decodeBody(resp, &out); err != nil {
540 return nil, err
541 }
542 return out, nil
543}
544
545// ServiceRegister is used to register a new service with
546// the local agent
547func (a *Agent) ServiceRegister(service *AgentServiceRegistration) error {
548 r := a.c.newRequest("PUT", "/v1/agent/service/register")
549 r.obj = service
550 _, resp, err := requireOK(a.c.doRequest(r))
551 if err != nil {
552 return err
553 }
554 resp.Body.Close()
555 return nil
556}
557
558// ServiceDeregister is used to deregister a service with
559// the local agent
560func (a *Agent) ServiceDeregister(serviceID string) error {
561 r := a.c.newRequest("PUT", "/v1/agent/service/deregister/"+serviceID)
562 _, resp, err := requireOK(a.c.doRequest(r))
563 if err != nil {
564 return err
565 }
566 resp.Body.Close()
567 return nil
568}
569
570// PassTTL is used to set a TTL check to the passing state.
571//
572// DEPRECATION NOTICE: This interface is deprecated in favor of UpdateTTL().
573// The client interface will be removed in 0.8 or changed to use
574// UpdateTTL()'s endpoint and the server endpoints will be removed in 0.9.
575func (a *Agent) PassTTL(checkID, note string) error {
576 return a.updateTTL(checkID, note, "pass")
577}
578
579// WarnTTL is used to set a TTL check to the warning state.
580//
581// DEPRECATION NOTICE: This interface is deprecated in favor of UpdateTTL().
582// The client interface will be removed in 0.8 or changed to use
583// UpdateTTL()'s endpoint and the server endpoints will be removed in 0.9.
584func (a *Agent) WarnTTL(checkID, note string) error {
585 return a.updateTTL(checkID, note, "warn")
586}
587
588// FailTTL is used to set a TTL check to the failing state.
589//
590// DEPRECATION NOTICE: This interface is deprecated in favor of UpdateTTL().
591// The client interface will be removed in 0.8 or changed to use
592// UpdateTTL()'s endpoint and the server endpoints will be removed in 0.9.
593func (a *Agent) FailTTL(checkID, note string) error {
594 return a.updateTTL(checkID, note, "fail")
595}
596
597// updateTTL is used to update the TTL of a check. This is the internal
598// method that uses the old API that's present in Consul versions prior to
599// 0.6.4. Since Consul didn't have an analogous "update" API before it seemed
600// ok to break this (former) UpdateTTL in favor of the new UpdateTTL below,
601// but keep the old Pass/Warn/Fail methods using the old API under the hood.
602//
603// DEPRECATION NOTICE: This interface is deprecated in favor of UpdateTTL().
604// The client interface will be removed in 0.8 and the server endpoints will
605// be removed in 0.9.
606func (a *Agent) updateTTL(checkID, note, status string) error {
607 switch status {
608 case "pass":
609 case "warn":
610 case "fail":
611 default:
612 return fmt.Errorf("Invalid status: %s", status)
613 }
614 endpoint := fmt.Sprintf("/v1/agent/check/%s/%s", status, checkID)
615 r := a.c.newRequest("PUT", endpoint)
616 r.params.Set("note", note)
617 _, resp, err := requireOK(a.c.doRequest(r))
618 if err != nil {
619 return err
620 }
621 resp.Body.Close()
622 return nil
623}
624
625// checkUpdate is the payload for a PUT for a check update.
626type checkUpdate struct {
627 // Status is one of the api.Health* states: HealthPassing
628 // ("passing"), HealthWarning ("warning"), or HealthCritical
629 // ("critical").
630 Status string
631
632 // Output is the information to post to the UI for operators as the
633 // output of the process that decided to hit the TTL check. This is
634 // different from the note field that's associated with the check
635 // itself.
636 Output string
637}
638
639// UpdateTTL is used to update the TTL of a check. This uses the newer API
640// that was introduced in Consul 0.6.4 and later. We translate the old status
641// strings for compatibility (though a newer version of Consul will still be
642// required to use this API).
643func (a *Agent) UpdateTTL(checkID, output, status string) error {
644 switch status {
645 case "pass", HealthPassing:
646 status = HealthPassing
647 case "warn", HealthWarning:
648 status = HealthWarning
649 case "fail", HealthCritical:
650 status = HealthCritical
651 default:
652 return fmt.Errorf("Invalid status: %s", status)
653 }
654
655 endpoint := fmt.Sprintf("/v1/agent/check/update/%s", checkID)
656 r := a.c.newRequest("PUT", endpoint)
657 r.obj = &checkUpdate{
658 Status: status,
659 Output: output,
660 }
661
662 _, resp, err := requireOK(a.c.doRequest(r))
663 if err != nil {
664 return err
665 }
666 resp.Body.Close()
667 return nil
668}
669
670// CheckRegister is used to register a new check with
671// the local agent
672func (a *Agent) CheckRegister(check *AgentCheckRegistration) error {
673 r := a.c.newRequest("PUT", "/v1/agent/check/register")
674 r.obj = check
675 _, resp, err := requireOK(a.c.doRequest(r))
676 if err != nil {
677 return err
678 }
679 resp.Body.Close()
680 return nil
681}
682
683// CheckDeregister is used to deregister a check with
684// the local agent
685func (a *Agent) CheckDeregister(checkID string) error {
686 r := a.c.newRequest("PUT", "/v1/agent/check/deregister/"+checkID)
687 _, resp, err := requireOK(a.c.doRequest(r))
688 if err != nil {
689 return err
690 }
691 resp.Body.Close()
692 return nil
693}
694
695// Join is used to instruct the agent to attempt a join to
696// another cluster member
697func (a *Agent) Join(addr string, wan bool) error {
698 r := a.c.newRequest("PUT", "/v1/agent/join/"+addr)
699 if wan {
700 r.params.Set("wan", "1")
701 }
702 _, resp, err := requireOK(a.c.doRequest(r))
703 if err != nil {
704 return err
705 }
706 resp.Body.Close()
707 return nil
708}
709
710// Leave is used to have the agent gracefully leave the cluster and shutdown
711func (a *Agent) Leave() error {
712 r := a.c.newRequest("PUT", "/v1/agent/leave")
713 _, resp, err := requireOK(a.c.doRequest(r))
714 if err != nil {
715 return err
716 }
717 resp.Body.Close()
718 return nil
719}
720
721// ForceLeave is used to have the agent eject a failed node
722func (a *Agent) ForceLeave(node string) error {
723 r := a.c.newRequest("PUT", "/v1/agent/force-leave/"+node)
724 _, resp, err := requireOK(a.c.doRequest(r))
725 if err != nil {
726 return err
727 }
728 resp.Body.Close()
729 return nil
730}
731
732// ConnectAuthorize is used to authorize an incoming connection
733// to a natively integrated Connect service.
734func (a *Agent) ConnectAuthorize(auth *AgentAuthorizeParams) (*AgentAuthorize, error) {
735 r := a.c.newRequest("POST", "/v1/agent/connect/authorize")
736 r.obj = auth
737 _, resp, err := requireOK(a.c.doRequest(r))
738 if err != nil {
739 return nil, err
740 }
741 defer resp.Body.Close()
742
743 var out AgentAuthorize
744 if err := decodeBody(resp, &out); err != nil {
745 return nil, err
746 }
747 return &out, nil
748}
749
750// ConnectCARoots returns the list of roots.
751func (a *Agent) ConnectCARoots(q *QueryOptions) (*CARootList, *QueryMeta, error) {
752 r := a.c.newRequest("GET", "/v1/agent/connect/ca/roots")
753 r.setQueryOptions(q)
754 rtt, resp, err := requireOK(a.c.doRequest(r))
755 if err != nil {
756 return nil, nil, err
757 }
758 defer resp.Body.Close()
759
760 qm := &QueryMeta{}
761 parseQueryMeta(resp, qm)
762 qm.RequestTime = rtt
763
764 var out CARootList
765 if err := decodeBody(resp, &out); err != nil {
766 return nil, nil, err
767 }
768 return &out, qm, nil
769}
770
771// ConnectCALeaf gets the leaf certificate for the given service ID.
772func (a *Agent) ConnectCALeaf(serviceID string, q *QueryOptions) (*LeafCert, *QueryMeta, error) {
773 r := a.c.newRequest("GET", "/v1/agent/connect/ca/leaf/"+serviceID)
774 r.setQueryOptions(q)
775 rtt, resp, err := requireOK(a.c.doRequest(r))
776 if err != nil {
777 return nil, nil, err
778 }
779 defer resp.Body.Close()
780
781 qm := &QueryMeta{}
782 parseQueryMeta(resp, qm)
783 qm.RequestTime = rtt
784
785 var out LeafCert
786 if err := decodeBody(resp, &out); err != nil {
787 return nil, nil, err
788 }
789 return &out, qm, nil
790}
791
William Kurkianea869482019-04-09 15:16:11 -0400792// EnableServiceMaintenance toggles service maintenance mode on
793// for the given service ID.
794func (a *Agent) EnableServiceMaintenance(serviceID, reason string) error {
795 r := a.c.newRequest("PUT", "/v1/agent/service/maintenance/"+serviceID)
796 r.params.Set("enable", "true")
797 r.params.Set("reason", reason)
798 _, resp, err := requireOK(a.c.doRequest(r))
799 if err != nil {
800 return err
801 }
802 resp.Body.Close()
803 return nil
804}
805
806// DisableServiceMaintenance toggles service maintenance mode off
807// for the given service ID.
808func (a *Agent) DisableServiceMaintenance(serviceID string) error {
809 r := a.c.newRequest("PUT", "/v1/agent/service/maintenance/"+serviceID)
810 r.params.Set("enable", "false")
811 _, resp, err := requireOK(a.c.doRequest(r))
812 if err != nil {
813 return err
814 }
815 resp.Body.Close()
816 return nil
817}
818
819// EnableNodeMaintenance toggles node maintenance mode on for the
820// agent we are connected to.
821func (a *Agent) EnableNodeMaintenance(reason string) error {
822 r := a.c.newRequest("PUT", "/v1/agent/maintenance")
823 r.params.Set("enable", "true")
824 r.params.Set("reason", reason)
825 _, resp, err := requireOK(a.c.doRequest(r))
826 if err != nil {
827 return err
828 }
829 resp.Body.Close()
830 return nil
831}
832
833// DisableNodeMaintenance toggles node maintenance mode off for the
834// agent we are connected to.
835func (a *Agent) DisableNodeMaintenance() error {
836 r := a.c.newRequest("PUT", "/v1/agent/maintenance")
837 r.params.Set("enable", "false")
838 _, resp, err := requireOK(a.c.doRequest(r))
839 if err != nil {
840 return err
841 }
842 resp.Body.Close()
843 return nil
844}
845
846// Monitor returns a channel which will receive streaming logs from the agent
847// Providing a non-nil stopCh can be used to close the connection and stop the
848// log stream. An empty string will be sent down the given channel when there's
849// nothing left to stream, after which the caller should close the stopCh.
850func (a *Agent) Monitor(loglevel string, stopCh <-chan struct{}, q *QueryOptions) (chan string, error) {
851 r := a.c.newRequest("GET", "/v1/agent/monitor")
852 r.setQueryOptions(q)
853 if loglevel != "" {
854 r.params.Add("loglevel", loglevel)
855 }
856 _, resp, err := requireOK(a.c.doRequest(r))
857 if err != nil {
858 return nil, err
859 }
860
861 logCh := make(chan string, 64)
862 go func() {
863 defer resp.Body.Close()
864
865 scanner := bufio.NewScanner(resp.Body)
866 for {
867 select {
868 case <-stopCh:
869 close(logCh)
870 return
871 default:
872 }
873 if scanner.Scan() {
874 // An empty string signals to the caller that
875 // the scan is done, so make sure we only emit
876 // that when the scanner says it's done, not if
877 // we happen to ingest an empty line.
878 if text := scanner.Text(); text != "" {
879 logCh <- text
880 } else {
881 logCh <- " "
882 }
883 } else {
884 logCh <- ""
885 }
886 }
887 }()
888
889 return logCh, nil
890}
891
892// UpdateACLToken updates the agent's "acl_token". See updateToken for more
893// details.
894//
895// DEPRECATED (ACL-Legacy-Compat) - Prefer UpdateDefaultACLToken for v1.4.3 and above
896func (a *Agent) UpdateACLToken(token string, q *WriteOptions) (*WriteMeta, error) {
897 return a.updateToken("acl_token", token, q)
898}
899
900// UpdateACLAgentToken updates the agent's "acl_agent_token". See updateToken
901// for more details.
902//
903// DEPRECATED (ACL-Legacy-Compat) - Prefer UpdateAgentACLToken for v1.4.3 and above
904func (a *Agent) UpdateACLAgentToken(token string, q *WriteOptions) (*WriteMeta, error) {
905 return a.updateToken("acl_agent_token", token, q)
906}
907
908// UpdateACLAgentMasterToken updates the agent's "acl_agent_master_token". See
909// updateToken for more details.
910//
911// DEPRECATED (ACL-Legacy-Compat) - Prefer UpdateAgentMasterACLToken for v1.4.3 and above
912func (a *Agent) UpdateACLAgentMasterToken(token string, q *WriteOptions) (*WriteMeta, error) {
913 return a.updateToken("acl_agent_master_token", token, q)
914}
915
916// UpdateACLReplicationToken updates the agent's "acl_replication_token". See
917// updateToken for more details.
918//
919// DEPRECATED (ACL-Legacy-Compat) - Prefer UpdateReplicationACLToken for v1.4.3 and above
920func (a *Agent) UpdateACLReplicationToken(token string, q *WriteOptions) (*WriteMeta, error) {
921 return a.updateToken("acl_replication_token", token, q)
922}
923
924// UpdateDefaultACLToken updates the agent's "default" token. See updateToken
925// for more details
926func (a *Agent) UpdateDefaultACLToken(token string, q *WriteOptions) (*WriteMeta, error) {
927 return a.updateTokenFallback("default", "acl_token", token, q)
928}
929
930// UpdateAgentACLToken updates the agent's "agent" token. See updateToken
931// for more details
932func (a *Agent) UpdateAgentACLToken(token string, q *WriteOptions) (*WriteMeta, error) {
933 return a.updateTokenFallback("agent", "acl_agent_token", token, q)
934}
935
936// UpdateAgentMasterACLToken updates the agent's "agent_master" token. See updateToken
937// for more details
938func (a *Agent) UpdateAgentMasterACLToken(token string, q *WriteOptions) (*WriteMeta, error) {
939 return a.updateTokenFallback("agent_master", "acl_agent_master_token", token, q)
940}
941
942// UpdateReplicationACLToken updates the agent's "replication" token. See updateToken
943// for more details
944func (a *Agent) UpdateReplicationACLToken(token string, q *WriteOptions) (*WriteMeta, error) {
945 return a.updateTokenFallback("replication", "acl_replication_token", token, q)
946}
947
948// updateToken can be used to update one of an agent's ACL tokens after the agent has
949// started. The tokens are may not be persisted, so will need to be updated again if
950// the agent is restarted unless the agent is configured to persist them.
951func (a *Agent) updateToken(target, token string, q *WriteOptions) (*WriteMeta, error) {
952 meta, _, err := a.updateTokenOnce(target, token, q)
953 return meta, err
954}
955
956func (a *Agent) updateTokenFallback(target, fallback, token string, q *WriteOptions) (*WriteMeta, error) {
957 meta, status, err := a.updateTokenOnce(target, token, q)
958 if err != nil && status == 404 {
959 meta, _, err = a.updateTokenOnce(fallback, token, q)
960 }
961 return meta, err
962}
963
964func (a *Agent) updateTokenOnce(target, token string, q *WriteOptions) (*WriteMeta, int, error) {
965 r := a.c.newRequest("PUT", fmt.Sprintf("/v1/agent/token/%s", target))
966 r.setWriteOptions(q)
967 r.obj = &AgentToken{Token: token}
Abhilash S.L3b494632019-07-16 15:51:09 +0530968
969 rtt, resp, err := a.c.doRequest(r)
William Kurkianea869482019-04-09 15:16:11 -0400970 if err != nil {
Abhilash S.L3b494632019-07-16 15:51:09 +0530971 return nil, 0, err
William Kurkianea869482019-04-09 15:16:11 -0400972 }
Abhilash S.L3b494632019-07-16 15:51:09 +0530973 defer resp.Body.Close()
William Kurkianea869482019-04-09 15:16:11 -0400974
975 wm := &WriteMeta{RequestTime: rtt}
Abhilash S.L3b494632019-07-16 15:51:09 +0530976
977 if resp.StatusCode != 200 {
978 var buf bytes.Buffer
979 io.Copy(&buf, resp.Body)
980 return wm, resp.StatusCode, fmt.Errorf("Unexpected response code: %d (%s)", resp.StatusCode, buf.Bytes())
981 }
982
William Kurkianea869482019-04-09 15:16:11 -0400983 return wm, resp.StatusCode, nil
984}