Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add status Subcommand #130

Merged
merged 3 commits into from
Jan 3, 2023
Merged
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
4 changes: 3 additions & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
run:
deadline: 1m
timeout: 5m
skip-files:
- '(.+)_test\.go'

linters:
disable-all: false
Expand Down
30 changes: 27 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,34 @@
# check_cloud_aws

Icinga check plugin to check Amazon AWS resources. At the moment the check supports EC2 instances, Cloudfront and S3
Buckets.
An Icinga check plugin to check Amazon AWS resources.

## Usage

### Health Status

A general status based on the RSS feed on the AWS Health Page

```
Usage:
check_cloud_aws status [flags]

Flags:
-u, --url string The AWS Status Page URL (default "https://status.aws.amazon.com")
-s, --service string The AWS Service to check (default "ec2")
-h, --help help for status
```

```
check_cloud_aws status --service cloudfront
OK - Service cloudfront is operating normally

check_cloud_aws --region us-west-1 status --service cloudwatch
WARNING - Information available for cloudwatch in us-west-1

check_cloud_aws --region eu-west-1 status
CRITICAL - Service disruption for ec2 in eu-west-1
```

### EC2 - Instances

When one of the states is non-ok, or a machine is stopped, the check will alert.
Expand Down Expand Up @@ -152,7 +176,7 @@ Global Flags:
````

````
$ check_cloud_aws cloudfront
$ check_cloud_aws cloudfront
CRITICAL - Found 2 Distributions - critical 1 - warning 1

[WARNING] E32127W2BLH4SR status=InProgress enabled=true
Expand Down
2 changes: 1 addition & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ var (

var rootCmd = &cobra.Command{
Use: "check_cloud_aws",
Short: "Icinga check plugin to check Amazon EC2 instances",
Short: "An Icinga check plugin to check Amazon Web Services",
PersistentPreRun: func(cmd *cobra.Command, args []string) {
go check.HandleTimeout(Timeout)
},
Expand Down
183 changes: 183 additions & 0 deletions cmd/status.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
package cmd

import (
"encoding/xml"
"fmt"
"github.com/NETWAYS/check_cloud_aws/internal/status"
"github.com/NETWAYS/go-check"
"github.com/spf13/cobra"
"net/http"
"net/url"
"strings"
)

// To store the CLI parameters
type StatusConfig struct {
Url string
Service string
}

var cliStatusConfig StatusConfig

func contains(s string, list []string) bool {
// Tiny helper to see if a string is in a list of strings
for _, elem := range list {
if s == elem {
return true
}
}

return false
}

var statusCmd = &cobra.Command{
Use: "status",
Short: "Checks the status of AWS services",
Example: `
check_cloud_aws status --service cloudfront
OK - Service cloudfront is operating normally

check_cloud_aws --region us-west-1 status --service cloudwatch
WARNING - Information available for cloudwatch in us-west-1

check_cloud_aws --region eu-west-1 status
CRITICAL - Service disruption for ec2 in eu-west-1

check_cloud_aws --region "" status iam
CRITICAL - WARNING - Information available for iam (Global)`,
Run: func(cmd *cobra.Command, args []string) {
var (
feed status.Rss
rc int
output string
feedUrl string
)

// These services don't require a region
// Hint: This list might not be extensive
var globalServices = []string{"route53",
"route53domainregistration",
"route53apprecoverycontroller",
"chime",
"health",
"import-export",
"iam",
"awsiotdevicemanagement",
"marketplace",
"apipricing",
"awswaf",
"trustedadvisor",
"supportcenter",
"resourcegroups",
"organizations",
"management-console",
"awsiotdevicemanagement",
"account",
"interregionvpcpeering",
"cloudfront",
"billingconsole",
"chatbot"}

// Creating an client and connecting to the RSS Feed
// Access the AWS Health Dashboard at health.aws.amazon.com directly,
// since the AWS Go SDK just supports Personal Dashboards
c := &http.Client{}

if Region == "" && !contains(cliStatusConfig.Service, globalServices) {
check.ExitError(fmt.Errorf("Region required for regional services"))
}

// Using + concatenation since the JoinPath will add / inbetween
if Region == "" && contains(cliStatusConfig.Service, globalServices) {
feedUrl, _ = url.JoinPath(cliStatusConfig.Url, "/rss/", cliStatusConfig.Service+".rss")
// Just for the output later
Region = "Global"
} else {
feedUrl, _ = url.JoinPath(cliStatusConfig.Url, "/rss/", cliStatusConfig.Service+"-"+Region+".rss")
}

resp, err := c.Get(feedUrl)

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

if resp.StatusCode != http.StatusOK {
check.ExitError(fmt.Errorf("Could not get %s - Error: %d", feedUrl, resp.StatusCode))
}

defer resp.Body.Close()
err = xml.NewDecoder(resp.Body).Decode(&feed)

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

// Exit if there are no events
if len(feed.Channel.Items) == 0 {
rc = check.OK
output = fmt.Sprintf("No events for %s (%s)", cliStatusConfig.Service, Region)
check.ExitRaw(rc, output)
}

rc = check.Unknown
output = "Status unknown"

// Get the latest event
item := strings.Split(feed.Channel.Items[0].Title, ":")

// If we couldn't split the title
if len(item) < 2 {
output = "Could not determine status."
check.ExitRaw(rc, output, feed.Channel.Items[0].Title)
}

event := item[0]
details := item[1]

if strings.Contains(event, "Service disruption") {
// Service disruptions are Critical
rc = check.Critical
output = fmt.Sprintf("Service disruption for %s (%s)", cliStatusConfig.Service, Region)
}

if strings.Contains(event, "Performance issue") {
// Performance issues are Warnings
rc = check.Warning
output = fmt.Sprintf("Performance issues for %s (%s)", cliStatusConfig.Service, Region)
}

if strings.Contains(event, "Informational message") {
// There will be no new item if an information is resolved,
// we need to check if the info is resolved.
if strings.Contains(details, "[RESOLVED]") {
rc = check.OK
output = fmt.Sprintf("Event resolved for %s (%s)", cliStatusConfig.Service, Region)
} else {
// An information that should be checked by someone
rc = check.Warning
output = fmt.Sprintf("Information available for %s (%s)", cliStatusConfig.Service, Region)
}
}

if strings.Contains(event, "Service is operating normally") {
rc = check.OK
output = fmt.Sprintf("Service %s is operating normally (%s)", cliStatusConfig.Service, Region)
}

check.ExitRaw(rc, output)
},
}

func init() {
rootCmd.AddCommand(statusCmd)

fs := statusCmd.Flags()

fs.StringVarP(&cliStatusConfig.Url, "url", "u", "https://status.aws.amazon.com",
"The AWS Status Page URL")
fs.StringVarP(&cliStatusConfig.Service, "service", "s", "ec2",
"The AWS Service to check")

fs.SortFlags = false
}
Loading