Skip to content

Commit 1e4c19a

Browse files
committed
Refactor to remove go-elasticsearch
- Now uses Golang native HTTP requests - This change should make most of the subchecks work with ElasticSearch 7 and 8 as well as OpenSearch.
1 parent 182fd48 commit 1e4c19a

File tree

13 files changed

+1265
-207
lines changed

13 files changed

+1265
-207
lines changed

.golangci.yml

+17-9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
run:
2-
deadline: 1m
3-
tests: false
2+
timeout: 5m
3+
skip-files:
4+
- '(.+)_test\.go'
5+
- 'internal/config/http_config.go'
6+
- 'internal/config/config.go'
47

58
linters:
69
disable-all: false
@@ -10,14 +13,19 @@ linters:
1013
- whitespace
1114
- wsl
1215
- exportloopref
13-
presets:
14-
- bugs
15-
- unused
16-
fast: false
1716
disable:
18-
- gosec
1917
- funlen
2018
- scopelint
21-
- varcheck
22-
- deadcode
19+
- bodyclose
20+
- contextcheck
21+
- nilerr
22+
- noctx
23+
- rowserrcheck
24+
- sqlclosecheck
2325
- structcheck
26+
- unparam
27+
- musttag
28+
presets:
29+
- bugs
30+
- unused
31+
fast: false

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
.PHONY: test coverage lint vet
22

33
build:
4-
go build
4+
CGO_ENABLED=0 go build
55
lint:
66
go fmt $(go list ./... | grep -v /vendor/)
77
vet:

cmd/config.go

+53-10
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,32 @@ package cmd
22

33
import (
44
"check_elasticsearch/internal/client"
5+
"check_elasticsearch/internal/config"
6+
"fmt"
7+
"github.com/NETWAYS/go-check"
8+
"net/http"
59
"net/url"
610
"strconv"
11+
"time"
712
)
813

914
type Config struct {
10-
Hostname string
11-
Port int
12-
TLS bool
13-
Username string
14-
Password string
15-
Insecure bool
15+
Hostname string
16+
Port int
17+
BasicAuth string
18+
Bearer string
19+
CAFile string
20+
CertFile string
21+
KeyFile string
22+
TLS bool
23+
Username string
24+
Password string
25+
Insecure bool
1626
}
1727

1828
var cliConfig Config
1929

