Skip to content

Commit

Permalink
fixed cost of cloudfront
Browse files Browse the repository at this point in the history
Signed-off-by: bishal7679 <[email protected]>
  • Loading branch information
bishal7679 committed Oct 11, 2023
1 parent 334e804 commit e2c34bd
Showing 1 changed file with 138 additions and 10 deletions.
148 changes: 138 additions & 10 deletions providers/aws/cloudfront/distributions.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,24 @@ import (
"github.com/aws/aws-sdk-go-v2/service/cloudfront"
"github.com/aws/aws-sdk-go-v2/service/cloudwatch"
"github.com/aws/aws-sdk-go-v2/service/cloudwatch/types"
"github.com/aws/aws-sdk-go-v2/service/pricing"
pricingTypes "github.com/aws/aws-sdk-go-v2/service/pricing/types"
. "github.com/tailwarden/komiser/models"
. "github.com/tailwarden/komiser/providers"
awsUtils "github.com/tailwarden/komiser/providers/aws/utils"
"github.com/tailwarden/komiser/utils"
)

const (
freeTierRequests = 10000000
freeTierFunctionInvocation = 2000000
freeTierUpload = 1099511627776
)

func ConvertBytesToTerabytes(bytes int64) float64 {
return float64(bytes) / 1099511627776
}

