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