Skip to content

Commit 708ddcc

Browse files
authored
Merge pull request #97 from NETWAYS/release/v1-0-0
Release v1.0.0
2 parents de35cf7 + 3d7f816 commit 708ddcc

File tree

10 files changed

+130
-91
lines changed

10 files changed

+130
-91
lines changed

README.md

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,17 @@ Available Commands:
1717
snapshot Checks the status of Elasticsearch snapshots
1818
1919
Flags:
20-
-H, --hostname string Hostname of the Elasticsearch instance (CHECK_ELASTICSEARCH_HOSTNAME) (default "localhost")
21-
-p, --port int Port of the Elasticsearch instance (default 9200)
22-
-U, --username string Username for HTTP Basic Authentication (CHECK_ELASTICSEARCH_USERNAME)
23-
-P, --password string Password for HTTP Basic Authentication (CHECK_ELASTICSEARCH_PASSWORD)
24-
-S, --tls Use a HTTPS connection
25-
--insecure Skip the verification of the server's TLS certificate
26-
--ca-file string Specify the CA File for TLS authentication (CHECK_ELASTICSEARCH_CA_FILE)
27-
--cert-file string Specify the Certificate File for TLS authentication (CHECK_ELASTICSEARCH_CERT_FILE)
28-
--key-file string Specify the Key File for TLS authentication (CHECK_ELASTICSEARCH_KEY_FILE)
29-
-t, --timeout int Timeout in seconds for the CheckPlugin (default 30)
30-
-h, --help help for check_elasticsearch
31-
-v, --version version for check_elasticsearch
20+
-H, --hostname stringArray URL of an Elasticsearch instance. Can be used multiple times. (default [http://localhost:9200])
21+
-U, --username string Username for HTTP Basic Authentication (CHECK_ELASTICSEARCH_USERNAME)
22+
-P, --password string Password for HTTP Basic Authentication (CHECK_ELASTICSEARCH_PASSWORD)
23+
-b, --bearer string Specify the Bearer Token for authentication (CHECK_ELASTICSEARCH_BEARER)
24+
--insecure Skip the verification of the server's TLS certificate
25+
--ca-file string Specify the CA File for TLS authentication (CHECK_ELASTICSEARCH_CA_FILE)
26+
--cert-file string Specify the Certificate File for TLS authentication (CHECK_ELASTICSEARCH_CERT_FILE)
27+
--key-file string Specify the Key File for TLS authentication (CHECK_ELASTICSEARCH_KEY_FILE)
28+
-t, --timeout int Timeout in seconds for the plugin (default 30)
29+
-h, --help help for check_elasticsearch
30+
-v, --version version for check_elasticsearch
3231
```
3332

3433
The check plugin respects the environment variables `HTTP_PROXY`, `HTTPS_PROXY` and `NO_PROXY`.
@@ -54,14 +53,22 @@ Examples:
5453
Elasticsearch cluster with green status (all nodes are running):
5554

5655
```
57-
$ check_elasticsearch health -U exampleuser -P examplepassword -S --insecure
56+
$ check_elasticsearch health -U exampleuser -P examplepassword
57+
[OK] - Cluster es-example-cluster is green | status=0 nodes=3 data_nodes=3 active_primary_shards=10 active_shards=20
58+
```
59+
60+
When you have multiple cluster nodes:
61+
62+
```
63+
$ check_elasticsearch health -U exampleuser -P examplepassword \
64+
--hostname "https://node1:9200" --hostname "https://node2:9200" --hostname "https://node3:9200"
5865
[OK] - Cluster es-example-cluster is green | status=0 nodes=3 data_nodes=3 active_primary_shards=10 active_shards=20
5966
```
6067

6168
Elasticsearch cluster with yellow status (not all nodes are running):
6269

6370
```
64-
$ check_elasticsearch health -U exampleuser -P examplepassword -S --insecure
71+
$ check_elasticsearch health -U exampleuser -P examplepassword
6572
[WARNING] - Cluster es-example-cluster is yellow | status=1 nodes=2 data_nodes=2 active_primary_shards=10 active_shards=13```
6673
```
6774

cmd/config.go

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"net/url"
88
"os"
99
"reflect"
10-
"strconv"
1110
"time"
1211

1312
"github.com/NETWAYS/check_elasticsearch/internal/client"
@@ -16,16 +15,14 @@ import (
1615
)
1716

1817
type Config struct {
19-
Bearer string // Currently unused in CLI
20-
Hostname string `env:"CHECK_ELASTICSEARCH_HOSTNAME"`
21-
CAFile string `env:"CHECK_ELASTICSEARCH_CA_FILE"`
22-
CertFile string `env:"CHECK_ELASTICSEARCH_CERT_FILE"`
23-
KeyFile string `env:"CHECK_ELASTICSEARCH_KEY_FILE"`
24-
Username string `env:"CHECK_ELASTICSEARCH_USERNAME"`
25-
Password string `env:"CHECK_ELASTICSEARCH_PASSWORD"`
26-
Port int
27-
TLS bool
28-
Insecure bool
18+
Hostnames []string
19+
Bearer string `env:"CHECK_ELASTICSEARCH_BEARER"`
20+
CAFile string `env:"CHECK_ELASTICSEARCH_CA_FILE"`
21+
CertFile string `env:"CHECK_ELASTICSEARCH_CERT_FILE"`
22+
KeyFile string `env:"CHECK_ELASTICSEARCH_KEY_FILE"`
23+
Username string `env:"CHECK_ELASTICSEARCH_USERNAME"`
24+
Password string `env:"CHECK_ELASTICSEARCH_PASSWORD"`
25+
Insecure bool
2926
}
3027

3128
// LoadFromEnv can be used to load struct values from 'env' tags.
@@ -65,13 +62,16 @@ func loadFromEnv(config any) {
6562
var cliConfig Config
6663

6764
func (c *Config) NewClient() *client.Client {
68-
u := url.URL{
69-
Scheme: "http",
70-
Host: c.Hostname + ":" + strconv.Itoa(c.Port),
71-
}
65+
urls := make([]*url.URL, 0, len(c.Hostnames))
66+
67+
for _, host := range c.Hostnames {
68+
u, errParse := url.Parse(host)
69+
70+
if errParse != nil {
71+
check.ExitError(errParse)
72+
}
7273

73-
if c.TLS {
74-
u.Scheme = "https"
74+
urls = append(urls, u)
7575
}
7676

7777
// Create TLS configuration for default RoundTripper
@@ -110,5 +110,5 @@ func (c *Config) NewClient() *client.Client {
110110
rt = checkhttpconfig.NewBasicAuthRoundTripper(c.Username, c.Password, rt)
111111
}
112112

113-
return client.NewClient(u.String(), rt)
113+
return client.NewClient(urls, rt)
114114
}

cmd/health.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ The cluster health status is:
1515
green = OK
1616
yellow = WARNING
1717
red = CRITICAL`,
18-
Example: " check_elasticsearch health --hostname \"127.0.0.1\" --port 9200 --username \"exampleUser\" " +
19-
"--password \"examplePass\" --tls --insecure",
18+
Example: " check_elasticsearch health --hostname \"https://localhost:9200\" --username \"exampleUser\" " +
19+
"--password \"examplePass\" --insecure",
2020
Run: func(_ *cobra.Command, _ []string) {
2121
client := cliConfig.NewClient()
2222

@@ -26,6 +26,11 @@ The cluster health status is:
2626
}
2727

2828
var rc int
29+
// How we map cluster states:
30+
// green = OK
31+
// yellow = Warning
32+
// red = Critical
33+
// unknown = Unknown
2934
switch health.Status {
3035
case "green":
3136
rc = check.OK
@@ -42,12 +47,7 @@ The cluster health status is:
4247
output = "Cluster " + health.ClusterName + " is " + health.Status
4348
}
4449

45-
// green = 0
46-
// yellow = 1
47-
// red = 2
48-
// unknown = 3
4950
p := perfdata.PerfdataList{
50-
{Label: "status", Value: rc},
5151
{Label: "nodes", Value: health.NumberOfNodes},
5252
{Label: "data_nodes", Value: health.NumberOfDataNodes},
5353
{Label: "active_primary_shards", Value: health.ActivePrimaryShards},

cmd/health_test.go

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,18 @@ package cmd
33
import (
44
"net/http"
55
"net/http/httptest"
6-
"net/url"
76
"os/exec"
87
"strings"
98
"testing"
109
)
1110

1211
func TestHealth_ConnectionRefused(t *testing.T) {
1312

14-
cmd := exec.Command("go", "run", "../main.go", "health", "--port", "9999")
13+
cmd := exec.Command("go", "run", "../main.go", "health", "--hostname", "http://localhost:9999")
1514
out, _ := cmd.CombinedOutput()
1615

1716
actual := string(out)
18-
expected := "[UNKNOWN] - could not fetch cluster health: Get \"http://localhost:9999/_cluster/health\": dial"
17+
expected := "[UNKNOWN] - could not fetch cluster health: no node reachable (*errors.errorString)"
1918

2019
if !strings.Contains(actual, expected) {
2120
t.Error("\nActual: ", actual, "\nExpected: ", expected)
@@ -48,7 +47,24 @@ func TestHealthCmd(t *testing.T) {
4847
w.Write([]byte(`The Authorization header wasn't set`))
4948
})),
5049
args: []string{"run", "../main.go", "health", "--username", "username", "--password", "password"},
51-
expected: "[OK] - Cluster test is green | status=0 nodes=1 data_nodes=1 active_primary_shards=3 active_shards=3\n",
50+
expected: "[OK] - Cluster test is green | nodes=1 data_nodes=1 active_primary_shards=3 active_shards=3\n",
51+
},
52+
{
53+
name: "health-bearer-ok",
54+
server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
55+
token := r.Header.Get("Authorization")
56+
if token == "Bearer secret" {
57+
// Just for testing, this is now how to handle tokens properly
58+
w.Header().Set("X-Elastic-Product", "Elasticsearch")
59+
w.WriteHeader(http.StatusOK)
60+
w.Write([]byte(`{"cluster_name":"test","status":"green","timed_out":false,"number_of_nodes":1,"number_of_data_nodes":1,"active_primary_shards":3,"active_shards":3,"relocating_shards":0,"initializing_shards":0,"unassigned_shards":0,"delayed_unassigned_shards":0,"number_of_pending_tasks":0,"number_of_in_flight_fetch":0,"task_max_waiting_in_queue_millis":0,"active_shards_percent_as_number":100.0}`))
61+
return
62+
}
63+
w.WriteHeader(http.StatusUnauthorized)
64+
w.Write([]byte(`The Authorization header wasn't set`))
65+
})),
66+
args: []string{"run", "../main.go", "--bearer", "secret", "health"},
67+
expected: "[OK] - Cluster test is green | nodes=1 data_nodes=1 active_primary_shards=3 active_shards=3\n",
5268
},
5369
{
5470
name: "health-invalid",
@@ -58,7 +74,7 @@ func TestHealthCmd(t *testing.T) {
5874
w.Write([]byte(`{}`))
5975
})),
6076
args: []string{"run", "../main.go", "health"},
61-
expected: "[UNKNOWN] - Cluster status unknown | status=3 nodes=0 data_nodes=0 active_primary_shards=0 active_shards=0\nexit status 3\n",
77+
expected: "[UNKNOWN] - Cluster status unknown | nodes=0 data_nodes=0 active_primary_shards=0 active_shards=0\nexit status 3\n",
6278
},
6379
{
6480
name: "health-404",
@@ -78,7 +94,7 @@ func TestHealthCmd(t *testing.T) {
7894
w.Write([]byte(`{"cluster_name":"test","status":"foobar","timed_out":false,"number_of_nodes":1,"number_of_data_nodes":1,"active_primary_shards":3}`))
7995
})),
8096
args: []string{"run", "../main.go", "health"},
81-
expected: "[UNKNOWN] - Cluster test is foobar | status=3 nodes=1 data_nodes=1 active_primary_shards=3 active_shards=0\nexit status 3\n",
97+
expected: "[UNKNOWN] - Cluster test is foobar | nodes=1 data_nodes=1 active_primary_shards=3 active_shards=0\nexit status 3\n",
8298
},
8399
{
84100
name: "health-ok",
@@ -88,7 +104,7 @@ func TestHealthCmd(t *testing.T) {
88104
w.Write([]byte(`{"cluster_name":"test","status":"green","timed_out":false,"number_of_nodes":1,"number_of_data_nodes":1,"active_primary_shards":3,"active_shards":3,"relocating_shards":0,"initializing_shards":0,"unassigned_shards":0,"delayed_unassigned_shards":0,"number_of_pending_tasks":0,"number_of_in_flight_fetch":0,"task_max_waiting_in_queue_millis":0,"active_shards_percent_as_number":100.0}`))
89105
})),
90106
args: []string{"run", "../main.go", "health"},
91-
expected: "[OK] - Cluster test is green | status=0 nodes=1 data_nodes=1 active_primary_shards=3 active_shards=3\n",
107+
expected: "[OK] - Cluster test is green | nodes=1 data_nodes=1 active_primary_shards=3 active_shards=3\n",
92108
},
93109
{
94110
name: "health-yellow",
@@ -98,7 +114,7 @@ func TestHealthCmd(t *testing.T) {
98114
w.Write([]byte(`{"cluster_name":"test","status":"yellow","timed_out":false,"number_of_nodes":1,"number_of_data_nodes":1,"active_primary_shards":3,"active_shards":3,"relocating_shards":0,"initializing_shards":0,"unassigned_shards":0,"delayed_unassigned_shards":0,"number_of_pending_tasks":0,"number_of_in_flight_fetch":0,"task_max_waiting_in_queue_millis":0,"active_shards_percent_as_number":100.0}`))
99115
})),
100116
args: []string{"run", "../main.go", "health"},
101-
expected: "[WARNING] - Cluster test is yellow | status=1 nodes=1 data_nodes=1 active_primary_shards=3 active_shards=3\nexit status 1\n",
117+
expected: "[WARNING] - Cluster test is yellow | nodes=1 data_nodes=1 active_primary_shards=3 active_shards=3\nexit status 1\n",
102118
},
103119
{
104120
name: "health-red",
@@ -108,17 +124,15 @@ func TestHealthCmd(t *testing.T) {
108124
w.Write([]byte(`{"cluster_name":"test","status":"red","timed_out":false,"number_of_nodes":1,"number_of_data_nodes":1,"active_primary_shards":3,"active_shards":3,"relocating_shards":0,"initializing_shards":0,"unassigned_shards":0,"delayed_unassigned_shards":0,"number_of_pending_tasks":0,"number_of_in_flight_fetch":0,"task_max_waiting_in_queue_millis":0,"active_shards_percent_as_number":100.0}`))
109125
})),
110126
args: []string{"run", "../main.go", "health"},
111-
expected: "[CRITICAL] - Cluster test is red | status=2 nodes=1 data_nodes=1 active_primary_shards=3 active_shards=3\nexit status 2\n",
127+
expected: "[CRITICAL] - Cluster test is red | nodes=1 data_nodes=1 active_primary_shards=3 active_shards=3\nexit status 2\n",
112128
},
113129
}
114130

