Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 20 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,16 @@ Available Commands:
snapshot Checks the status of Elasticsearch snapshots

Flags:
-H, --hostname string Hostname of the Elasticsearch instance (CHECK_ELASTICSEARCH_HOSTNAME) (default "localhost")
-p, --port int Port of the Elasticsearch instance (default 9200)
-U, --username string Username for HTTP Basic Authentication (CHECK_ELASTICSEARCH_USERNAME)
-P, --password string Password for HTTP Basic Authentication (CHECK_ELASTICSEARCH_PASSWORD)
-S, --tls Use a HTTPS connection
--insecure Skip the verification of the server's TLS certificate
--ca-file string Specify the CA File for TLS authentication (CHECK_ELASTICSEARCH_CA_FILE)
--cert-file string Specify the Certificate File for TLS authentication (CHECK_ELASTICSEARCH_CERT_FILE)
--key-file string Specify the Key File for TLS authentication (CHECK_ELASTICSEARCH_KEY_FILE)
-t, --timeout int Timeout in seconds for the CheckPlugin (default 30)
-h, --help help for check_elasticsearch
-v, --version version for check_elasticsearch
-H, --hostname stringArray URL of an Elasticsearch instance. Can be used multiple times. (default [http://localhost:9200])
-U, --username string Username for HTTP Basic Authentication (CHECK_ELASTICSEARCH_USERNAME)
-P, --password string Password for HTTP Basic Authentication (CHECK_ELASTICSEARCH_PASSWORD)
--insecure Skip the verification of the server's TLS certificate
--ca-file string Specify the CA File for TLS authentication (CHECK_ELASTICSEARCH_CA_FILE)
--cert-file string Specify the Certificate File for TLS authentication (CHECK_ELASTICSEARCH_CERT_FILE)
--key-file string Specify the Key File for TLS authentication (CHECK_ELASTICSEARCH_KEY_FILE)
-t, --timeout int Timeout in seconds for the plugin (default 30)
-h, --help help for check_elasticsearch
-v, --version version for check_elasticsearch
```

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

```
$ check_elasticsearch health -U exampleuser -P examplepassword -S --insecure
$ check_elasticsearch health -U exampleuser -P examplepassword
[OK] - Cluster es-example-cluster is green | status=0 nodes=3 data_nodes=3 active_primary_shards=10 active_shards=20
```

When you have multiple cluster nodes:

```
$ check_elasticsearch health -U exampleuser -P examplepassword \
--hostname "https://node1:9200" --hostname "https://node2:9200" --hostname "https://node3:9200"
[OK] - Cluster es-example-cluster is green | status=0 nodes=3 data_nodes=3 active_primary_shards=10 active_shards=20
```

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

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

Expand Down
22 changes: 11 additions & 11 deletions cmd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"net/url"
"os"
"reflect"
"strconv"
"time"

"github.com/NETWAYS/check_elasticsearch/internal/client"
Expand All @@ -16,15 +15,13 @@ import (
)

type Config struct {
Hostname []string
Bearer string // Currently unused in CLI
Hostname string `env:"CHECK_ELASTICSEARCH_HOSTNAME"`
CAFile string `env:"CHECK_ELASTICSEARCH_CA_FILE"`
CertFile string `env:"CHECK_ELASTICSEARCH_CERT_FILE"`
KeyFile string `env:"CHECK_ELASTICSEARCH_KEY_FILE"`
Username string `env:"CHECK_ELASTICSEARCH_USERNAME"`
Password string `env:"CHECK_ELASTICSEARCH_PASSWORD"`
Port int
TLS bool
Insecure bool
}

Expand Down Expand Up @@ -65,13 +62,16 @@ func loadFromEnv(config any) {
var cliConfig Config

func (c *Config) NewClient() *client.Client {
u := url.URL{
Scheme: "http",
Host: c.Hostname + ":" + strconv.Itoa(c.Port),
}
urls := make([]*url.URL, 0, len(c.Hostname))

for _, host := range c.Hostname {
u, errParse := url.Parse(host)

if errParse != nil {
check.ExitError(errParse)
}

if c.TLS {
u.Scheme = "https"
urls = append(urls, u)
}

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

return client.NewClient(u.String(), rt)
return client.NewClient(urls, rt)
}
14 changes: 7 additions & 7 deletions cmd/health.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ The cluster health status is:
green = OK
yellow = WARNING
red = CRITICAL`,
Example: " check_elasticsearch health --hostname \"127.0.0.1\" --port 9200 --username \"exampleUser\" " +
"--password \"examplePass\" --tls --insecure",
Example: " check_elasticsearch health --hostname \"https://localhost:9200\" --username \"exampleUser\" " +
"--password \"examplePass\" --insecure",
Run: func(_ *cobra.Command, _ []string) {
client := cliConfig.NewClient()

Expand All @@ -26,6 +26,11 @@ The cluster health status is:
}

var rc int
// How we map cluster states:
// green = 0
// yellow = 1
// red = 2
// unknown = 3
switch health.Status {
case "green":
rc = check.OK
Expand All @@ -42,12 +47,7 @@ The cluster health status is:
output = "Cluster " + health.ClusterName + " is " + health.Status
}

// green = 0
// yellow = 1
// red = 2
// unknown = 3
p := perfdata.PerfdataList{
{Label: "status", Value: rc},
{Label: "nodes", Value: health.NumberOfNodes},
{Label: "data_nodes", Value: health.NumberOfDataNodes},
{Label: "active_primary_shards", Value: health.ActivePrimaryShards},
Expand Down
21 changes: 9 additions & 12 deletions cmd/health_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,18 @@ package cmd
import (
"net/http"
"net/http/httptest"
"net/url"
"os/exec"
"strings"
"testing"
)

func TestHealth_ConnectionRefused(t *testing.T) {

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

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

if !strings.Contains(actual, expected) {
t.Error("\nActual: ", actual, "\nExpected: ", expected)
Expand Down Expand Up @@ -48,7 +47,7 @@ func TestHealthCmd(t *testing.T) {
w.Write([]byte(`The Authorization header wasn't set`))
})),
args: []string{"run", "../main.go", "health", "--username", "username", "--password", "password"},
expected: "[OK] - Cluster test is green | status=0 nodes=1 data_nodes=1 active_primary_shards=3 active_shards=3\n",
expected: "[OK] - Cluster test is green | nodes=1 data_nodes=1 active_primary_shards=3 active_shards=3\n",
},
{
name: "health-invalid",
Expand All @@ -58,7 +57,7 @@ func TestHealthCmd(t *testing.T) {
w.Write([]byte(`{}`))
})),
args: []string{"run", "../main.go", "health"},
expected: "[UNKNOWN] - Cluster status unknown | status=3 nodes=0 data_nodes=0 active_primary_shards=0 active_shards=0\nexit status 3\n",
expected: "[UNKNOWN] - Cluster status unknown | nodes=0 data_nodes=0 active_primary_shards=0 active_shards=0\nexit status 3\n",
},
{
name: "health-404",
Expand All @@ -78,7 +77,7 @@ func TestHealthCmd(t *testing.T) {
w.Write([]byte(`{"cluster_name":"test","status":"foobar","timed_out":false,"number_of_nodes":1,"number_of_data_nodes":1,"active_primary_shards":3}`))
})),
args: []string{"run", "../main.go", "health"},
expected: "[UNKNOWN] - Cluster test is foobar | status=3 nodes=1 data_nodes=1 active_primary_shards=3 active_shards=0\nexit status 3\n",
expected: "[UNKNOWN] - Cluster test is foobar | nodes=1 data_nodes=1 active_primary_shards=3 active_shards=0\nexit status 3\n",
},
{
name: "health-ok",
Expand All @@ -88,7 +87,7 @@ func TestHealthCmd(t *testing.T) {
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}`))
})),
args: []string{"run", "../main.go", "health"},
expected: "[OK] - Cluster test is green | status=0 nodes=1 data_nodes=1 active_primary_shards=3 active_shards=3\n",
expected: "[OK] - Cluster test is green | nodes=1 data_nodes=1 active_primary_shards=3 active_shards=3\n",
},
{
name: "health-yellow",
Expand All @@ -98,7 +97,7 @@ func TestHealthCmd(t *testing.T) {
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}`))
})),
args: []string{"run", "../main.go", "health"},
expected: "[WARNING] - Cluster test is yellow | status=1 nodes=1 data_nodes=1 active_primary_shards=3 active_shards=3\nexit status 1\n",
expected: "[WARNING] - Cluster test is yellow | nodes=1 data_nodes=1 active_primary_shards=3 active_shards=3\nexit status 1\n",
},
{
name: "health-red",
Expand All @@ -108,17 +107,15 @@ func TestHealthCmd(t *testing.T) {
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}`))
})),
args: []string{"run", "../main.go", "health"},
expected: "[CRITICAL] - Cluster test is red | status=2 nodes=1 data_nodes=1 active_primary_shards=3 active_shards=3\nexit status 2\n",
expected: "[CRITICAL] - Cluster test is red | nodes=1 data_nodes=1 active_primary_shards=3 active_shards=3\nexit status 2\n",
},
}

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

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

actual := string(out)
Expand Down
9 changes: 3 additions & 6 deletions cmd/ingest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,18 @@ package cmd
import (
"net/http"
"net/http/httptest"
"net/url"
"os/exec"
"strings"
"testing"
)

func TestIngest_ConnectionRefused(t *testing.T) {

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

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

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

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

actual := string(out)
Expand Down
9 changes: 3 additions & 6 deletions cmd/query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,19 @@ package cmd
import (
"net/http"
"net/http/httptest"
"net/url"
"os/exec"
"strings"
"testing"
)

func TestQuery_ConnectionRefused(t *testing.T) {

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

actual := string(out)

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

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

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

actual := string(out)
Expand Down
10 changes: 3 additions & 7 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,12 @@ func init() {
})

pfs := rootCmd.PersistentFlags()
pfs.StringVarP(&cliConfig.Hostname, "hostname", "H", "localhost",
"Hostname of the Elasticsearch instance (CHECK_ELASTICSEARCH_HOSTNAME)")
pfs.IntVarP(&cliConfig.Port, "port", "p", 9200,
"Port of the Elasticsearch instance")
pfs.StringArrayVarP(&cliConfig.Hostname, "hostname", "H", []string{"http://localhost:9200"},
"URL of an Elasticsearch instance. Can be used multiple times.")
pfs.StringVarP(&cliConfig.Username, "username", "U", "",
"Username for HTTP Basic Authentication (CHECK_ELASTICSEARCH_USERNAME)")
pfs.StringVarP(&cliConfig.Password, "password", "P", "",
"Password for HTTP Basic Authentication (CHECK_ELASTICSEARCH_PASSWORD)")
pfs.BoolVarP(&cliConfig.TLS, "tls", "S", false,
"Use a HTTPS connection")
pfs.BoolVar(&cliConfig.Insecure, "insecure", false,
"Skip the verification of the server's TLS certificate")
pfs.StringVarP(&cliConfig.CAFile, "ca-file", "", "",
Expand All @@ -60,7 +56,7 @@ func init() {
pfs.StringVarP(&cliConfig.KeyFile, "key-file", "", "",
"Specify the Key File for TLS authentication (CHECK_ELASTICSEARCH_KEY_FILE)")
pfs.IntVarP(&timeout, "timeout", "t", timeout,
"Timeout in seconds for the CheckPlugin")
"Timeout in seconds for the plugin")

rootCmd.Flags().SortFlags = false
pfs.SortFlags = false
Expand Down
9 changes: 3 additions & 6 deletions cmd/snapshot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,18 @@ package cmd
import (
"net/http"
"net/http/httptest"
"net/url"
"os/exec"
"strings"
"testing"
)

func TestSnapshot_ConnectionRefused(t *testing.T) {

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

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

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

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

actual := string(out)
Expand Down
Loading