forked from StephanU/beats
-
Notifications
You must be signed in to change notification settings - Fork 0
/
client.go
220 lines (175 loc) · 5.4 KB
/
client.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
package kibana
import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"strings"
"github.com/elastic/beats/libbeat/common"
"github.com/elastic/beats/libbeat/outputs"
"github.com/elastic/beats/libbeat/outputs/transport"
)
type Connection struct {
URL string
Username string
Password string
Headers map[string]string
http *http.Client
version string
}
type Client struct {
Connection
}
func addToURL(_url, _path string, params url.Values) string {
if len(params) == 0 {
return _url + _path
}
return strings.Join([]string{_url, _path, "?", params.Encode()}, "")
}
func NewKibanaClient(cfg *common.Config) (*Client, error) {
config := defaultKibanaConfig
if err := cfg.Unpack(&config); err != nil {
return nil, err
}
kibanaURL, err := common.MakeURL(config.Protocol, config.Path, config.Host, 5601)
if err != nil {
return nil, fmt.Errorf("invalid Kibana host: %v", err)
}
u, err := url.Parse(kibanaURL)
if err != nil {
return nil, fmt.Errorf("failed to parse the Kibana URL: %v", err)
}
username := config.Username
password := config.Password
if u.User != nil {
username = u.User.Username()
password, _ = u.User.Password()
u.User = nil
// Re-write URL without credentials.
kibanaURL = u.String()
}
var dialer, tlsDialer transport.Dialer
tlsConfig, err := outputs.LoadTLSConfig(config.TLS)
if err != nil {
return nil, fmt.Errorf("fail to load the TLS config: %v", err)
}
dialer = transport.NetDialer(config.Timeout)
tlsDialer, err = transport.TLSDialer(dialer, tlsConfig, config.Timeout)
if err != nil {
return nil, err
}
client := &Client{
Connection: Connection{
URL: kibanaURL,
Username: username,
Password: password,
http: &http.Client{
Transport: &http.Transport{
Dial: dialer.Dial,
DialTLS: tlsDialer.Dial,
},
Timeout: config.Timeout,
},
},
}
if err = client.SetVersion(); err != nil {
return nil, fmt.Errorf("fail to get the Kibana version:%v", err)
}
return client, nil
}
func (conn *Connection) Request(method, extraPath string,
params url.Values, body io.Reader) (int, []byte, error) {
reqURL := addToURL(conn.URL, extraPath, params)
req, err := http.NewRequest(method, reqURL, body)
if err != nil {
return 0, nil, fmt.Errorf("fail to create the HTTP %s request: %v", method, err)
}
if conn.Username != "" || conn.Password != "" {
req.SetBasicAuth(conn.Username, conn.Password)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Add("Accept", "application/json")
if method != "GET" {
req.Header.Set("kbn-version", conn.version)
}
resp, err := conn.http.Do(req)
if err != nil {
return 0, nil, fmt.Errorf("fail to execute the HTTP %s request: %v", method, err)
}
defer resp.Body.Close()
var retError error
if resp.StatusCode >= 300 {
retError = fmt.Errorf("%v", resp.Status)
}
result, err := ioutil.ReadAll(resp.Body)
if err != nil {
return 0, nil, fmt.Errorf("fail to read response %s", err)
}
return resp.StatusCode, result, retError
}
func (client *Client) SetVersion() error {
type kibanaVersionResponse struct {
Name string `json:"name"`
Version struct {
Number string `json:"number"`
Snapshot bool `json:"build_snapshot"`
} `json:"version"`
}
type kibanaVersionResponse5x struct {
Name string `json:"name"`
Version string `json:"version"`
}
_, result, err := client.Connection.Request("GET", "/api/status", nil, nil)
if err != nil {
return fmt.Errorf("HTTP GET request to /api/status fails: %v. Response: %s.",
err, truncateString(result))
}
var kibanaVersion kibanaVersionResponse
var kibanaVersion5x kibanaVersionResponse5x
err = json.Unmarshal(result, &kibanaVersion)
if err != nil {
// The response returned by /api/status is different in Kibana 5.x than in Kibana 6.x
err5x := json.Unmarshal(result, &kibanaVersion5x)
if err5x != nil {
return fmt.Errorf("fail to unmarshal the response from GET %s/api/status. Response: %s. Kibana 5.x status api returns: %v. Kibana 6.x status api returns: %v",
client.Connection.URL, truncateString(result), err5x, err)
}
client.version = kibanaVersion5x.Version
} else {
client.version = kibanaVersion.Version.Number
if kibanaVersion.Version.Snapshot {
// needed for the tests
client.version = client.version + "-SNAPSHOT"
}
}
return nil
}
func (client *Client) GetVersion() string { return client.version }
func (client *Client) ImportJSON(url string, params url.Values, body io.Reader) error {
statusCode, response, err := client.Connection.Request("POST", url, params, body)
if err != nil {
return fmt.Errorf("%v. Response: %s", err, truncateString(response))
}
if statusCode >= 300 {
return fmt.Errorf("returned %d to import file: %v. Response: %s", statusCode, err, response)
}
return nil
}
func (client *Client) Close() error { return nil }
// truncateString returns a truncated string if the length is greater than 250
// runes. If the string is truncated "... (truncated)" is appended. Newlines are
// replaced by spaces in the returned string.
//
// This function is useful for logging raw HTTP responses with errors when those
// responses can be very large (such as an HTML page with CSS content).
func truncateString(b []byte) string {
const maxLength = 250
runes := bytes.Runes(b)
if len(runes) > maxLength {
runes = append(runes[:maxLength], []rune("... (truncated)")...)
}
return strings.Replace(string(runes), "\n", " ", -1)
}