115131
for _, test := range tests {
116132
t.Run(test.name, func(t *testing.T) {
117133
defer test.server.Close()
118134

119-
// We need the random Port extracted
120-
u, _ := url.Parse(test.server.URL)
121-
cmd := exec.Command("go", append(test.args, "--port", u.Port())...)
135+
cmd := exec.Command("go", append(test.args, "--hostname", test.server.URL)...)
122136
out, _ := cmd.CombinedOutput()
123137

124138
actual := string(out)

cmd/ingest_test.go

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,18 @@ package cmd
33
import (
44
"net/http"
55
"net/http/httptest"
6-
"net/url"
76
"os/exec"
87
"strings"
98
"testing"
109
)
1110

1211
func TestIngest_ConnectionRefused(t *testing.T) {
1312

14-
cmd := exec.Command("go", "run", "../main.go", "ingest", "--port", "9999")
13+
cmd := exec.Command("go", "run", "../main.go", "ingest", "--hostname", "http://localhost:9999")
1514
out, _ := cmd.CombinedOutput()
1615

1716
actual := string(out)
18-
expected := "[UNKNOWN] - could not fetch cluster nodes statistics: Get \"http://localhost:9999/_nodes/stats\": dial"
17+
expected := "[UNKNOWN] - could not fetch cluster nodes statistics: no node reachable (*errors.errorString)"
1918

2019
if !strings.Contains(actual, expected) {
2120
t.Error("\nActual: ", actual, "\nExpected: ", expected)
@@ -82,9 +81,7 @@ func TestIngestCmd(t *testing.T) {
8281
t.Run(test.name, func(t *testing.T) {
8382
defer test.server.Close()
8483

85-
// We need the random Port extracted
86-
u, _ := url.Parse(test.server.URL)
87-
cmd := exec.Command("go", append(test.args, "--port", u.Port())...)
84+
cmd := exec.Command("go", append(test.args, "--hostname", test.server.URL)...)
8885
out, _ := cmd.CombinedOutput()
8986

9087
actual := string(out)

cmd/query_test.go

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,19 @@ package cmd
33
import (
44
"net/http"
55
"net/http/httptest"
6-
"net/url"
76
"os/exec"
87
"strings"
98
"testing"
109
)
1110

1211
func TestQuery_ConnectionRefused(t *testing.T) {
1312

14-
cmd := exec.Command("go", "run", "../main.go", "query", "--port", "9999")
13+
cmd := exec.Command("go", "run", "../main.go", "query", "--hostname", "http://localhost:9999")
1514
out, _ := cmd.CombinedOutput()
1615

1716
actual := string(out)
1817

19-
expected := "[UNKNOWN] - could not execute search request: Get \"http://localhost:9999/_all/_search?size=1&track_total_hits=true\": dial"
18+
expected := "[UNKNOWN] - could not execute search request: no node reachable (*errors.errorString)"
2019

2120
if !strings.Contains(actual, expected) {
2221
t.Error("\nActual: ", actual, "\nExpected: ", expected)
@@ -137,9 +136,7 @@ exit status 1
137136
t.Run(test.name, func(t *testing.T) {
138137
defer test.server.Close()
139138

140-
// We need the random Port extracted
141-
u, _ := url.Parse(test.server.URL)
142-
cmd := exec.Command("go", append(test.args, "--port", u.Port())...)
139+
cmd := exec.Command("go", append(test.args, "--hostname", test.server.URL)...)
143140
out, _ := cmd.CombinedOutput()
144141

145142
actual := string(out)

cmd/root.go

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,16 +41,14 @@ func init() {
4141
})
4242

4343
pfs := rootCmd.PersistentFlags()
44-
pfs.StringVarP(&cliConfig.Hostname, "hostname", "H", "localhost",
45-
"Hostname of the Elasticsearch instance (CHECK_ELASTICSEARCH_HOSTNAME)")
46-
pfs.IntVarP(&cliConfig.Port, "port", "p", 9200,
47-
"Port of the Elasticsearch instance")
44+
pfs.StringArrayVarP(&cliConfig.Hostnames, "hostname", "H", []string{"http://localhost:9200"},
45+
"URL of an Elasticsearch instance. Can be used multiple times.")
4846
pfs.StringVarP(&cliConfig.Username, "username", "U", "",
4947
"Username for HTTP Basic Authentication (CHECK_ELASTICSEARCH_USERNAME)")
5048
pfs.StringVarP(&cliConfig.Password, "password", "P", "",
5149
"Password for HTTP Basic Authentication (CHECK_ELASTICSEARCH_PASSWORD)")
52-
pfs.BoolVarP(&cliConfig.TLS, "tls", "S", false,
53-
"Use a HTTPS connection")
50+
pfs.StringVarP(&cliConfig.Bearer, "bearer", "b", "",
51+
"Specify the Bearer Token for authentication (CHECK_ELASTICSEARCH_BEARER)")
5452
pfs.BoolVar(&cliConfig.Insecure, "insecure", false,
5553
"Skip the verification of the server's TLS certificate")
5654
pfs.StringVarP(&cliConfig.CAFile, "ca-file", "", "",
@@ -60,7 +58,7 @@ func init() {
6058
pfs.StringVarP(&cliConfig.KeyFile, "key-file", "", "",
6159
"Specify the Key File for TLS authentication (CHECK_ELASTICSEARCH_KEY_FILE)")
6260
pfs.IntVarP(&timeout, "timeout", "t", timeout,
63-
"Timeout in seconds for the CheckPlugin")
61+
"Timeout in seconds for the plugin")
6462

6563
rootCmd.Flags().SortFlags = false
6664
pfs.SortFlags = false

cmd/snapshot_test.go

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,18 @@ package cmd
33
import (
44
"net/http"
55
"net/http/httptest"
6-
"net/url"
76
"os/exec"
87
"strings"
98
"testing"
109
)
1110

1211
func TestSnapshot_ConnectionRefused(t *testing.T) {
1312

14-
cmd := exec.Command("go", "run", "../main.go", "snapshot", "--port", "9999")
13+
cmd := exec.Command("go", "run", "../main.go", "snapshot", "--hostname", "http://localhost:9999")
1514
out, _ := cmd.CombinedOutput()
1615

1716
actual := string(out)
18-
expected := "[UNKNOWN] - could not fetch snapshots: Get \"http://localhost:9999/_snapshot/*/*?order=desc\": dial"
17+
expected := "[UNKNOWN] - could not fetch snapshots: no node reachable (*errors.errorString)"
1918

2019
if !strings.Contains(actual, expected) {
2120
t.Error("\nActual: ", actual, "\nExpected: ", expected)
@@ -154,9 +153,7 @@ func TestSnapshotCmd(t *testing.T) {
154153
t.Run(test.name, func(t *testing.T) {
155154
defer test.server.Close()
156155

157-
// We need the random Port extracted
158-
u, _ := url.Parse(test.server.URL)
159-
cmd := exec.Command("go", append(test.args, "--port", u.Port())...)
156+
cmd := exec.Command("go", append(test.args, "--hostname", test.server.URL)...)
160157
out, _ := cmd.CombinedOutput()
161158

162159
actual := string(out)

0 commit comments

Comments
 (0)