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