20-
func (c *Config) Client() *client.Client {
30+
func (c *Config) NewClient() *client.Client {
2131
u := url.URL{
2232
Scheme: "http",
2333
Host: c.Hostname + ":" + strconv.Itoa(c.Port),
@@ -27,8 +37,41 @@ func (c *Config) Client() *client.Client {
2737
u.Scheme = "https"
2838
}
2939

30-
cl := client.NewClient(u.String(), c.Username, c.Password)
31-
cl.Insecure = c.Insecure
40+
// Create TLS configuration for default RoundTripper
41+
tlsConfig, err := config.NewTLSConfig(&config.TLSConfig{
42+
InsecureSkipVerify: c.Insecure,
43+
CAFile: c.CAFile,
44+
KeyFile: c.KeyFile,
45+
CertFile: c.CertFile,
46+
})
3247

33-
return cl
48+
if err != nil {
49+
check.ExitError(err)
50+
}
51+
52+
var rt http.RoundTripper = &http.Transport{
53+
TLSClientConfig: tlsConfig,
54+
IdleConnTimeout: 10 * time.Second,
55+
TLSHandshakeTimeout: 10 * time.Second,
56+
ExpectContinueTimeout: 10 * time.Second,
57+
}
58+
59+
// Using a Bearer Token for authentication
60+
if c.Bearer != "" {
61+
var t config.Secret = config.Secret(c.Bearer)
62+
rt = config.NewAuthorizationCredentialsRoundTripper("Bearer", t, rt)
63+
}
64+
65+
// Using a BasicAuth for authentication
66+
if c.Username != "" {
67+
if c.Password == "" {
68+
check.ExitError(fmt.Errorf("Specify the user name and password for server authentication"))
69+
}
70+
71+
var p config.Secret = config.Secret(c.Password)
72+
73+
rt = config.NewBasicAuthRoundTripper(c.Username, p, "", rt)
74+
}
75+
76+
return client.NewClient(u.String(), rt)
3477
}

cmd/health.go

+1-5
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,7 @@ The cluster health status is:
1818
Example: " check_elasticsearch health --hostname \"127.0.0.1\" --port 9200 --username \"exampleUser\" " +
1919
"--password \"examplePass\" --tls --insecure",
2020
Run: func(cmd *cobra.Command, args []string) {
21-
client := cliConfig.Client()
22-
err := client.Connect()
23-
if err != nil {
24-
check.ExitError(err)
25-
}
21+
client := cliConfig.NewClient()
2622

2723
health, err := client.Health()
2824
if err != nil {

cmd/query.go

+1-5
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,7 @@ https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-query-
3737
output strings.Builder
3838
)
3939

40-
client := cliConfig.Client()
41-
err := client.Connect()
42-
if err != nil {
43-
check.ExitError(err)
44-
}
40+
client := cliConfig.NewClient()
4541

4642
total, messages, err := client.SearchMessages(
4743
cliQueryConfig.Index,

cmd/root.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ func init() {
4545
pfs.IntVarP(&cliConfig.Port, "port", "p", 9200,
4646
"Port of the Elasticsearch instance")
4747
pfs.StringVarP(&cliConfig.Username, "username", "U", "",
48-
"Username if authentication is required")
48+
"Username for HTTP Basic Authentication")
4949
pfs.StringVarP(&cliConfig.Password, "password", "P", "",
50-
"Password if authentication is required")
50+
"Password for HTTP Basic Authentication")
5151
pfs.BoolVarP(&cliConfig.TLS, "tls", "S", false,
5252
"Use a HTTPS connection")
5353
pfs.BoolVar(&cliConfig.Insecure, "insecure", false,

go.mod

+7-2
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,19 @@ go 1.19
44

55
require (
66
github.com/NETWAYS/go-check v0.4.2
7-
github.com/elastic/go-elasticsearch/v7 v7.17.10
87
github.com/spf13/cobra v1.7.0
8+
golang.org/x/oauth2 v0.8.0
9+
gopkg.in/yaml.v2 v2.4.0
910
)
1011

1112
require (
13+
github.com/golang/protobuf v1.5.2 // indirect
1214
github.com/inconshreveable/mousetrap v1.1.0 // indirect
1315
github.com/mitchellh/go-ps v1.0.0 // indirect
1416
github.com/sirupsen/logrus v1.9.2 // indirect
1517
github.com/spf13/pflag v1.0.5 // indirect
16-
golang.org/x/sys v0.2.0 // indirect
18+
golang.org/x/net v0.10.0 // indirect
19+
golang.org/x/sys v0.8.0 // indirect
20+
google.golang.org/appengine v1.6.7 // indirect
21+
google.golang.org/protobuf v1.28.0 // indirect
1722
)

go.sum

+28-4
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,12 @@ github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t
44
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
55
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
66
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
7-
github.com/elastic/go-elasticsearch/v7 v7.17.10 h1:TCQ8i4PmIJuBunvBS6bwT2ybzVFxxUhhltAs3Gyu1yo=
8-
github.com/elastic/go-elasticsearch/v7 v7.17.10/go.mod h1:OJ4wdbtDNk5g503kvlHLyErCgQwwzmDtaFC4XyOxXA4=
7+
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
8+
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
9+
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
10+
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
11+
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
12+
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
913
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
1014
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
1115
github.com/jarcoal/httpmock v1.3.0/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg=
@@ -29,10 +33,30 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
2933
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
3034
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
3135
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
36+
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
37+
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
38+
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
39+
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
40+
golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8=
41+
golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE=
42+
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
3243
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
33-
golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
34-
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
44+
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
45+
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
46+
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
47+
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
48+
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
49+
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
50+
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
51+
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
52+
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
53+
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
54+
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
55+
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
56+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
3557
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
58+
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
59+
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
3660
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
3761
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
3862
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

internal/client/client.go

+97-29
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,124 @@
11
package client
22

33
import (
4-
"crypto/tls"
4+
"bytes"
5+
es "check_elasticsearch/internal/elasticsearch"
6+
"encoding/json"
57
"fmt"
6-
es7 "github.com/elastic/go-elasticsearch/v7"
78
"net/http"
9+
"net/url"
810
)
911

1012
type Client struct {
11-
Url string
12-
Username string
13-
Password string
14-
Insecure bool
15-
Client *es7.Client
16-
Version string
13+
Client http.Client
14+
Url string
1715
}
1816

19-
func NewClient(url, username, password string) *Client {
17+
func NewClient(url string, rt http.RoundTripper) *Client {
18+
// Small wrapper
19+
c := &http.Client{
20+
Transport: rt,
21+
}
22+
2023
return &Client{
21-
Url: url,
22-
Username: username,
23-
Password: password,
24-
Insecure: false,
24+
Url: url,
25+
Client: *c,
2526
}
2627
}
2728

28-
func (c *Client) Connect() error {
29-
cfg := es7.Config{
30-
Addresses: []string{c.Url},
31-
Username: c.Username,
32-
Password: c.Password,
33-
Transport: &http.Transport{
34-
TLSClientConfig: &tls.Config{
35-
InsecureSkipVerify: c.Insecure,
36-
MinVersion: tls.VersionTLS11,
29+
func (c *Client) Health() (r *es.HealthResponse, err error) {
30+
u, _ := url.JoinPath(c.Url, "/_cluster/health")
31+
resp, err := c.Client.Get(u)
32+
33+
if err != nil {
34+
err = fmt.Errorf("could not fetch cluster health: %w", err)
35+
return
36+
}
37+
38+
if resp.StatusCode != http.StatusOK {
39+
err = fmt.Errorf("request failed for cluster health: %s", resp.Status)
40+
return
41+
}
42+
43+
r = &es.HealthResponse{}
44+
45+
defer resp.Body.Close()
46+
err = json.NewDecoder(resp.Body).Decode(r)
47+
48+
if err != nil {
49+
err = fmt.Errorf("could not decode health json: %w", err)
50+
return
51+
}
52+
53+
return
54+
}
55+
56+
func (c *Client) SearchMessages(index string, query string, messageKey string) (total uint, messages []string, err error) {
57+
queryBody := es.SearchRequest{
58+
Query: es.Query{
59+
QueryString: &es.QueryString{
60+
Query: query,
3761
},
3862
},
3963
}
4064

41-
esClient, err := es7.NewClient(cfg)
65+
data, err := json.Marshal(queryBody)
66+
body := bytes.NewReader(data)
67+
4268
if err != nil {
43-
return fmt.Errorf("could not connect to cluster: %w", err)
69+
err = fmt.Errorf("error encoding query: %w", err)
70+
return
4471
}
4572

46-
c.Client = esClient
73+
u, _ := url.JoinPath(c.Url, index, "/_search")
74+
75+
req, err := http.NewRequest("GET", u, body)
76+
77+
req.Header.Add("Content-Type", "application/json")
78+
79+
if err != nil {
80+
err = fmt.Errorf("error creating request: %w", err)
81+
return
82+
}
83+
84+
p := req.URL.Query()
85+
p.Add("track_total_hits", "true")
86+
p.Add("size", "1")
87+
88+
req.URL.RawQuery = p.Encode()
89+
90+
resp, err := c.Client.Do(req)
91+
92+
if err != nil {
93+
err = fmt.Errorf("could not execute search request: %w", err)
94+
return
95+
}
96+
97+
if resp.StatusCode != http.StatusOK {
98+
err = fmt.Errorf("request failed for search: %s", resp.Status)
99+
return
100+
}
101+
102+
var response es.SearchResponse
103+
104+
defer resp.Body.Close()
105+
err = json.NewDecoder(resp.Body).Decode(&response)
47106

48-
info, err := c.Info()
49107
if err != nil {
50-
return err
108+
err = fmt.Errorf("error parsing the response body: %w", err)
109+
return
51110
}
52111

53-
c.Version = info.Version.Number
112+
total = response.Hits.Total.Value
113+
114+
for _, hit := range response.Hits.Hits {
115+
if value, ok := hit.Source[messageKey]; ok {
116+
messages = append(messages, fmt.Sprint(value))
117+
} else {
118+
err = fmt.Errorf("message does not contain key '%s': %s", messageKey, hit.Id)
119+
return
120+
}
121+
}
54122

55-
return nil
123+
return
56124
}

0 commit comments

Comments
 (0)