Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
AndreZiviani committed Apr 3, 2023
1 parent 29458f1 commit 9b3e73b
Show file tree
Hide file tree
Showing 13 changed files with 1,052 additions and 1 deletion.
74 changes: 74 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
name: Create Release
on:
push:
tags:
- '*'

jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0

- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.19

- name: Cache Go Modules
uses: actions/cache@v3
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.OS }}-go-
${{ runner.OS }}-
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v4
with:
version: latest
args: release --rm-dist
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

push_to_registries:
name: Push Docker image to Docker Hub and GitHub Packages
runs-on: ubuntu-latest
steps:
- name: Check out the repo
uses: actions/checkout@v3
with:
fetch-depth: 0

- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Log in to the Container registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v4
with:
images: |
${{ secrets.DOCKERHUB_USERNAME }}/aws-health-exporter
ghcr.io/${{ github.repository }}
- name: Build and push Docker images
uses: docker/build-push-action@v3
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
18 changes: 18 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
FROM golang:alpine as builder

WORKDIR /app

COPY . .

RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o aws-health-exporter .

FROM alpine:3.17

ENV HOME /app
USER 1000:1000

WORKDIR /app

COPY --from=builder /app/aws-health-exporter /bin

ENTRYPOINT ["/bin/aws-health-exporter"]
96 changes: 96 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
EXECUTABLE ?= aws-health-exporter
IMAGE ?= andreziviani/$(EXECUTABLE)
TAG ?= dev-$(shell git log -1 --pretty=format:"%h")

LD_FLAGS = -X "main.version=$(TAG)"
GOFILES_NOVENDOR = $(shell find . -type f -name '*.go' -not -path "./vendor/*")
PKGS=$(shell go list ./... | grep -v /vendor)

.PHONY: _no-target-specified
_no-target-specified:
$(error Please specify the target to make - `make list` shows targets.)

.PHONY: list
list:
@$(MAKE) -pRrn : -f $(MAKEFILE_LIST) 2>/dev/null | awk -v RS= -F: '/^# File/,/^# Finished Make data base/ {if ($$1 !~ "^[#.]") {print $$1}}' | egrep -v -e '^[^[:alnum:]]' -e '^$@$$' | sort

LICENSEI_VERSION = 0.0.7
bin/licensei: ## Install license checker
@mkdir -p ./bin/
curl -sfL https://raw.githubusercontent.com/goph/licensei/master/install.sh | bash -s v${LICENSEI_VERSION}

.PHONY: license-check
license-check: bin/licensei ## Run license check
@bin/licensei check

.PHONY: license-cache
license-cache: bin/licensei ## Generate license cache
@bin/licensei cache

all: clean deps fmt vet docker push

clean:
go clean -i ./...

deps:
go get ./...

fmt:
@gofmt -w ${GOFILES_NOVENDOR}

vet:
@go vet -composites=false ./...

docker:
docker build --rm -t $(IMAGE):$(TAG) .

docker-run:
docker run -e AWS_REGION=$${AWS_REGION} -e AWS_SECRET_ACCESS_KEY=$${AWS_SECRET_ACCESS_KEY} -e AWS_SESSION_TOKEN=$${AWS_SESSION_TOKEN} -e AWS_ACCESS_KEY_ID=$${AWS_ACCESS_KEY_ID} -it --rm -p 8080:8080 $(IMAGE):$(TAG)

push:
docker push $(IMAGE):$(TAG)

run-dev:
go run $(wildcard *.go)

build:
go build -o $(EXECUTABLE) $(wildcard *.go)

build-all: fmt lint vet build

misspell: install-misspell
misspell -w ${GOFILES_NOVENDOR}

lint: install-golint
golint -min_confidence 0.9 -set_exit_status $(PKGS)

install-golint:
GOLINT_CMD=$(shell command -v golint 2> /dev/null)
ifndef GOLINT_CMD
go get github.com/golang/lint/golint
endif

install-misspell:
MISSPELL_CMD=$(shell command -v misspell 2> /dev/null)
ifndef MISSPELL_CMD
go get -u github.com/client9/misspell/cmd/misspell
endif