func Distributions(ctx context.Context, client ProviderClient) ([]Resource, error) {
resources := make([]Resource, 0)
var config cloudfront.ListDistributionsInput
Expand All @@ -25,6 +38,28 @@ func Distributions(ctx context.Context, client ProviderClient) ([]Resource, erro
client.AWSClient.Region = "us-east-1"
cloudwatchClient := cloudwatch.NewFromConfig(*client.AWSClient)
client.AWSClient.Region = tempRegion
pricingClient := pricing.NewFromConfig(*client.AWSClient)

pricingOutput, err := pricingClient.GetProducts(ctx, &pricing.GetProductsInput{
ServiceCode: aws.String("AmazonCloudFront"),
Filters: []pricingTypes.Filter{
{
Field: aws.String("regionCode"),
Value: aws.String(client.AWSClient.Region),
Type: pricingTypes.FilterTypeTermMatch,
},
},
})
if err != nil {
log.Errorf("ERROR: Couldn't fetch pricing info for AWS CloudFront: %v", err)
return resources, err
}

priceMap, err := awsUtils.GetPriceMap(pricingOutput)
if err != nil {
log.Errorf("ERROR: Failed to calculate cost per month: %v", err)
return resources, err
}

for {
output, err := cloudfrontClient.ListDistributions(ctx, &config)
Expand All @@ -39,7 +74,7 @@ func Distributions(ctx context.Context, client ProviderClient) ([]Resource, erro
MetricName: aws.String("BytesDownloaded"),
Namespace: aws.String("AWS/CloudFront"),
Dimensions: []types.Dimension{
types.Dimension{
{
Name: aws.String("DistributionId"),
Value: distribution.Id,
},
Expand All @@ -59,13 +94,15 @@ func Distributions(ctx context.Context, client ProviderClient) ([]Resource, erro
bytesDownloaded = *metricsBytesDownloadedOutput.Datapoints[0].Sum
}

sizeInTBDownload := ConvertBytesToTerabytes(int64(bytesDownloaded))

metricsBytesUploadedOutput, err := cloudwatchClient.GetMetricStatistics(ctx, &cloudwatch.GetMetricStatisticsInput{
StartTime: aws.Time(utils.BeginningOfMonth(time.Now())),
EndTime: aws.Time(time.Now()),
MetricName: aws.String("BytesUploaded"),
Namespace: aws.String("AWS/CloudFront"),
Dimensions: []types.Dimension{
types.Dimension{
{
Name: aws.String("DistributionId"),
Value: distribution.Id,
},
Expand All @@ -84,14 +121,19 @@ func Distributions(ctx context.Context, client ProviderClient) ([]Resource, erro
if metricsBytesUploadedOutput != nil && len(metricsBytesUploadedOutput.Datapoints) > 0 {
bytesUploaded = *metricsBytesUploadedOutput.Datapoints[0].Sum
}
if bytesUploaded > freeTierUpload {
bytesUploaded -= freeTierUpload
}

sizeInTBUpload := ConvertBytesToTerabytes(int64(bytesUploaded))

metricsRequestsOutput, err := cloudwatchClient.GetMetricStatistics(ctx, &cloudwatch.GetMetricStatisticsInput{
StartTime: aws.Time(utils.BeginningOfMonth(time.Now())),
EndTime: aws.Time(time.Now()),
MetricName: aws.String("Requests"),
Namespace: aws.String("AWS/CloudFront"),
Dimensions: []types.Dimension{
types.Dimension{
{
Name: aws.String("DistributionId"),
Value: distribution.Id,
},
Expand All @@ -110,17 +152,103 @@ func Distributions(ctx context.Context, client ProviderClient) ([]Resource, erro
if metricsRequestsOutput != nil && len(metricsRequestsOutput.Datapoints) > 0 {
requests = *metricsRequestsOutput.Datapoints[0].Sum
}
if requests > freeTierRequests {
requests -= freeTierRequests
}

metricsLambdaEdgeDurationOutput, err := cloudwatchClient.GetMetricStatistics(ctx, &cloudwatch.GetMetricStatisticsInput{
StartTime: aws.Time(utils.BeginningOfMonth(time.Now())),
EndTime: aws.Time(time.Now()),
MetricName: aws.String("Duration"),
Namespace: aws.String("AWS/LambdaEdge"),
Dimensions: []types.Dimension{
{
Name: aws.String("DistributionId"),
Value: distribution.Id,
},
},
Period: aws.Int32(86400),
Statistics: []types.Statistic{
types.StatisticAverage,
},
})

if err != nil {
log.Warnf("Couldn't fetch Lambda@Edge Duration metric for %s", *distribution.Id)
}

lambdaEdgeDuration := 0.0
if metricsLambdaEdgeDurationOutput != nil && len(metricsLambdaEdgeDurationOutput.Datapoints) > 0 {
lambdaEdgeDuration = *metricsLambdaEdgeDurationOutput.Datapoints[0].Average
}

metricsLambdaEdgeRequestsOutput, err := cloudwatchClient.GetMetricStatistics(ctx, &cloudwatch.GetMetricStatisticsInput{
StartTime: aws.Time(utils.BeginningOfMonth(time.Now())),
EndTime: aws.Time(time.Now()),
MetricName: aws.String("Requests"),
Namespace: aws.String("AWS/LambdaEdge"),
Dimensions: []types.Dimension{
{
Name: aws.String("DistributionId"),
Value: distribution.Id,
},
},
Period: aws.Int32(86400),
Statistics: []types.Statistic{
types.StatisticSum,
},
})

if err != nil {
log.Warnf("Couldn't fetch Lambda@Edge Requests metric for %s", *distribution.Id)
}

lambdaEdgeRequests := 0.0
if metricsLambdaEdgeRequestsOutput != nil && len(metricsLambdaEdgeRequestsOutput.Datapoints) > 0 {
lambdaEdgeRequests = *metricsLambdaEdgeRequestsOutput.Datapoints[0].Sum
}

metricsFunctionInvocationsOutput, err := cloudwatchClient.GetMetricStatistics(ctx, &cloudwatch.GetMetricStatisticsInput{
StartTime: aws.Time(utils.BeginningOfMonth(time.Now())),
EndTime: aws.Time(time.Now()),
MetricName: aws.String("FunctionInvocations"),
Namespace: aws.String("AWS/CloudFront"),
Dimensions: []types.Dimension{
{
Name: aws.String("DistributionId"),
Value: distribution.Id,
},
},
Period: aws.Int32(3600),
Statistics: []types.Statistic{
types.StatisticSum,
},
})
if err != nil {
log.Warnf("Couldn't fetch Function Invocations metric for %s", *distribution.Id)
}

functionInvocation := 0.0
if metricsFunctionInvocationsOutput != nil && len(metricsFunctionInvocationsOutput.Datapoints) > 0 {
functionInvocation = *metricsFunctionInvocationsOutput.Datapoints[0].Sum
}
if functionInvocation > freeTierFunctionInvocation {
functionInvocation -= freeTierFunctionInvocation
}

dataTransferToInternetCost := awsUtils.GetCost(priceMap["CloudFront-DataTransfer-In-Bytes"], sizeInTBUpload*1024)

dataTransferToOriginCost := awsUtils.GetCost(priceMap["CloudFront-DataTransfer-Out-Bytes"], sizeInTBDownload*1024)

requestsCost := awsUtils.GetCost(priceMap["CloudFront-Requests"], requests/10000)

// calculate region data transfer out to internet
dataTransferToInternet := (bytesUploaded / 1000000000) * 0.085
lambdaEdgeDurationCost := awsUtils.GetCost(priceMap["AWS-Lambda-Edge-Duration"], lambdaEdgeDuration)

// calculate region data transfer out to origin
dataTransferToOrigin := (bytesDownloaded / 1000000000) * 0.02
lambdaEdgeRequestsCost := awsUtils.GetCost(priceMap["AWS-Lambda-Edge-Requests"], lambdaEdgeRequests/10000000)

// calculate requests cost
requestsCost := requests * 0.000001
functionInvocationsCost := awsUtils.GetCost(priceMap["AWS-CloudFront-FunctionInvocation"], functionInvocation)

monthlyCost := dataTransferToInternet + dataTransferToOrigin + requestsCost
monthlyCost := dataTransferToInternetCost + dataTransferToOriginCost + requestsCost + lambdaEdgeDurationCost + lambdaEdgeRequestsCost + functionInvocationsCost

outputTags, err := cloudfrontClient.ListTagsForResource(ctx, &cloudfront.ListTagsForResourceInput{
Resource: distribution.ARN,
Expand Down

0 comments on commit e2c34bd

Please sign in to comment.