From 9b3e73bd22e65d405ee9408c61dad29285a6f20d Mon Sep 17 00:00:00 2001 From: Andre Ziviani Date: Wed, 29 Mar 2023 16:46:07 -0300 Subject: [PATCH] Initial commit --- .github/workflows/release.yml | 74 +++++++++++++ Dockerfile | 18 ++++ Makefile | 96 +++++++++++++++++ README.md | 53 ++++++++- exporter/aws.go | 63 +++++++++++ exporter/health.go | 102 ++++++++++++++++++ exporter/metrics.go | 81 ++++++++++++++ exporter/org.go | 115 ++++++++++++++++++++ exporter/single.go | 82 ++++++++++++++ exporter/types.go | 30 ++++++ go.mod | 67 ++++++++++++ go.sum | 195 ++++++++++++++++++++++++++++++++++ main.go | 77 ++++++++++++++ 13 files changed, 1052 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/release.yml create mode 100644 Dockerfile create mode 100644 Makefile create mode 100644 exporter/aws.go create mode 100644 exporter/health.go create mode 100644 exporter/metrics.go create mode 100644 exporter/org.go create mode 100644 exporter/single.go create mode 100644 exporter/types.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..c636be7 --- /dev/null +++ b/.github/workflows/release.yml @@ -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 }} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..89be564 --- /dev/null +++ b/Dockerfile @@ -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"] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ce3b7ea --- /dev/null +++ b/Makefile @@ -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} + diff --git a/README.md b/README.md index c440c69..0fbd25e 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/exporter/aws.go b/exporter/aws.go new file mode 100644 index 0000000..a6c35e3 --- /dev/null +++ b/exporter/aws.go @@ -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 +} diff --git a/exporter/health.go b/exporter/health.go new file mode 100644 index 0000000..a67f03b --- /dev/null +++ b/exporter/health.go @@ -0,0 +1,102 @@ +package exporter + +import ( + "context" + "fmt" + "os" + "strings" + "time" + + "github.com/aws/aws-sdk-go-v2/service/health" + healthTypes "github.com/aws/aws-sdk-go-v2/service/health/types" + "github.com/slack-go/slack" +) + +func (m *Metrics) HealthOrganizationEnabled(ctx context.Context) bool { + enabled, err := m.health.DescribeHealthServiceStatusForOrganization(ctx, &health.DescribeHealthServiceStatusForOrganizationInput{}) + + if err == nil && *enabled.HealthServiceAccessStatusForOrganization == "ENABLED" { + return true + } + + return false +} + +func (m Metrics) SendSlackNotification(events []HealthEvent) { + tz, err := time.LoadLocation(os.Getenv("TZ")) + if err != nil { + panic(err.Error()) + } + + for _, e := range events { + + resources := m.extractResources(e.AffectedResources) + accounts := m.extractAccounts(e.AffectedAccounts) + + service := *e.Event.Service + region := *e.Event.Region + status := e.Event.StatusCode + + var text, color string + attachmentFields := []slack.AttachmentField{ + {Title: "Account(s)", Value: accounts, Short: true}, + {Title: "Resource(s)", Value: resources, Short: true}, + {Title: "Service", Value: service, Short: true}, + {Title: "Region", Value: region, Short: true}, + {Title: "Start Time", Value: e.Event.StartTime.In(tz).String(), Short: true}, + {Title: "Status", Value: string(status), Short: true}, + {Title: "Event ARN", Value: fmt.Sprintf("`%s`", *e.Event.Arn), Short: false}, + {Title: "Updates", Value: *e.EventDescription.LatestDescription, Short: false}, + } + + if status == healthTypes.EventStatusCodeClosed { + text = fmt.Sprintf(":heavy_check_mark:*[RESOLVED] The AWS Health issue with the %s service in the %s region is now resolved.*", service, region) + color = "18be52" + attachmentFields = append(attachmentFields[:6], attachmentFields[5:]...) + attachmentFields[5] = slack.AttachmentField{Title: "End Time", Value: e.Event.EndTime.In(tz).String(), Short: true} + } else { + text = fmt.Sprintf(":rotating_light:*[NEW] AWS Health reported an issue with the %s service in the %s region.*", service, region) + color = "danger" + } + + attachment := slack.Attachment{ + Color: color, + Fields: attachmentFields, + } + + _, _, err := m.slackApi.PostMessage( + m.slackChannel, + slack.MsgOptionText(text, false), + slack.MsgOptionAttachments(attachment), + ) + if err != nil { + panic(err.Error()) + } + } +} + +func (m Metrics) extractResources(resources []healthTypes.AffectedEntity) string { + if len(resources) > 0 { + var tmp []string + for _, r := range resources { + tmp = append(tmp, *r.EntityValue) + } + + resource := fmt.Sprintf("`%s`", strings.Join(tmp, ",")) + if resource == "UNKNOWN" { + return "All resources in region" + } + + return resource + } + + return "All resources in region" +} + +func (m Metrics) extractAccounts(accounts []string) string { + if len(accounts) > 0 { + return strings.Join(accounts, ",") + } else { + return "All accounts in region" + } +} diff --git a/exporter/metrics.go b/exporter/metrics.go new file mode 100644 index 0000000..f72f5bc --- /dev/null +++ b/exporter/metrics.go @@ -0,0 +1,81 @@ +package exporter + +import ( + "context" + "strings" + "time" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/credentials/stscreds" + "github.com/aws/aws-sdk-go-v2/service/health" + "github.com/aws/aws-sdk-go-v2/service/sts" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/collectors" + "github.com/slack-go/slack" + "github.com/urfave/cli/v2" +) + +const ( + namespace = "aws_health" +) + +func NewMetrics(ctx context.Context, registry *prometheus.Registry, c *cli.Context) (*Metrics, error) { + m := Metrics{} + + m.init(ctx, c) + + registry.MustRegister(&m) + registry.MustRegister(collectors.NewProcessCollector(collectors.ProcessCollectorOpts{})) + registry.MustRegister(collectors.NewGoCollector()) + + return &m, nil +} + +func (m *Metrics) init(ctx context.Context, c *cli.Context) { + cfg, err := newAWSConfig(ctx) + if err != nil { + panic(err.Error()) + } + + cfg.Region = "us-east-1" + + if len(c.String("assume-role")) > 0 { + stsclient := sts.NewFromConfig(cfg) + creds := stscreds.NewAssumeRoleProvider(stsclient, c.String("assume-role")) + cfg.Credentials = aws.NewCredentialsCache(creds) + } + + m.health = health.NewFromConfig(cfg) + m.awsconfig = cfg + + m.lastScrape = time.Now().AddDate(0, 0, -7) + + m.slackToken = c.String("slack-token") + m.slackChannel = c.String("slack-channel") + m.slackApi = slack.New(m.slackToken) + + m.organizationEnabled = m.HealthOrganizationEnabled(ctx) +} + +func (m *Metrics) Describe(ch chan<- *prometheus.Desc) { + prometheus.DescribeByCollect(m, ch) +} + +func (m *Metrics) Collect(ch chan<- prometheus.Metric) { + var events []HealthEvent + if m.organizationEnabled { + events = m.GetOrgEvents() + } else { + events = m.GetEvents() + } + + if len(events) == 0 { + return + } + + m.SendSlackNotification(events) +} + +func sanitizeLabel(label string) string { + return strings.Replace(strings.Replace(label, ".", "_", -1), "/", "_", -1) +} diff --git a/exporter/org.go b/exporter/org.go new file mode 100644 index 0000000..fbbb8ea --- /dev/null +++ b/exporter/org.go @@ -0,0 +1,115 @@ +package exporter + +import ( + "context" + "time" + + "github.com/aws/aws-sdk-go-v2/service/health" + healthTypes "github.com/aws/aws-sdk-go-v2/service/health/types" +) + +func (m *Metrics) GetOrgEvents() []HealthEvent { + ctx := context.TODO() + now := time.Now() + pag := health.NewDescribeEventsForOrganizationPaginator( + m.health, + &health.DescribeEventsForOrganizationInput{ + Filter: &healthTypes.OrganizationEventFilter{ + EndTime: &healthTypes.DateTimeRange{ + From: &m.lastScrape, + To: &now, + }, + }, + }) + + updatedEvents := make([]HealthEvent, 0) + + for pag.HasMorePages() { + events, err := pag.NextPage(ctx) + if err != nil { + panic(err.Error()) + } + + for _, event := range events.Events { + enrichedOrgEvent := m.EnrichOrgEvents(ctx, event) + updatedEvents = append(updatedEvents, enrichedOrgEvent) + } + } + + m.lastScrape = now + + return updatedEvents +} + +func (m *Metrics) EnrichOrgEvents(ctx context.Context, event healthTypes.OrganizationEvent) HealthEvent { + + enrichedEvent := HealthEvent{Arn: event.Arn} + + m.getAffectedAccountsForOrg(ctx, event, &enrichedEvent) + + m.getEventDetailsForOrg(ctx, event, &enrichedEvent) + + m.getAffectedEntitiesForOrg(ctx, event, &enrichedEvent) + + return enrichedEvent +} + +func (m Metrics) getAffectedAccountsForOrg(ctx context.Context, event healthTypes.OrganizationEvent, enrichedEvent *HealthEvent) { + pag := health.NewDescribeAffectedAccountsForOrganizationPaginator( + m.health, + &health.DescribeAffectedAccountsForOrganizationInput{EventArn: event.Arn}) + + for pag.HasMorePages() { + accounts, err := pag.NextPage(ctx) + if err != nil { + panic(err.Error()) + } + + enrichedEvent.EventScope = accounts.EventScopeCode + enrichedEvent.AffectedAccounts = append(enrichedEvent.AffectedAccounts, accounts.AffectedAccounts...) + } +} + +func (m Metrics) getEventDetailsForOrg(ctx context.Context, event healthTypes.OrganizationEvent, enrichedEvent *HealthEvent) { + var accountId *string + if enrichedEvent.EventScope == healthTypes.EventScopeCodeAccountSpecific { + accountId = &enrichedEvent.AffectedAccounts[0] + } + + details, err := m.health.DescribeEventDetailsForOrganization(ctx, &health.DescribeEventDetailsForOrganizationInput{ + OrganizationEventDetailFilters: []healthTypes.EventAccountFilter{{EventArn: event.Arn, AwsAccountId: accountId}}, + }) + if err != nil { + panic(err.Error()) + } + + enrichedEvent.Event = details.SuccessfulSet[0].Event + enrichedEvent.EventDescription = details.SuccessfulSet[0].EventDescription +} + +func (m Metrics) getAffectedEntitiesForOrg(ctx context.Context, event healthTypes.OrganizationEvent, enrichedEvent *HealthEvent) { + var pagResources *health.DescribeAffectedEntitiesForOrganizationPaginator + if len(enrichedEvent.AffectedAccounts) > 0 { + accountFilter := make([]healthTypes.EventAccountFilter, len(enrichedEvent.AffectedAccounts)) + for i, account := range enrichedEvent.AffectedAccounts { + accountFilter[i] = healthTypes.EventAccountFilter{EventArn: event.Arn, AwsAccountId: &account} + } + + pagResources = health.NewDescribeAffectedEntitiesForOrganizationPaginator( + m.health, + &health.DescribeAffectedEntitiesForOrganizationInput{OrganizationEntityFilters: accountFilter}) + } else { + pagResources = health.NewDescribeAffectedEntitiesForOrganizationPaginator( + m.health, + &health.DescribeAffectedEntitiesForOrganizationInput{OrganizationEntityFilters: []healthTypes.EventAccountFilter{{EventArn: event.Arn}}}) + } + + for pagResources.HasMorePages() { + resources, err := pagResources.NextPage(ctx) + if err != nil { + panic(err.Error()) + } + + enrichedEvent.AffectedResources = append(enrichedEvent.AffectedResources, resources.Entities...) + } +} diff --git a/exporter/single.go b/exporter/single.go new file mode 100644 index 0000000..0679c16 --- /dev/null +++ b/exporter/single.go @@ -0,0 +1,82 @@ +package exporter + +import ( + "context" + "time" + + "github.com/aws/aws-sdk-go-v2/service/health" + healthTypes "github.com/aws/aws-sdk-go-v2/service/health/types" +) + +func (m *Metrics) GetEvents() []HealthEvent { + ctx := context.TODO() + now := time.Now() + pag := health.NewDescribeEventsPaginator( + m.health, + &health.DescribeEventsInput{ + Filter: &healthTypes.EventFilter{ + EndTimes: []healthTypes.DateTimeRange{ + { + From: &m.lastScrape, + To: &now, + }, + }, + }, + }) + + updatedEvents := make([]HealthEvent, 0) + + for pag.HasMorePages() { + events, err := pag.NextPage(ctx) + if err != nil { + panic(err.Error()) + } + + for _, event := range events.Events { + enrichedEvent := m.EnrichEvents(ctx, event) + updatedEvents = append(updatedEvents, enrichedEvent) + } + } + + m.lastScrape = now + + return updatedEvents +} + +func (m *Metrics) EnrichEvents(ctx context.Context, event healthTypes.Event) HealthEvent { + + enrichedEvent := HealthEvent{Arn: event.Arn} + + m.getEventDetails(ctx, event, &enrichedEvent) + + m.getAffectedEntities(ctx, event, &enrichedEvent) + + return enrichedEvent +} + +func (m Metrics) getEventDetails(ctx context.Context, event healthTypes.Event, enrichedEvent *HealthEvent) { + details, err := m.health.DescribeEventDetails(ctx, &health.DescribeEventDetailsInput{EventArns: []string{*event.Arn}}) + if err != nil { + panic(err.Error()) + } + + enrichedEvent.Event = details.SuccessfulSet[0].Event + enrichedEvent.EventDescription = details.SuccessfulSet[0].EventDescription +} + +func (m Metrics) getAffectedEntities(ctx context.Context, event healthTypes.Event, enrichedEvent *HealthEvent) { + pagResources := health.NewDescribeAffectedEntitiesPaginator( + m.health, + &health.DescribeAffectedEntitiesInput{Filter: &healthTypes.EntityFilter{EventArns: []string{*event.Arn}}}) + + for pagResources.HasMorePages() { + resources, err := pagResources.NextPage(ctx) + if err != nil { + panic(err.Error()) + } + + enrichedEvent.AffectedResources = append(enrichedEvent.AffectedResources, resources.Entities...) + } + + enrichedEvent.EventScope = event.EventScopeCode +} diff --git a/exporter/types.go b/exporter/types.go new file mode 100644 index 0000000..772d630 --- /dev/null +++ b/exporter/types.go @@ -0,0 +1,30 @@ +package exporter + +import ( + "time" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/health" + healthTypes "github.com/aws/aws-sdk-go-v2/service/health/types" + "github.com/slack-go/slack" +) + +type Metrics struct { + health *health.Client + organizationEnabled bool + awsconfig aws.Config + lastScrape time.Time + + slackApi *slack.Client + slackToken string + slackChannel string +} + +type HealthEvent struct { + Arn *string + AffectedAccounts []string + EventScope healthTypes.EventScopeCode + Event *healthTypes.Event + EventDescription *healthTypes.EventDescription + AffectedResources []healthTypes.AffectedEntity +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..6c38f59 --- /dev/null +++ b/go.mod @@ -0,0 +1,67 @@ +module github.com/AndreZiviani/aws-health-exporter + +go 1.19 + +require ( + github.com/aws/aws-sdk-go-v2 v1.17.7 + github.com/aws/aws-sdk-go-v2/config v1.18.7 + github.com/aws/aws-sdk-go-v2/service/ec2 v1.77.0 + github.com/aws/aws-sdk-go-v2/service/health v1.16.6 + github.com/aws/aws-sdk-go-v2/service/pricing v1.17.5 + github.com/prometheus/client_golang v1.14.0 + github.com/sirupsen/logrus v1.9.0 + github.com/slack-go/slack v0.12.1 + github.com/urfave/cli/v2 v2.25.1 + k8s.io/api v0.26.0 + k8s.io/apimachinery v0.26.0 + k8s.io/client-go v0.26.0 +) + +require ( + github.com/aws/aws-sdk-go-v2/credentials v1.13.7 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.21 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.31 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.25 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.3.28 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.21 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.11.28 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.11 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.17.7 // indirect + github.com/aws/smithy-go v1.13.5 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/go-logr/logr v1.2.3 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/google/go-cmp v0.5.9 // indirect + github.com/google/gofuzz v1.1.0 // indirect + github.com/gorilla/websocket v1.4.2 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/kr/pretty v0.2.0 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/prometheus/client_model v0.3.0 // indirect + github.com/prometheus/common v0.39.0 // indirect + github.com/prometheus/procfs v0.9.0 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect + golang.org/x/net v0.4.0 // indirect + golang.org/x/oauth2 v0.3.0 // indirect + golang.org/x/sys v0.3.0 // indirect + golang.org/x/term v0.3.0 // indirect + golang.org/x/text v0.5.0 // indirect + golang.org/x/time v0.3.0 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/protobuf v1.28.1 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + k8s.io/klog/v2 v2.80.1 // indirect + k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 // indirect + sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..94c2ffd --- /dev/null +++ b/go.sum @@ -0,0 +1,195 @@ +github.com/aws/aws-sdk-go-v2 v1.17.3/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= +github.com/aws/aws-sdk-go-v2 v1.17.7 h1:CLSjnhJSTSogvqUGhIC6LqFKATMRexcxLZ0i/Nzk9Eg= +github.com/aws/aws-sdk-go-v2 v1.17.7/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= +github.com/aws/aws-sdk-go-v2/config v1.18.7 h1:V94lTcix6jouwmAsgQMAEBozVAGJMFhVj+6/++xfe3E= +github.com/aws/aws-sdk-go-v2/config v1.18.7/go.mod h1:OZYsyHFL5PB9UpyS78NElgKs11qI/B5KJau2XOJDXHA= +github.com/aws/aws-sdk-go-v2/credentials v1.13.7 h1:qUUcNS5Z1092XBFT66IJM7mYkMwgZ8fcC8YDIbEwXck= +github.com/aws/aws-sdk-go-v2/credentials v1.13.7/go.mod h1:AdCcbZXHQCjJh6NaH3pFaw8LUeBFn5+88BZGMVGuBT8= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.21 h1:j9wi1kQ8b+e0FBVHxCqCGo4kxDU175hoDHcWAi0sauU= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.21/go.mod h1:ugwW57Z5Z48bpvUyZuaPy4Kv+vEfJWnIrky7RmkBvJg= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.27/go.mod h1:a1/UpzeyBBerajpnP5nGZa9mGzsBn5cOKxm6NWQsvoI= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.31 h1:sJLYcS+eZn5EeNINGHSCRAwUJMFVqklwkH36Vbyai7M= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.31/go.mod h1:QT0BqUvX1Bh2ABdTGnjqEjvjzrCfIniM9Sc8zn9Yndo= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.21/go.mod h1:+Gxn8jYn5k9ebfHEqlhrMirFjSW0v0C9fI+KN5vk2kE= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.25 h1:1mnRASEKnkqsntcxHaysxwgVoUUp5dkiB+l3llKnqyg= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.25/go.mod h1:zBHOPwhBc3FlQjQJE/D3IfPWiWaQmT06Vq9aNukDo0k= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.28 h1:KeTxcGdNnQudb46oOl4d90f2I33DF/c6q3RnZAmvQdQ= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.28/go.mod h1:yRZVr/iT0AqyHeep00SZ4YfBAKojXz08w3XMBscdi0c= +github.com/aws/aws-sdk-go-v2/service/ec2 v1.77.0 h1:m6HYlpZlTWb9vHuuRHpWRieqPHWlS0mvQ90OJNrG/Nk= +github.com/aws/aws-sdk-go-v2/service/ec2 v1.77.0/go.mod h1:mV0E7631M1eXdB+tlGFIw6JxfsC7Pz7+7Aw15oLVhZw= +github.com/aws/aws-sdk-go-v2/service/health v1.16.6 h1:qwbNYWGgcfqlBb6ZNbB2pBDiaJepiDHzzE832uuGuGY= +github.com/aws/aws-sdk-go-v2/service/health v1.16.6/go.mod h1:q1i79Qt6zxIFVzQQrnrGrn7xl1CMRrXX7foTuuKXSn8= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.21 h1:5C6XgTViSb0bunmU57b3CT+MhxULqHH2721FVA+/kDM= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.21/go.mod h1:lRToEJsn+DRA9lW4O9L9+/3hjTkUzlzyzHqn8MTds5k= +github.com/aws/aws-sdk-go-v2/service/pricing v1.17.5 h1:89yKwg+Kn3jgjcpxzmbZYH0O+I2+HjEcILIQrLkj8ik= +github.com/aws/aws-sdk-go-v2/service/pricing v1.17.5/go.mod h1:1YtXjD073MNbQvowCxfSsdhGUCJQOt04FVDcs8uYCmI= +github.com/aws/aws-sdk-go-v2/service/sso v1.11.28 h1:gItLq3zBYyRDPmqAClgzTH8PBjDQGeyptYGHIwtYYNA= +github.com/aws/aws-sdk-go-v2/service/sso v1.11.28/go.mod h1:wo/B7uUm/7zw/dWhBJ4FXuw1sySU5lyIhVg1Bu2yL9A= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.11 h1:KCacyVSs/wlcPGx37hcbT3IGYO8P8Jx+TgSDhAXtQMY= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.11/go.mod h1:TZSH7xLO7+phDtViY/KUp9WGCJMQkLJ/VpgkTFd5gh8= +github.com/aws/aws-sdk-go-v2/service/sts v1.17.7 h1:9Mtq1KM6nD8/+HStvWcvYnixJ5N85DX+P+OY3kI3W2k= +github.com/aws/aws-sdk-go-v2/service/sts v1.17.7/go.mod h1:+lGbb3+1ugwKrNTWcf2RT05Xmp543B06zDFTwiTLp7I= +github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8= +github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= +github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= +github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA= +github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng= +github.com/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho= +github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= +github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= +github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= +github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= +github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/common v0.39.0 h1:oOyhkDq05hPZKItWVBkJ6g6AtGxi+fy7F4JvUV8uhsI= +github.com/prometheus/common v0.39.0/go.mod h1:6XBZ7lYdLCbkAVhwRsWTZn+IN5AB9F/NXd5w0BbEX0Y= +github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= +github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/slack-go/slack v0.12.1 h1:X97b9g2hnITDtNsNe5GkGx6O2/Sz/uC20ejRZN6QxOw= +github.com/slack-go/slack v0.12.1/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/urfave/cli/v2 v2.25.1 h1:zw8dSP7ghX0Gmm8vugrs6q9Ku0wzweqPyshy+syu9Gw= +github.com/urfave/cli/v2 v2.25.1/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU= +golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/oauth2 v0.3.0 h1:6l90koy8/LaBLmLu8jpHeHexzMwEita0zFfYlggy2F8= +golang.org/x/oauth2 v0.3.0/go.mod h1:rQrIauxkUhJ6CuwEXwymO2/eh4xz2ZWF1nBkcxS+tGk= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI= +golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +k8s.io/api v0.26.0 h1:IpPlZnxBpV1xl7TGk/X6lFtpgjgntCg8PJ+qrPHAC7I= +k8s.io/api v0.26.0/go.mod h1:k6HDTaIFC8yn1i6pSClSqIwLABIcLV9l5Q4EcngKnQg= +k8s.io/apimachinery v0.26.0 h1:1feANjElT7MvPqp0JT6F3Ss6TWDwmcjLypwoPpEf7zg= +k8s.io/apimachinery v0.26.0/go.mod h1:tnPmbONNJ7ByJNz9+n9kMjNP8ON+1qoAIIC70lztu74= +k8s.io/client-go v0.26.0 h1:lT1D3OfO+wIi9UFolCrifbjUUgu7CpLca0AD8ghRLI8= +k8s.io/client-go v0.26.0/go.mod h1:I2Sh57A79EQsDmn7F7ASpmru1cceh3ocVT9KlX2jEZg= +k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4= +k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 h1:+70TFaan3hfJzs+7VK2o+OGxg8HsuBr/5f6tVAjDu6E= +k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 h1:KTgPnR10d5zhztWptI952TNtt/4u5h3IzDXkdIMuo2Y= +k8s.io/utils v0.0.0-20221128185143-99ec85e7a448/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= +sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/main.go b/main.go new file mode 100644 index 0000000..3acdc2b --- /dev/null +++ b/main.go @@ -0,0 +1,77 @@ +package main + +import ( + "context" + "fmt" + "net/http" + "os" + + "github.com/AndreZiviani/aws-health-exporter/exporter" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" + log "github.com/sirupsen/logrus" + "github.com/urfave/cli/v2" +) + +func main() { + flags := []cli.Flag{ + &cli.StringFlag{Name: "listen-address", Aliases: []string{"l"}, Usage: "The address to listen on for HTTP requests.", Value: ":8080"}, + &cli.StringFlag{Name: "metrics-path", Aliases: []string{"m"}, Usage: "Metrics endpoint path", Value: "/metrics"}, + &cli.StringFlag{Name: "regions", Aliases: []string{"r"}, Usage: "Comma separated list of AWS regions to monitor", Value: "all-regions"}, + &cli.StringFlag{Name: "log-level", Aliases: []string{"v"}, Usage: "Log level", Value: "info"}, + &cli.StringFlag{Name: "slack-token", Usage: "Slack token", EnvVars: []string{"SLACK_TOKEN"}, Required: true}, + &cli.StringFlag{Name: "slack-channel", Usage: "Slack channel id", EnvVars: []string{"SLACK_CHANNEL"}, Required: true}, + &cli.StringFlag{Name: "assume-role", Usage: "Assume another AWS IAM role", EnvVars: []string{"ASSUME_ROLE"}}, + } + + app := &cli.App{ + Flags: flags, + Name: "aws-health-exporter", + Action: func(c *cli.Context) error { + parsedLevel, err := log.ParseLevel(c.String("log-level")) + if err != nil { + log.WithError(err).Warnf("Couldn't parse log level, using default: %s", log.GetLevel()) + } else { + log.SetLevel(parsedLevel) + log.Debugf("Set log level to %s", parsedLevel) + } + + log.Infof("Starting AWS Health Exporter. [log-level=%s]", c.String("log-level")) + + ctx := context.TODO() + + registry := prometheus.NewRegistry() + + _, err = exporter.NewMetrics(ctx, registry, c) + if err != nil { + log.Fatal(err) + } + + log.Infof("Starting metric http endpoint [address=%s, path=%s, regions=%s]", c.String("listen-address"), c.String("metrics-path"), c.String("regions")) + http.Handle(c.String("metrics-path"), promhttp.HandlerFor(registry, promhttp.HandlerOpts{})) + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + w.Write([]byte(` + AWS Health Exporter + +

AWS Health Exporter

+

Metrics

+ + + `)) + + }) + log.Fatal(http.ListenAndServe(c.String("listen-address"), nil)) + + return nil + }, + } + + err := app.Run(os.Args) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + return +}