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