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

AWS cost explorer API integration #1247

Merged
merged 8 commits into from
Dec 11, 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
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ require (
github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.30.2
github.com/aws/aws-sdk-go-v2/service/codedeploy v1.20.3
github.com/aws/aws-sdk-go-v2/service/configservice v1.41.2
github.com/aws/aws-sdk-go-v2/service/costexplorer v1.32.4
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.25.2
github.com/aws/aws-sdk-go-v2/service/ec2 v1.136.0
github.com/aws/aws-sdk-go-v2/service/ecr v1.23.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ github.com/aws/aws-sdk-go-v2/service/codedeploy v1.20.3 h1:rGqIKTmugpZ7lEzXTmbiP
github.com/aws/aws-sdk-go-v2/service/codedeploy v1.20.3/go.mod h1:A7i1lQClkFz09enKv5WYKb8a2lf9QeeI1s9dNiym3hg=
github.com/aws/aws-sdk-go-v2/service/configservice v1.41.2 h1:WJt83aWld986AxwJpzE0eDqQi18a/PwZ36y7DqENYdk=
github.com/aws/aws-sdk-go-v2/service/configservice v1.41.2/go.mod h1:wIuYBSC8G7HHXK/T6YO0t/m463ssur9aMLnycNvKXqQ=
github.com/aws/aws-sdk-go-v2/service/costexplorer v1.32.4 h1:ojxirFFJN39ar+tHiz84PuaeKA/Z3BiopdhxOGGQD4A=
github.com/aws/aws-sdk-go-v2/service/costexplorer v1.32.4/go.mod h1:1ujrFMokNtwDv3fwb9RBwdeXS+RonpIeV9uh19GJoH8=
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.25.2 h1:O6ff5PwwgQ7QkL/XA0H+0U0mWwjkYaP9tHvbr0Ptqak=
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.25.2/go.mod h1:kuVxCbsxbP/h6YTT2BfOj4s/bwXYsG3C/8Qn9gO5QJY=
github.com/aws/aws-sdk-go-v2/service/ec2 v1.136.0 h1:nZPVFkGojUUJupKJzaCKE07LaFDO3Tto1U69F8JipsI=
Expand Down
46 changes: 44 additions & 2 deletions providers/aws/aws.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ package aws
import (
"context"
"strings"
"time"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/costexplorer"
"github.com/aws/aws-sdk-go-v2/service/costexplorer/types"
log "github.com/sirupsen/logrus"

"github.com/tailwarden/komiser/models"
"github.com/tailwarden/komiser/providers"
"github.com/tailwarden/komiser/providers/aws/apigateway"
Expand Down Expand Up @@ -34,7 +37,9 @@ import (
"github.com/tailwarden/komiser/providers/aws/sns"
"github.com/tailwarden/komiser/providers/aws/sqs"
"github.com/tailwarden/komiser/providers/aws/systemsmanager"
awsUtils "github.com/tailwarden/komiser/providers/aws/utils"
"github.com/tailwarden/komiser/utils"

"github.com/uptrace/bun"
)

Expand Down Expand Up @@ -113,6 +118,43 @@ func FetchResources(ctx context.Context, client providers.ProviderClient, region
listOfSupportedRegions = regions
}

costexplorerClient := costexplorer.NewFromConfig(*client.AWSClient)
costexplorerOutputList := []*costexplorer.GetCostAndUsageOutput{}
var nextPageToken *string
for {
costexplorerOutput, err := costexplorerClient.GetCostAndUsage(ctx, &costexplorer.GetCostAndUsageInput{
Granularity: "DAILY",
Azanul marked this conversation as resolved.
Show resolved Hide resolved
Metrics: []string{"UnblendedCost"},
TimePeriod: &types.DateInterval{
Start: aws.String(utils.BeginningOfMonth(time.Now()).Format("2006-01-02")),
End: aws.String(time.Now().Format("2006-01-02")),
},
GroupBy: []types.GroupDefinition{
{
Key: aws.String("SERVICE"),
Type: "DIMENSION",
},
{
Key: aws.String("REGION"),
Type: "DIMENSION",
},
},
NextPageToken: nextPageToken,
})
if err != nil {
log.Warn("Couldn't fetch cost and usage data:", err)
break
}

costexplorerOutputList = append(costexplorerOutputList, costexplorerOutput)

if aws.ToString(costexplorerOutput.NextPageToken) == "" {
break
}

nextPageToken = costexplorerOutput.NextPageToken
}
ctxWithCostexplorerOutput := context.WithValue(ctx, awsUtils.CostexplorerKey, costexplorerOutputList)
for _, region := range listOfSupportedRegions {
c := client.AWSClient.Copy()
c.Region = region
Expand All @@ -123,7 +165,7 @@ func FetchResources(ctx context.Context, client providers.ProviderClient, region
for _, fetchResources := range listOfSupportedServices() {
fetchResources := fetchResources
wp.SubmitTask(func() {
resources, err := fetchResources(ctx, client)
resources, err := fetchResources(ctxWithCostexplorerOutput, client)
if err != nil {
log.Warnf("[%s][AWS] %s", client.Name, err)
} else {
Expand Down
8 changes: 7 additions & 1 deletion providers/aws/lambda/functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ func Functions(ctx context.Context, client providers.ProviderClient) ([]models.R
cloudwatchClient := cloudwatch.NewFromConfig(*client.AWSClient)
lambdaClient := lambda.NewFromConfig(*client.AWSClient)

serviceCost, err := awsUtils.GetCostAndUsage(ctx, client.AWSClient.Region, "Lambda")
if err != nil {
log.Warnln("Couldn't fetch Lambda cost and usage:", err)
}

tempRegion := client.AWSClient.Region
client.AWSClient.Region = "us-east-1"
pricingClient := pricing.NewFromConfig(*client.AWSClient)
Expand Down Expand Up @@ -157,7 +162,8 @@ func Functions(ctx context.Context, client providers.ProviderClient) ([]models.R
Name: *o.FunctionName,
Cost: monthlyCost,
Metadata: map[string]string{
"runtime": string(o.Runtime),
"runtime": string(o.Runtime),
"serviceCost": fmt.Sprint(serviceCost),
},
Relations: relations,
FetchedAt: time.Now(),
Expand Down
38 changes: 34 additions & 4 deletions providers/aws/utils/utils.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,43 @@
package utils

import (
"context"
"encoding/json"
"fmt"
"strconv"

"github.com/aws/aws-sdk-go-v2/service/costexplorer"
"github.com/aws/aws-sdk-go-v2/service/pricing"
)

type AWSCtxKey uint8

const (
CostexplorerKey AWSCtxKey = iota
)

func GetCostAndUsage(ctx context.Context, region string, svcName string) (float64, error) {
total := 0.0
costexplorerOutputList, ok := ctx.Value(CostexplorerKey).([]*costexplorer.GetCostAndUsageOutput)
if !ok || costexplorerOutputList == nil {
return 0, fmt.Errorf("incorrect costexplorerOutputList")
}
for _, costexplorerOutput := range costexplorerOutputList {
for _, group := range costexplorerOutput.ResultsByTime {
for _, v := range group.Groups {
if v.Keys[0] == svcName && v.Keys[1] == region {
amt, err := strconv.ParseFloat(*v.Metrics["UnblendedCost"].Amount, 64)
if err != nil {
return 0, err
}
total += amt
}
}
}
}
return total, nil
}

type ProductEntry struct {
Product struct {
Attributes struct {
Expand Down Expand Up @@ -93,8 +123,8 @@ func GetPriceMap(pricingOutput *pricing.GetProductsOutput, field string) (map[st
}

func Int64PtrToFloat64(i *int64) float64 {
if i == nil {
return 0.0 // or any default value you prefer
}
return float64(*i)
if i == nil {
return 0.0 // or any default value you prefer
}
return float64(*i)
}
Loading