blob: b179406dc12ce5d07ad9cf177ff9589a2de36cae [file] [log] [blame]
William Kurkianea869482019-04-09 15:16:11 -04001package api
2
3import (
4 "bytes"
5 "fmt"
6 "io"
7 "strconv"
8 "strings"
9 "time"
10)
11
12// AutopilotConfiguration is used for querying/setting the Autopilot configuration.
13// Autopilot helps manage operator tasks related to Consul servers like removing
14// failed servers from the Raft quorum.
15type AutopilotConfiguration struct {
16 // CleanupDeadServers controls whether to remove dead servers from the Raft
17 // peer list when a new server joins
18 CleanupDeadServers bool
19
20 // LastContactThreshold is the limit on the amount of time a server can go
21 // without leader contact before being considered unhealthy.
22 LastContactThreshold *ReadableDuration
23
24 // MaxTrailingLogs is the amount of entries in the Raft Log that a server can
25 // be behind before being considered unhealthy.
26 MaxTrailingLogs uint64
27
28 // ServerStabilizationTime is the minimum amount of time a server must be
29 // in a stable, healthy state before it can be added to the cluster. Only
30 // applicable with Raft protocol version 3 or higher.
31 ServerStabilizationTime *ReadableDuration
32
33 // (Enterprise-only) RedundancyZoneTag is the node tag to use for separating
34 // servers into zones for redundancy. If left blank, this feature will be disabled.
35 RedundancyZoneTag string
36
37 // (Enterprise-only) DisableUpgradeMigration will disable Autopilot's upgrade migration
38 // strategy of waiting until enough newer-versioned servers have been added to the
39 // cluster before promoting them to voters.
40 DisableUpgradeMigration bool
41
42 // (Enterprise-only) UpgradeVersionTag is the node tag to use for version info when
43 // performing upgrade migrations. If left blank, the Consul version will be used.
44 UpgradeVersionTag string
45
46 // CreateIndex holds the index corresponding the creation of this configuration.
47 // This is a read-only field.
48 CreateIndex uint64
49
50 // ModifyIndex will be set to the index of the last update when retrieving the
51 // Autopilot configuration. Resubmitting a configuration with
52 // AutopilotCASConfiguration will perform a check-and-set operation which ensures
53 // there hasn't been a subsequent update since the configuration was retrieved.
54 ModifyIndex uint64
55}
56
57// ServerHealth is the health (from the leader's point of view) of a server.
58type ServerHealth struct {
59 // ID is the raft ID of the server.
60 ID string
61
62 // Name is the node name of the server.
63 Name string
64
65 // Address is the address of the server.
66 Address string
67
68 // The status of the SerfHealth check for the server.
69 SerfStatus string
70
71 // Version is the Consul version of the server.
72 Version string
73
74 // Leader is whether this server is currently the leader.
75 Leader bool
76
77 // LastContact is the time since this node's last contact with the leader.
78 LastContact *ReadableDuration
79
80 // LastTerm is the highest leader term this server has a record of in its Raft log.
81 LastTerm uint64
82
83 // LastIndex is the last log index this server has a record of in its Raft log.
84 LastIndex uint64
85
86 // Healthy is whether or not the server is healthy according to the current
87 // Autopilot config.
88 Healthy bool
89
90 // Voter is whether this is a voting server.
91 Voter bool
92
93 // StableSince is the last time this server's Healthy value changed.
94 StableSince time.Time
95}
96
97// OperatorHealthReply is a representation of the overall health of the cluster
98type OperatorHealthReply struct {
99 // Healthy is true if all the servers in the cluster are healthy.
100 Healthy bool
101
102 // FailureTolerance is the number of healthy servers that could be lost without
103 // an outage occurring.
104 FailureTolerance int
105
106 // Servers holds the health of each server.
107 Servers []ServerHealth
108}
109
110// ReadableDuration is a duration type that is serialized to JSON in human readable format.
111type ReadableDuration time.Duration
112
113func NewReadableDuration(dur time.Duration) *ReadableDuration {
114 d := ReadableDuration(dur)
115 return &d
116}
117
118func (d *ReadableDuration) String() string {
119 return d.Duration().String()
120}
121
122func (d *ReadableDuration) Duration() time.Duration {
123 if d == nil {
124 return time.Duration(0)
125 }
126 return time.Duration(*d)
127}
128
129func (d *ReadableDuration) MarshalJSON() ([]byte, error) {
130 return []byte(fmt.Sprintf(`"%s"`, d.Duration().String())), nil
131}
132
133func (d *ReadableDuration) UnmarshalJSON(raw []byte) error {
134 if d == nil {
135 return fmt.Errorf("cannot unmarshal to nil pointer")
136 }
137
138 str := string(raw)
139 if len(str) < 2 || str[0] != '"' || str[len(str)-1] != '"' {
140 return fmt.Errorf("must be enclosed with quotes: %s", str)
141 }
142 dur, err := time.ParseDuration(str[1 : len(str)-1])
143 if err != nil {
144 return err
145 }
146 *d = ReadableDuration(dur)
147 return nil
148}
149
150// AutopilotGetConfiguration is used to query the current Autopilot configuration.
151func (op *Operator) AutopilotGetConfiguration(q *QueryOptions) (*AutopilotConfiguration, error) {
152 r := op.c.newRequest("GET", "/v1/operator/autopilot/configuration")
153 r.setQueryOptions(q)
154 _, resp, err := requireOK(op.c.doRequest(r))
155 if err != nil {
156 return nil, err
157 }
158 defer resp.Body.Close()
159
160 var out AutopilotConfiguration
161 if err := decodeBody(resp, &out); err != nil {
162 return nil, err
163 }
164
165 return &out, nil
166}
167
168// AutopilotSetConfiguration is used to set the current Autopilot configuration.
169func (op *Operator) AutopilotSetConfiguration(conf *AutopilotConfiguration, q *WriteOptions) error {
170 r := op.c.newRequest("PUT", "/v1/operator/autopilot/configuration")
171 r.setWriteOptions(q)
172 r.obj = conf
173 _, resp, err := requireOK(op.c.doRequest(r))
174 if err != nil {
175 return err
176 }
177 resp.Body.Close()
178 return nil
179}
180
181// AutopilotCASConfiguration is used to perform a Check-And-Set update on the
182// Autopilot configuration. The ModifyIndex value will be respected. Returns
183// true on success or false on failures.
184func (op *Operator) AutopilotCASConfiguration(conf *AutopilotConfiguration, q *WriteOptions) (bool, error) {
185 r := op.c.newRequest("PUT", "/v1/operator/autopilot/configuration")
186 r.setWriteOptions(q)
187 r.params.Set("cas", strconv.FormatUint(conf.ModifyIndex, 10))
188 r.obj = conf
189 _, resp, err := requireOK(op.c.doRequest(r))
190 if err != nil {
191 return false, err
192 }
193 defer resp.Body.Close()
194
195 var buf bytes.Buffer
196 if _, err := io.Copy(&buf, resp.Body); err != nil {
197 return false, fmt.Errorf("Failed to read response: %v", err)
198 }
199 res := strings.Contains(buf.String(), "true")
200
201 return res, nil
202}
203
204// AutopilotServerHealth
205func (op *Operator) AutopilotServerHealth(q *QueryOptions) (*OperatorHealthReply, error) {
206 r := op.c.newRequest("GET", "/v1/operator/autopilot/health")
207 r.setQueryOptions(q)
208 _, resp, err := requireOK(op.c.doRequest(r))
209 if err != nil {
210 return nil, err
211 }
212 defer resp.Body.Close()
213
214 var out OperatorHealthReply
215 if err := decodeBody(resp, &out); err != nil {
216 return nil, err
217 }
218 return &out, nil
219}