install-ineffassign:
INEFFASSIGN_CMD=$(shell command -v ineffassign 2> /dev/null)
ifndef INEFFASSIGN_CMD
go get -u github.com/gordonklaus/ineffassign
endif

install-gocyclo:
GOCYCLO_CMD=$(shell command -v gocyclo 2> /dev/null)
ifndef GOCYCLO_CMD
go get -u github.com/fzipp/gocyclo
endif

ineffassign: install-ineffassign
ineffassign ${GOFILES_NOVENDOR}

gocyclo: install-gocyclo
gocyclo -over 19 ${GOFILES_NOVENDOR}

53 changes: 52 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,53 @@
# aws-health-exporter
Prometheus exporter for AWS Health events that also sends them to Slack

This is a trivial implementation of [AWS AHA][aha-blog] with limited features but without any external dependencies.

**You must have a Business, Enterprise On-Ramp, or Enterprise Support plan from AWS Support to use the AWS Health API. If you call the AWS Health API from an AWS account that doesn't have a Business, Enterprise On-Ramp, or Enterprise Support plan, you receive a SubscriptionRequiredException error.**

This is a restriction imposed by AWS, check [the docs][health-api] for more information.

## Features

- Does not require a database
- Sends AWS Health events to slack
- Expose events as Prometheus metrics

## How it works

This exporter checks for new AWS Health events whenever it is scraped and sends them to a slack channel using the same message format as AWS AHA.

If the exporter is running on the Payer account (or with credentials from that account) and [AWS Health Organizational View][health-org] is enabled
it will monitor events from all accounts, otherwise it will check only the current account.

Only new events will be sent to slack, past events (events that were created/updated before the exporter started) will be ignored.

## How to use

The exporter should be running with the following permissions:
```
"health:DescribeHealthServiceStatusForOrganization",
"health:DescribeAffectedAccountsForOrganization",
"health:DescribeAffectedEntitiesForOrganization",
"health:DescribeEventDetailsForOrganization",
"health:DescribeEventsForOrganization",
"health:DescribeEventDetails",
"health:DescribeEvents",
"health:DescribeEventTypes",
"health:DescribeAffectedEntities",
"organizations:ListAccounts",
"organizations:DescribeAccount",
```

You must specify, at least, the following parameters via command options or environment flags:
```
--slack-token value Slack token [$SLACK_TOKEN]
--slack-channel value Slack channel id [$SLACK_CHANNEL]
```

## Helm chart

A helm chart is available [here][chart]

[aha-blog]: https://aws.amazon.com/blogs/mt/aws-health-aware-customize-aws-health-alerts-for-organizational-and-personal-aws-accounts/
[health-api]: https://docs.aws.amazon.com/health/latest/ug/health-api.html
[chart]: https://github.com/AndreZiviani/helm-charts/tree/main/charts/aws-health-exporter
63 changes: 63 additions & 0 deletions exporter/aws.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package exporter

import (
"context"
"fmt"
"os"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
)

const (
TermOnDemand string = "JRTCKXETXF"
TermPerHour string = "6YS6EN2CT7"
)

type Pricing struct {
Product Product
ServiceCode string
Terms Terms
}

type Terms struct {
OnDemand map[string]SKU
Reserved map[string]SKU
}
type Product struct {
ProductFamily string
Attributes map[string]string
Sku string
}

type SKU struct {
PriceDimensions map[string]Details
Sku string
EffectiveDate string
OfferTermCode string
TermAttributes string
}

type Details struct {
Unit string
EndRange string
Description string
AppliesTo []string
RateCode string
BeginRange string
PricePerUnit map[string]string
}

func newAWSConfig(ctx context.Context) (aws.Config, error) {
region := os.Getenv("AWS_REGION")
if region == "" {
return aws.Config{}, fmt.Errorf("Please configure the AWS_REGION environment variable")
}

cfg, err := config.LoadDefaultConfig(ctx)
if err != nil {
return aws.Config{}, err
}

return cfg, nil
}
Loading

0 comments on commit 9b3e73b

Please sign in to comment.