blob: 0c18963fd60f03848bf944ba24bbbfad6f2d4ee8 [file] [log] [blame]
Abhilash S.L3b494632019-07-16 15:51:09 +05301package api
2
3import (
4 "bytes"
5 "encoding/json"
6 "fmt"
7 "io"
8 "strconv"
9 "strings"
10
11 "github.com/mitchellh/mapstructure"
12)
13
14const (
15 ServiceDefaults string = "service-defaults"
16 ProxyDefaults string = "proxy-defaults"
17 ProxyConfigGlobal string = "global"
18)
19
20type ConfigEntry interface {
21 GetKind() string
22 GetName() string
23 GetCreateIndex() uint64
24 GetModifyIndex() uint64
25}
26
27type ServiceConfigEntry struct {
28 Kind string
29 Name string
30 Protocol string
31 CreateIndex uint64
32 ModifyIndex uint64
33}
34
35func (s *ServiceConfigEntry) GetKind() string {
36 return s.Kind
37}
38
39func (s *ServiceConfigEntry) GetName() string {
40 return s.Name
41}
42
43func (s *ServiceConfigEntry) GetCreateIndex() uint64 {
44 return s.CreateIndex
45}
46
47func (s *ServiceConfigEntry) GetModifyIndex() uint64 {
48 return s.ModifyIndex
49}
50
51type ProxyConfigEntry struct {
52 Kind string
53 Name string
54 Config map[string]interface{}
55 CreateIndex uint64
56 ModifyIndex uint64
57}
58
59func (p *ProxyConfigEntry) GetKind() string {
60 return p.Kind
61}
62
63func (p *ProxyConfigEntry) GetName() string {
64 return p.Name
65}
66
67func (p *ProxyConfigEntry) GetCreateIndex() uint64 {
68 return p.CreateIndex
69}
70
71func (p *ProxyConfigEntry) GetModifyIndex() uint64 {
72 return p.ModifyIndex
73}
74
75type rawEntryListResponse struct {
76 kind string
77 Entries []map[string]interface{}
78}
79
80func makeConfigEntry(kind, name string) (ConfigEntry, error) {
81 switch kind {
82 case ServiceDefaults:
83 return &ServiceConfigEntry{Name: name}, nil
84 case ProxyDefaults:
85 return &ProxyConfigEntry{Name: name}, nil
86 default:
87 return nil, fmt.Errorf("invalid config entry kind: %s", kind)
88 }
89}
90
91func DecodeConfigEntry(raw map[string]interface{}) (ConfigEntry, error) {
92 var entry ConfigEntry
93
94 kindVal, ok := raw["Kind"]
95 if !ok {
96 kindVal, ok = raw["kind"]
97 }
98 if !ok {
99 return nil, fmt.Errorf("Payload does not contain a kind/Kind key at the top level")
100 }
101
102 if kindStr, ok := kindVal.(string); ok {
103 newEntry, err := makeConfigEntry(kindStr, "")
104 if err != nil {
105 return nil, err
106 }
107 entry = newEntry
108 } else {
109 return nil, fmt.Errorf("Kind value in payload is not a string")
110 }
111
112 decodeConf := &mapstructure.DecoderConfig{
113 DecodeHook: mapstructure.StringToTimeDurationHookFunc(),
114 Result: &entry,
115 WeaklyTypedInput: true,
116 }
117
118 decoder, err := mapstructure.NewDecoder(decodeConf)
119 if err != nil {
120 return nil, err
121 }
122
123 return entry, decoder.Decode(raw)
124}
125
126func DecodeConfigEntryFromJSON(data []byte) (ConfigEntry, error) {
127 var raw map[string]interface{}
128 if err := json.Unmarshal(data, &raw); err != nil {
129 return nil, err
130 }
131
132 return DecodeConfigEntry(raw)
133}
134
135// Config can be used to query the Config endpoints
136type ConfigEntries struct {
137 c *Client
138}
139
140// Config returns a handle to the Config endpoints
141func (c *Client) ConfigEntries() *ConfigEntries {
142 return &ConfigEntries{c}
143}
144
145func (conf *ConfigEntries) Get(kind string, name string, q *QueryOptions) (ConfigEntry, *QueryMeta, error) {
146 if kind == "" || name == "" {
147 return nil, nil, fmt.Errorf("Both kind and name parameters must not be empty")
148 }
149
150 entry, err := makeConfigEntry(kind, name)
151 if err != nil {
152 return nil, nil, err
153 }
154
155 r := conf.c.newRequest("GET", fmt.Sprintf("/v1/config/%s/%s", kind, name))
156 r.setQueryOptions(q)
157 rtt, resp, err := requireOK(conf.c.doRequest(r))
158 if err != nil {
159 return nil, nil, err
160 }
161
162 defer resp.Body.Close()
163
164 qm := &QueryMeta{}
165 parseQueryMeta(resp, qm)
166 qm.RequestTime = rtt
167
168 if err := decodeBody(resp, entry); err != nil {
169 return nil, nil, err
170 }
171
172 return entry, qm, nil
173}
174
175func (conf *ConfigEntries) List(kind string, q *QueryOptions) ([]ConfigEntry, *QueryMeta, error) {
176 if kind == "" {
177 return nil, nil, fmt.Errorf("The kind parameter must not be empty")
178 }
179
180 r := conf.c.newRequest("GET", fmt.Sprintf("/v1/config/%s", kind))
181 r.setQueryOptions(q)
182 rtt, resp, err := requireOK(conf.c.doRequest(r))
183 if err != nil {
184 return nil, nil, err
185 }
186
187 defer resp.Body.Close()
188
189 qm := &QueryMeta{}
190 parseQueryMeta(resp, qm)
191 qm.RequestTime = rtt
192
193 var raw []map[string]interface{}
194 if err := decodeBody(resp, &raw); err != nil {
195 return nil, nil, err
196 }
197
198 var entries []ConfigEntry
199 for _, rawEntry := range raw {
200 entry, err := DecodeConfigEntry(rawEntry)
201 if err != nil {
202 return nil, nil, err
203 }
204 entries = append(entries, entry)
205 }
206
207 return entries, qm, nil
208}
209
210func (conf *ConfigEntries) Set(entry ConfigEntry, w *WriteOptions) (bool, *WriteMeta, error) {
211 return conf.set(entry, nil, w)
212}
213
214func (conf *ConfigEntries) CAS(entry ConfigEntry, index uint64, w *WriteOptions) (bool, *WriteMeta, error) {
215 return conf.set(entry, map[string]string{"cas": strconv.FormatUint(index, 10)}, w)
216}
217
218func (conf *ConfigEntries) set(entry ConfigEntry, params map[string]string, w *WriteOptions) (bool, *WriteMeta, error) {
219 r := conf.c.newRequest("PUT", "/v1/config")
220 r.setWriteOptions(w)
221 for param, value := range params {
222 r.params.Set(param, value)
223 }
224 r.obj = entry
225 rtt, resp, err := requireOK(conf.c.doRequest(r))
226 if err != nil {
227 return false, nil, err
228 }
229 defer resp.Body.Close()
230
231 var buf bytes.Buffer
232 if _, err := io.Copy(&buf, resp.Body); err != nil {
233 return false, nil, fmt.Errorf("Failed to read response: %v", err)
234 }
235 res := strings.Contains(buf.String(), "true")
236
237 wm := &WriteMeta{RequestTime: rtt}
238 return res, wm, nil
239}
240
241func (conf *ConfigEntries) Delete(kind string, name string, w *WriteOptions) (*WriteMeta, error) {
242 if kind == "" || name == "" {
243 return nil, fmt.Errorf("Both kind and name parameters must not be empty")
244 }
245
246 r := conf.c.newRequest("DELETE", fmt.Sprintf("/v1/config/%s/%s", kind, name))
247 r.setWriteOptions(w)
248 rtt, resp, err := requireOK(conf.c.doRequest(r))
249 if err != nil {
250 return nil, err
251 }
252 resp.Body.Close()
253 wm := &WriteMeta{RequestTime: rtt}
254 return wm, nil
255}