diff --git a/dashboard/components/modal/Modal.tsx b/dashboard/components/modal/Modal.tsx
index b6acd73d0..341ef4815 100644
--- a/dashboard/components/modal/Modal.tsx
+++ b/dashboard/components/modal/Modal.tsx
@@ -29,7 +29,7 @@ function Modal({ isOpen, closeModal, id, children }: ModalProps) {
-
{filteredCloudAccounts.map(account => (
trust.json
```
- Make sure to substitute ${NAMESPACE} for the namespace you will deploy the helm chart in. If deployed in any other namespace, you will see sts:AssumeRoleWithWebIdentity failure messages in the pod logs.
+ Make sure to substitute `${NAMESPACE}` for the namespace you will deploy the helm chart in. If deployed in any other namespace, you will see sts:AssumeRoleWithWebIdentity failure messages in the pod logs.
1. Run the modified code block from the previous step to create a file named *`trust.json`*\.
diff --git a/providers/aws/apigateway/apis.go b/providers/aws/apigateway/apis.go
index 305d1277d..8a379d516 100644
--- a/providers/aws/apigateway/apis.go
+++ b/providers/aws/apigateway/apis.go
@@ -13,6 +13,7 @@ import (
"github.com/aws/aws-sdk-go-v2/service/cloudwatch/types"
. "github.com/tailwarden/komiser/models"
. "github.com/tailwarden/komiser/providers"
+ awsUtils "github.com/tailwarden/komiser/providers/aws/utils"
"github.com/tailwarden/komiser/utils"
)
@@ -27,6 +28,11 @@ func Apis(ctx context.Context, client ProviderClient) ([]Resource, error) {
return resources, err
}
+ serviceCost, err := awsUtils.GetCostAndUsage(ctx, client.AWSClient.Region, "Amazon API Gateway")
+ if err != nil {
+ log.Warnln("Couldn't fetch Amazon API Gateway cost and usage:", err)
+ }
+
for _, api := range output.Items {
tags := make([]Tag, 0)
for key, value := range api.Tags {
@@ -72,6 +78,9 @@ func Apis(ctx context.Context, client ProviderClient) ([]Resource, error) {
Region: client.AWSClient.Region,
Name: *api.Name,
Cost: monthlyCost,
+ Metadata: map[string]string{
+ "serviceCost": fmt.Sprint(serviceCost),
+ },
Tags: tags,
CreatedAt: *api.CreatedDate,
FetchedAt: time.Now(),
diff --git a/providers/aws/cloudfront/distributions.go b/providers/aws/cloudfront/distributions.go
index 92c56ce38..c46b93d90 100644
--- a/providers/aws/cloudfront/distributions.go
+++ b/providers/aws/cloudfront/distributions.go
@@ -13,6 +13,7 @@ import (
"github.com/aws/aws-sdk-go-v2/service/cloudwatch/types"
. "github.com/tailwarden/komiser/models"
. "github.com/tailwarden/komiser/providers"
+ awsUtils "github.com/tailwarden/komiser/providers/aws/utils"
"github.com/tailwarden/komiser/utils"
)
@@ -26,6 +27,10 @@ func Distributions(ctx context.Context, client ProviderClient) ([]Resource, erro
cloudwatchClient := cloudwatch.NewFromConfig(*client.AWSClient)
client.AWSClient.Region = tempRegion
+ serviceCost, err := awsUtils.GetCostAndUsage(ctx, client.AWSClient.Region, "Amazon CloudFront")
+ if err != nil {
+ log.Warnln("Couldn't fetch Amazon CloudFront cost and usage:", err)
+ }
for {
output, err := cloudfrontClient.ListDistributions(ctx, &config)
if err != nil {
@@ -145,6 +150,9 @@ func Distributions(ctx context.Context, client ProviderClient) ([]Resource, erro
Region: client.AWSClient.Region,
Name: *distribution.DomainName,
Cost: monthlyCost,
+ Metadata: map[string]string{
+ "serviceCost": fmt.Sprint(serviceCost),
+ },
Tags: tags,
FetchedAt: time.Now(),
Link: fmt.Sprintf("https://%s.console.aws.amazon.com/cloudfront/v3/home?region=%s#/distributions/%s", client.AWSClient.Region, client.AWSClient.Region, *distribution.Id),
diff --git a/providers/aws/cloudwatch/dashboards.go b/providers/aws/cloudwatch/dashboards.go
index 1c53545a3..74d028986 100644
--- a/providers/aws/cloudwatch/dashboards.go
+++ b/providers/aws/cloudwatch/dashboards.go
@@ -10,6 +10,7 @@ import (
"github.com/aws/aws-sdk-go-v2/service/cloudwatch"
. "github.com/tailwarden/komiser/models"
. "github.com/tailwarden/komiser/providers"
+ awsUtils "github.com/tailwarden/komiser/providers/aws/utils"
)
func Dashboards(ctx context.Context, client ProviderClient) ([]Resource, error) {
@@ -17,6 +18,10 @@ func Dashboards(ctx context.Context, client ProviderClient) ([]Resource, error)
cloudWatchClient := cloudwatch.NewFromConfig(*client.AWSClient)
var nextToken *string
+ serviceCost, err := awsUtils.GetCostAndUsage(ctx, client.AWSClient.Region, "AmazonCloudWatch")
+ if err != nil {
+ log.Warnln("Couldn't fetch AmazonCloudWatch cost and usage:", err)
+ }
for {
input := &cloudwatch.ListDashboardsInput{
@@ -53,6 +58,9 @@ func Dashboards(ctx context.Context, client ProviderClient) ([]Resource, error)
Region: client.AWSClient.Region,
Name: *dashboard.DashboardName,
Cost: cost,
+ Metadata: map[string]string{
+ "serviceCost": fmt.Sprint(serviceCost),
+ },
Tags: tags,
FetchedAt: time.Now(),
Link: fmt.Sprintf("https://%s.console.aws.amazon.com/cloudwatch/home?region=%s#dashboards:name=%s", client.AWSClient.Region, client.AWSClient.Region, *dashboard.DashboardName),
diff --git a/providers/aws/codebuild/projects.go b/providers/aws/codebuild/projects.go
index 14d8d584e..98e45bc4a 100644
--- a/providers/aws/codebuild/projects.go
+++ b/providers/aws/codebuild/projects.go
@@ -13,6 +13,7 @@ import (
"github.com/aws/aws-sdk-go-v2/service/sts"
"github.com/tailwarden/komiser/models"
"github.com/tailwarden/komiser/providers"
+ awsUtils "github.com/tailwarden/komiser/providers/aws/utils"
)
func BuildProjects(ctx context.Context, client providers.ProviderClient) ([]models.Resource, error) {
@@ -25,6 +26,10 @@ func BuildProjects(ctx context.Context, client providers.ProviderClient) ([]mode
return resources, err
}
+ serviceCost, err := awsUtils.GetCostAndUsage(ctx, client.AWSClient.Region, "CodeBuild")
+ if err != nil {
+ log.Warnln("Couldn't fetch CodeBuild cost and usage:", err)
+ }
accountId := stsOutput.Account
for {
@@ -44,6 +49,9 @@ func BuildProjects(ctx context.Context, client providers.ProviderClient) ([]mode
ResourceId: resourceArn,
Region: client.AWSClient.Region,
Name: project,
+ Metadata: map[string]string{
+ "serviceCost": fmt.Sprint(serviceCost),
+ },
Tags: tags,
FetchedAt: time.Now(),
Link: fmt.Sprintf("https://%s.console.aws.amazon.com/codesuite/codebuild/%s/projects/%s/details?region=%s", client.AWSClient.Region, *accountId, project, client.AWSClient.Region),
diff --git a/providers/aws/codedeploy/deployment_groups.go b/providers/aws/codedeploy/deployment_groups.go
index af214b5e7..a4ae8a9ee 100644
--- a/providers/aws/codedeploy/deployment_groups.go
+++ b/providers/aws/codedeploy/deployment_groups.go
@@ -3,13 +3,15 @@ package codedeploy
import (
"context"
"fmt"
+ "time"
+
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/codedeploy"
"github.com/aws/aws-sdk-go-v2/service/sts"
log "github.com/sirupsen/logrus"
"github.com/tailwarden/komiser/models"
"github.com/tailwarden/komiser/providers"
- "time"
+ awsUtils "github.com/tailwarden/komiser/providers/aws/utils"
)
func DeploymentGroups(ctx context.Context, client providers.ProviderClient) ([]models.Resource, error) {
@@ -23,6 +25,11 @@ func DeploymentGroups(ctx context.Context, client providers.ProviderClient) ([]m
}
accountId := stsOutput.Account
+ serviceCost, err := awsUtils.GetCostAndUsage(ctx, client.AWSClient.Region, "CodeDeploy")
+ if err != nil {
+ log.Warnln("Couldn't fetch CodeDeploy cost and usage:", err)
+ }
+
for {
output, err := codedeployClient.ListApplications(ctx, &listApplicationParams)
if err != nil {
@@ -47,6 +54,9 @@ func DeploymentGroups(ctx context.Context, client providers.ProviderClient) ([]m
ResourceId: resourceArn,
Region: client.AWSClient.Region,
Name: deploymentGroup,
+ Metadata: map[string]string{
+ "serviceCost": fmt.Sprint(serviceCost),
+ },
Tags: tags,
FetchedAt: time.Now(),
})
diff --git a/providers/aws/dynamodb/tables.go b/providers/aws/dynamodb/tables.go
index b94a528b5..c1891e4f1 100644
--- a/providers/aws/dynamodb/tables.go
+++ b/providers/aws/dynamodb/tables.go
@@ -12,9 +12,9 @@ import (
"github.com/aws/aws-sdk-go-v2/service/pricing"
"github.com/aws/aws-sdk-go-v2/service/pricing/types"
"github.com/aws/aws-sdk-go-v2/service/sts"
- awsUtils "github.com/tailwarden/komiser/providers/aws/utils"
. "github.com/tailwarden/komiser/models"
. "github.com/tailwarden/komiser/providers"
+ awsUtils "github.com/tailwarden/komiser/providers/aws/utils"
)
@@ -30,6 +30,10 @@ func Tables(ctx context.Context, client ProviderClient) ([]Resource, error) {
client.AWSClient.Region = "us-east-1"
pricingClient := pricing.NewFromConfig(*client.AWSClient)
client.AWSClient.Region = oldRegion
+ serviceCost, err := awsUtils.GetCostAndUsage(ctx, client.AWSClient.Region, "Amazon DynamoDB")
+ if err != nil {
+ log.Warnln("Couldn't fetch Amazon DynamoDB cost and usage:", err)
+ }
pricingOutput, err := pricingClient.GetProducts(ctx, &pricing.GetProductsInput{
ServiceCode: aws.String("AmazonDynamoDB"),
@@ -110,6 +114,9 @@ func Tables(ctx context.Context, client ProviderClient) ([]Resource, error) {
Region: client.AWSClient.Region,
Name: table,
Cost: monthlyCost,
+ Metadata: map[string]string{
+ "serviceCost": fmt.Sprint(serviceCost),
+ },
Tags: tags,
FetchedAt: time.Now(),
Link: fmt.Sprintf("https://%s.console.aws.amazon.com/dynamodbv2/home?region=%s#table?initialTagKey=&name=%s", client.AWSClient.Region, client.AWSClient.Region, table),
diff --git a/providers/aws/ec2/instances.go b/providers/aws/ec2/instances.go
index 6efb0c11a..384c36e9b 100644
--- a/providers/aws/ec2/instances.go
+++ b/providers/aws/ec2/instances.go
@@ -17,6 +17,7 @@ import (
"github.com/aws/aws-sdk-go-v2/service/sts"
"github.com/tailwarden/komiser/models"
"github.com/tailwarden/komiser/providers"
+ awsUtils "github.com/tailwarden/komiser/providers/aws/utils"
"github.com/tailwarden/komiser/utils"
)
@@ -33,6 +34,11 @@ func Instances(ctx context.Context, client providers.ProviderClient) ([]models.R
accountId := stsOutput.Account
+ serviceCost, err := awsUtils.GetCostAndUsage(ctx, client.AWSClient.Region, "EC2")
+ if err != nil {
+ log.Warnln("Couldn't fetch EC2 cost and usage:", err)
+ }
+
oldRegion := client.AWSClient.Region
client.AWSClient.Region = "us-east-1"
pricingClient := pricing.NewFromConfig(*client.AWSClient)
@@ -151,6 +157,7 @@ func Instances(ctx context.Context, client providers.ProviderClient) ([]models.R
Metadata: map[string]string{
"instanceType": string(instance.InstanceType),
"state": string(instance.State.Name),
+ "serviceCost": fmt.Sprint(serviceCost),
},
Link: fmt.Sprintf("https://%s.console.aws.amazon.com/ec2/home?region=%s#InstanceDetails:instanceId=%s", client.AWSClient.Region, client.AWSClient.Region, *instance.InstanceId),
})
diff --git a/providers/aws/ecr/repositories.go b/providers/aws/ecr/repositories.go
index 095c4b0a6..549fdc4e9 100644
--- a/providers/aws/ecr/repositories.go
+++ b/providers/aws/ecr/repositories.go
@@ -11,12 +11,17 @@ import (
"github.com/aws/aws-sdk-go-v2/service/ecr"
. "github.com/tailwarden/komiser/models"
. "github.com/tailwarden/komiser/providers"
+ awsUtils "github.com/tailwarden/komiser/providers/aws/utils"
)
func Repositories(ctx context.Context, client ProviderClient) ([]Resource, error) {
resources := make([]Resource, 0)
var config ecr.DescribeRepositoriesInput
ecrClient := ecr.NewFromConfig(*client.AWSClient)
+ serviceCost, err := awsUtils.GetCostAndUsage(ctx, client.AWSClient.Region, "ECR")
+ if err != nil {
+ log.Warnln("Couldn't fetch ECR cost and usage:", err)
+ }
for {
output, err := ecrClient.DescribeRepositories(context.Background(), &config)
if err != nil {
@@ -47,6 +52,9 @@ func Repositories(ctx context.Context, client ProviderClient) ([]Resource, error
Region: client.AWSClient.Region,
Name: *repository.RepositoryName,
Cost: 0.10,
+ Metadata: map[string]string{
+ "serviceCost": fmt.Sprint(serviceCost),
+ },
Tags: tags,
FetchedAt: time.Now(),
Link: fmt.Sprintf("https://%s.console.aws.amazon.com/ecr/repositories/%s", client.AWSClient.Region, *repository.RepositoryName),
diff --git a/providers/aws/ecs/containers.go b/providers/aws/ecs/containers.go
index 23e63bd38..5c59df0ef 100644
--- a/providers/aws/ecs/containers.go
+++ b/providers/aws/ecs/containers.go
@@ -12,6 +12,7 @@ import (
"github.com/aws/aws-sdk-go-v2/service/sts"
. "github.com/tailwarden/komiser/models"
. "github.com/tailwarden/komiser/providers"
+ awsUtils "github.com/tailwarden/komiser/providers/aws/utils"
)
func ContainerInstances(ctx context.Context, client ProviderClient) ([]Resource, error) {
@@ -26,6 +27,10 @@ func ContainerInstances(ctx context.Context, client ProviderClient) ([]Resource,
return resources, err
}
+ serviceCost, err := awsUtils.GetCostAndUsage(ctx, client.AWSClient.Region, "ECS")
+ if err != nil {
+ log.Warnln("Couldn't fetch ECS cost and usage:", err)
+ }
accountId := stsOutput.Account
for {
output, err := ecsContainer.ListContainerInstances(context.Background(), &config)
@@ -44,6 +49,9 @@ func ContainerInstances(ctx context.Context, client ProviderClient) ([]Resource,
Region: client.AWSClient.Region,
Name: containerInstance,
Cost: 0,
+ Metadata: map[string]string{
+ "serviceCost": fmt.Sprint(serviceCost),
+ },
FetchedAt: time.Now(),
Link: fmt.Sprintf("https://%s.console.aws.amazon.com/ecs/home?#/containers/%s", client.AWSClient.Region, containerInstance),
})
diff --git a/providers/aws/efs/efs.go b/providers/aws/efs/efs.go
index 2c1d8e484..4b27b4770 100644
--- a/providers/aws/efs/efs.go
+++ b/providers/aws/efs/efs.go
@@ -12,6 +12,7 @@ import (
"github.com/aws/aws-sdk-go-v2/service/sts"
. "github.com/tailwarden/komiser/models"
. "github.com/tailwarden/komiser/providers"
+ awsUtils "github.com/tailwarden/komiser/providers/aws/utils"
)
func ElasticFileStorage(ctx context.Context, client ProviderClient) ([]Resource, error) {
@@ -27,6 +28,11 @@ func ElasticFileStorage(ctx context.Context, client ProviderClient) ([]Resource,
accountId := stsOutput.Account
+ serviceCost, err := awsUtils.GetCostAndUsage(ctx, client.AWSClient.Region, "EFS")
+ if err != nil {
+ log.Warnln("Couldn't fetch EFS cost and usage:", err)
+ }
+
for {
output, err := efsClient.DescribeFileSystems(ctx, &config)
if err != nil {
@@ -62,6 +68,9 @@ func ElasticFileStorage(ctx context.Context, client ProviderClient) ([]Resource,
Region: client.AWSClient.Region,
Name: *filesystem.Name,
Cost: monthlyCost,
+ Metadata: map[string]string{
+ "serviceCost": fmt.Sprint(serviceCost),
+ },
Tags: tags,
FetchedAt: time.Now(),
Link: fmt.Sprintf("https://%s.console.aws.amazon.com/efs/home?region=%s#/file-systems/%s", client.AWSClient.Region, client.AWSClient.Region, *filesystem.Name),
diff --git a/providers/aws/eks/clusters.go b/providers/aws/eks/clusters.go
index 2b9707522..65dbd76ae 100644
--- a/providers/aws/eks/clusters.go
+++ b/providers/aws/eks/clusters.go
@@ -12,6 +12,7 @@ import (
"github.com/aws/aws-sdk-go-v2/service/sts"
. "github.com/tailwarden/komiser/models"
. "github.com/tailwarden/komiser/providers"
+ awsUtils "github.com/tailwarden/komiser/providers/aws/utils"
"github.com/tailwarden/komiser/utils"
)
@@ -28,6 +29,11 @@ func KubernetesClusters(ctx context.Context, client ProviderClient) ([]Resource,
accountId := stsOutput.Account
+ serviceCost, err := awsUtils.GetCostAndUsage(ctx, client.AWSClient.Region, "EKS")
+ if err != nil {
+ log.Warnln("Couldn't fetch EKS cost and usage:", err)
+ }
+
for {
output, err := eksClient.ListClusters(ctx, &config)
if err != nil {
@@ -96,6 +102,7 @@ func KubernetesClusters(ctx context.Context, client ProviderClient) ([]Resource,
"region": client.AWSClient.Region,
"service": "EKS",
"resources": len(resources),
+ "serviceCost":fmt.Sprint(serviceCost),
}).Info("Fetched resources")
return resources, nil
}
diff --git a/providers/aws/elasticache/clusters.go b/providers/aws/elasticache/clusters.go
index de2be2361..43beb6e05 100644
--- a/providers/aws/elasticache/clusters.go
+++ b/providers/aws/elasticache/clusters.go
@@ -16,6 +16,7 @@ import (
"github.com/tailwarden/komiser/models"
. "github.com/tailwarden/komiser/models"
. "github.com/tailwarden/komiser/providers"
+ awsUtils "github.com/tailwarden/komiser/providers/aws/utils"
"github.com/tailwarden/komiser/utils"
)
@@ -26,6 +27,11 @@ func Clusters(ctx context.Context, client ProviderClient) ([]Resource, error) {
pricingClient := pricing.NewFromConfig(*client.AWSClient)
+ serviceCost, err := awsUtils.GetCostAndUsage(ctx, client.AWSClient.Region, "Amazon ElastiCache")
+ if err != nil {
+ log.Warnln("Couldn't fetch Amazon ElastiCache cost and usage:", err)
+ }
+
for {
output, err := elasticacheClient.DescribeCacheClusters(ctx, &config)
if err != nil {
@@ -122,6 +128,7 @@ func Clusters(ctx context.Context, client ProviderClient) ([]Resource, error) {
"nodeType": *cluster.CacheNodeType,
"status": *cluster.CacheClusterStatus,
"clusterId": *cluster.CacheClusterId,
+ "serviceCost": fmt.Sprint(serviceCost),
},
FetchedAt: time.Now(),
Link: fmt.Sprintf("https:/%s.console.aws.amazon.com/elasticache/home?region=%s#/%s/%s", client.AWSClient.Region, client.AWSClient.Region, *cluster.Engine, *cluster.CacheClusterId),
diff --git a/providers/aws/elb/loadbalancers.go b/providers/aws/elb/loadbalancers.go
index 3aea90989..9aeea1a22 100644
--- a/providers/aws/elb/loadbalancers.go
+++ b/providers/aws/elb/loadbalancers.go
@@ -10,6 +10,7 @@ import (
"github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2"
. "github.com/tailwarden/komiser/models"
. "github.com/tailwarden/komiser/providers"
+ awsUtils "github.com/tailwarden/komiser/providers/aws/utils"
"github.com/tailwarden/komiser/utils"
)
@@ -38,6 +39,11 @@ func LoadBalancers(ctx context.Context, client ProviderClient) ([]Resource, erro
var configListeners elasticloadbalancingv2.DescribeListenersInput
var configRules elasticloadbalancingv2.DescribeRulesInput
+ serviceCost, err := awsUtils.GetCostAndUsage(ctx, client.AWSClient.Region, "ELB")
+ if err != nil {
+ log.Warnln("Couldn't fetch ELB cost and usage:", err)
+ }
+
for _, loadbalancer := range output.LoadBalancers {
resourceArn := *loadbalancer.LoadBalancerArn
resourceType := string(loadbalancer.Type)
@@ -76,6 +82,9 @@ func LoadBalancers(ctx context.Context, client ProviderClient) ([]Resource, erro
Region: client.AWSClient.Region,
Name: *loadbalancer.LoadBalancerName,
Cost: monthlyCost,
+ Metadata: map[string]string{
+ "serviceCost": fmt.Sprint(serviceCost),
+ },
Tags: tags,
CreatedAt: *loadbalancer.CreatedTime,
FetchedAt: time.Now(),
diff --git a/providers/aws/iam/users.go b/providers/aws/iam/users.go
index 5df62ab1d..a7b26e0ac 100644
--- a/providers/aws/iam/users.go
+++ b/providers/aws/iam/users.go
@@ -10,6 +10,7 @@ import (
log "github.com/sirupsen/logrus"
"github.com/tailwarden/komiser/models"
"github.com/tailwarden/komiser/providers"
+ awsUtils "github.com/tailwarden/komiser/providers/aws/utils"
)
const (
@@ -25,6 +26,11 @@ func Users(ctx context.Context, client providers.ProviderClient) ([]models.Resou
paginator := iam.NewListUsersPaginator(iamClient, &iam.ListUsersInput{})
+ serviceCost, err := awsUtils.GetCostAndUsage(ctx, client.AWSClient.Region, "IAM")
+ if err != nil {
+ log.Warnln("Couldn't fetch IAM cost and usage:", err)
+ }
+
for paginator.HasMorePages() {
output, err := paginator.NextPage(ctx)
if err != nil {
@@ -49,6 +55,9 @@ func Users(ctx context.Context, client providers.ProviderClient) ([]models.Resou
Region: client.AWSClient.Region,
Name: aws.ToString(o.UserName),
Cost: 0,
+ Metadata: map[string]string{
+ "serviceCost": fmt.Sprint(serviceCost),
+ },
CreatedAt: *o.CreateDate,
Tags: tags,
FetchedAt: time.Now(),
diff --git a/providers/aws/kinesis/streams.go b/providers/aws/kinesis/streams.go
index 7c49672a1..fdd2ca618 100644
--- a/providers/aws/kinesis/streams.go
+++ b/providers/aws/kinesis/streams.go
@@ -47,6 +47,10 @@ func Streams(ctx context.Context, client ProviderClient) ([]Resource, error) {
return resources, err
}
+ serviceCost, err := awsUtils.GetCostAndUsage(ctx, client.AWSClient.Region, "Amazon Kinesis")
+ if err != nil {
+ log.Warnln("Couldn't fetch Amazon Kinesis cost and usage:", err)
+ }
var config kinesis.ListStreamsInput
for {
output, err := kinesisClient.ListStreams(ctx, &config)
@@ -88,6 +92,9 @@ func Streams(ctx context.Context, client ProviderClient) ([]Resource, error) {
Region: client.AWSClient.Region,
Name: *stream.StreamName,
Cost: cost,
+ Metadata: map[string]string{
+ "serviceCost": fmt.Sprint(serviceCost),
+ },
CreatedAt: *stream.StreamCreationTimestamp,
FetchedAt: time.Now(),
Tags: tags,
diff --git a/providers/aws/kms/keys.go b/providers/aws/kms/keys.go
index 2292a6b26..a552b4fe1 100644
--- a/providers/aws/kms/keys.go
+++ b/providers/aws/kms/keys.go
@@ -12,12 +12,17 @@ import (
"github.com/aws/aws-sdk-go-v2/service/kms/types"
. "github.com/tailwarden/komiser/models"
. "github.com/tailwarden/komiser/providers"
+ awsUtils "github.com/tailwarden/komiser/providers/aws/utils"
)
func Keys(ctx context.Context, client ProviderClient) ([]Resource, error) {
var config kms.ListKeysInput
resources := make([]Resource, 0)
kmsClient := kms.NewFromConfig(*client.AWSClient)
+ serviceCost, err := awsUtils.GetCostAndUsage(ctx, client.AWSClient.Region, "AWS Key Management Service")
+ if err != nil {
+ log.Warnln("Couldn't fetch AWS Key Management Service cost and usage:", err)
+ }
for {
output, err := kmsClient.ListKeys(ctx, &config)
@@ -62,6 +67,9 @@ func Keys(ctx context.Context, client ProviderClient) ([]Resource, error) {
Region: client.AWSClient.Region,
ResourceId: *key.KeyArn,
Cost: monthlyCost,
+ Metadata: map[string]string{
+ "serviceCost": fmt.Sprint(serviceCost),
+ },
Name: *key.KeyId,
FetchedAt: time.Now(),
Tags: tags,
diff --git a/providers/aws/lambda/functions.go b/providers/aws/lambda/functions.go
index 7ab13e6e8..ceaca2f34 100644
--- a/providers/aws/lambda/functions.go
+++ b/providers/aws/lambda/functions.go
@@ -32,9 +32,9 @@ 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")
+ serviceCost, err := awsUtils.GetCostAndUsage(ctx, client.AWSClient.Region, "AWS Lambda")
if err != nil {
- log.Warnln("Couldn't fetch Lambda cost and usage:", err)
+ log.Warnln("Couldn't fetch AWS Lambda cost and usage:", err)
}
tempRegion := client.AWSClient.Region
diff --git a/providers/aws/opensearch/service_domains.go b/providers/aws/opensearch/service_domains.go
index d95f3f41a..4e74d29b3 100644
--- a/providers/aws/opensearch/service_domains.go
+++ b/providers/aws/opensearch/service_domains.go
@@ -11,6 +11,7 @@ import (
"github.com/aws/aws-sdk-go-v2/service/opensearch"
. "github.com/tailwarden/komiser/models"
. "github.com/tailwarden/komiser/providers"
+ awsUtils "github.com/tailwarden/komiser/providers/aws/utils"
)
func ServiceDomains(ctx context.Context, client ProviderClient) ([]Resource, error) {
@@ -23,6 +24,11 @@ func ServiceDomains(ctx context.Context, client ProviderClient) ([]Resource, err
return resources, err
}
+ serviceCost, err := awsUtils.GetCostAndUsage(ctx, client.AWSClient.Region, "OpenSearch")
+ if err != nil {
+ log.Warnln("Couldn't fetch OpenSearch cost and usage:", err)
+ }
+
for _, domainName := range output.DomainNames {
domainConfig, err := openSearchClient.DescribeDomain(ctx, &opensearch.DescribeDomainInput{
DomainName: domainName.DomainName,
@@ -40,6 +46,9 @@ func ServiceDomains(ctx context.Context, client ProviderClient) ([]Resource, err
Region: client.AWSClient.Region,
Name: aws.ToString(domain.DomainName),
Cost: 0,
+ Metadata: map[string]string{
+ "serviceCost": fmt.Sprint(serviceCost),
+ },
FetchedAt: time.Now(),
Link: fmt.Sprintf("https://%s.console.aws.amazon.com/aos/home?region=%s#/opensearch/domains/%s", client.AWSClient.Region, client.AWSClient.Region, aws.ToString(domain.DomainName)),
})
diff --git a/providers/aws/rds/instances.go b/providers/aws/rds/instances.go
index f2b74bd46..9a30ee538 100644
--- a/providers/aws/rds/instances.go
+++ b/providers/aws/rds/instances.go
@@ -15,6 +15,7 @@ import (
"github.com/aws/aws-sdk-go-v2/service/rds"
"github.com/tailwarden/komiser/models"
"github.com/tailwarden/komiser/providers"
+ awsUtils "github.com/tailwarden/komiser/providers/aws/utils"
"github.com/tailwarden/komiser/utils"
)
@@ -27,6 +28,10 @@ func Instances(ctx context.Context, client providers.ProviderClient) ([]models.R
client.AWSClient.Region = "us-east-1"
pricingClient := pricing.NewFromConfig(*client.AWSClient)
client.AWSClient.Region = oldRegion
+ serviceCost, err := awsUtils.GetCostAndUsage(ctx, client.AWSClient.Region, "Amazon Relational Database Service")
+ if err != nil {
+ log.Warnln("Couldn't fetch Amazon Relational Database Service cost and usage:", err)
+ }
for {
output, err := rdsClient.DescribeDBInstances(ctx, &config)
@@ -116,6 +121,9 @@ func Instances(ctx context.Context, client providers.ProviderClient) ([]models.R
Region: client.AWSClient.Region,
ResourceId: *instance.DBInstanceArn,
Cost: monthlyCost,
+ Metadata: map[string]string{
+ "serviceCost": fmt.Sprint(serviceCost),
+ },
Name: _instanceName,
FetchedAt: time.Now(),
Tags: tags,
diff --git a/providers/aws/redshift/eventsubscription.go b/providers/aws/redshift/eventsubscription.go
index d2b488d8c..b4b52317b 100644
--- a/providers/aws/redshift/eventsubscription.go
+++ b/providers/aws/redshift/eventsubscription.go
@@ -12,6 +12,7 @@ import (
"github.com/aws/aws-sdk-go-v2/service/sts"
. "github.com/tailwarden/komiser/models"
. "github.com/tailwarden/komiser/providers"
+ awsUtils "github.com/tailwarden/komiser/providers/aws/utils"
)
func EventSubscriptions(ctx context.Context, client ProviderClient) ([]Resource, error) {
@@ -27,6 +28,11 @@ func EventSubscriptions(ctx context.Context, client ProviderClient) ([]Resource,
accountId := stsOutput.Account
+ serviceCost, err := awsUtils.GetCostAndUsage(ctx, client.AWSClient.Region, "Redshift")
+ if err != nil {
+ log.Warnln("Couldn't fetch Redshift cost and usage:", err)
+ }
+
for {
output, err := redshiftClient.DescribeEventSubscriptions(ctx, &config)
if err != nil {
@@ -60,6 +66,9 @@ func EventSubscriptions(ctx context.Context, client ProviderClient) ([]Resource,
Region: client.AWSClient.Region,
Name: *eventSubscription.CustSubscriptionId,
Cost: monthlyCost,
+ Metadata: map[string]string{
+ "serviceCost": fmt.Sprint(serviceCost),
+ },
Tags: tags,
FetchedAt: time.Now(),
Link: fmt.Sprintf("https://%s.console.aws.amaxon.com/redshift/home?region=%s/event-subscriptions/%s", client.AWSClient.Region, client.AWSClient.Region, *eventSubscription.CustSubscriptionId),
diff --git a/providers/aws/s3/buckets.go b/providers/aws/s3/buckets.go
index b4d6e700e..59179954e 100644
--- a/providers/aws/s3/buckets.go
+++ b/providers/aws/s3/buckets.go
@@ -33,6 +33,10 @@ func Buckets(ctx context.Context, client ProviderClient) ([]Resource, error) {
client.AWSClient.Region = "us-east-1"
pricingClient := pricing.NewFromConfig(*client.AWSClient)
client.AWSClient.Region = tempRegion
+ serviceCost, err := awsUtils.GetCostAndUsage(ctx, client.AWSClient.Region, "S3")
+ if err != nil {
+ log.Warnln("Couldn't fetch S3 cost and usage:", err)
+ }
pricingOutput, err := pricingClient.GetProducts(ctx, &pricing.GetProductsInput{
ServiceCode: aws.String("AmazonS3"),
@@ -143,6 +147,9 @@ func Buckets(ctx context.Context, client ProviderClient) ([]Resource, error) {
ResourceId: resourceArn,
Name: *bucket.Name,
Cost: monthlyCost,
+ Metadata: map[string]string{
+ "serviceCost": fmt.Sprint(serviceCost),
+ },
CreatedAt: *bucket.CreationDate,
Tags: tags,
FetchedAt: time.Now(),
diff --git a/providers/aws/servicecatalog/applications.go b/providers/aws/servicecatalog/applications.go
index 96b72060d..c878b5bed 100644
--- a/providers/aws/servicecatalog/applications.go
+++ b/providers/aws/servicecatalog/applications.go
@@ -11,6 +11,7 @@ import (
"github.com/aws/aws-sdk-go-v2/service/servicecatalog"
"github.com/tailwarden/komiser/models"
"github.com/tailwarden/komiser/providers"
+ awsUtils "github.com/tailwarden/komiser/providers/aws/utils"
)
func Products(ctx context.Context, client providers.ProviderClient) ([]models.Resource, error) {
@@ -20,6 +21,11 @@ func Products(ctx context.Context, client providers.ProviderClient) ([]models.Re
input := &servicecatalog.SearchProductsAsAdminInput{}
+ serviceCost, err := awsUtils.GetCostAndUsage(ctx, client.AWSClient.Region, "ServiceCatalog")
+ if err != nil {
+ log.Warnln("Couldn't fetch ServiceCatalog cost and usage:", err)
+ }
+
for {
output, err := serviceCatalogsClient.SearchProductsAsAdmin(ctx, input)
if err != nil {
@@ -35,6 +41,9 @@ func Products(ctx context.Context, client providers.ProviderClient) ([]models.Re
Region: client.AWSClient.Region,
Name: aws.ToString(product.ProductViewSummary.Name),
Cost: 0,
+ Metadata: map[string]string{
+ "serviceCost": fmt.Sprint(serviceCost),
+ },
FetchedAt: time.Now(),
Link: fmt.Sprintf("https://%s.console.aws.amazon.com/servicecatalog/home?region=%s#/admin-products/%s", client.AWSClient.Region, client.AWSClient.Region, aws.ToString(product.ProductViewSummary.ProductId)),
})
diff --git a/providers/aws/sns/topics.go b/providers/aws/sns/topics.go
index 2aa3880e1..37ef57d1a 100644
--- a/providers/aws/sns/topics.go
+++ b/providers/aws/sns/topics.go
@@ -15,6 +15,7 @@ import (
"github.com/aws/aws-sdk-go-v2/service/sts"
. "github.com/tailwarden/komiser/models"
. "github.com/tailwarden/komiser/providers"
+ awsUtils "github.com/tailwarden/komiser/providers/aws/utils"
"github.com/tailwarden/komiser/utils"
)
@@ -30,6 +31,11 @@ func Topics(ctx context.Context, client ProviderClient) ([]Resource, error) {
if err != nil {
return resources, err
}
+
+ serviceCost, err := awsUtils.GetCostAndUsage(ctx, client.AWSClient.Region, "Amazon Simple Notification Service")
+ if err != nil {
+ log.Warnln("Couldn't fetch Amazon Simple Notification Service cost and usage:", err)
+ }
accountId := stsOutput.Account
@@ -94,6 +100,9 @@ func Topics(ctx context.Context, client ProviderClient) ([]Resource, error) {
Region: client.AWSClient.Region,
Name: *topic.TopicArn,
Cost: monthlyCost,
+ Metadata: map[string]string{
+ "serviceCost": fmt.Sprint(serviceCost),
+ },
Tags: tags,
FetchedAt: time.Now(),
Link: fmt.Sprintf("https://%s.console.aws.amazon.com/sns/v3/home?region=%s#/topic/%s", client.AWSClient.Region, client.AWSClient.Region, *topic.TopicArn),
diff --git a/providers/aws/sqs/queues.go b/providers/aws/sqs/queues.go
index cee9d06cb..70430e771 100644
--- a/providers/aws/sqs/queues.go
+++ b/providers/aws/sqs/queues.go
@@ -14,6 +14,7 @@ import (
"github.com/aws/aws-sdk-go-v2/service/sqs"
. "github.com/tailwarden/komiser/models"
. "github.com/tailwarden/komiser/providers"
+ awsUtils "github.com/tailwarden/komiser/providers/aws/utils"
"github.com/tailwarden/komiser/utils"
)
@@ -24,6 +25,10 @@ func Queues(ctx context.Context, client ProviderClient) ([]Resource, error) {
var config sqs.ListQueuesInput
sqsClient := sqs.NewFromConfig(*client.AWSClient)
+ serviceCost, err := awsUtils.GetCostAndUsage(ctx, client.AWSClient.Region, "Amazon Simple Queue Service")
+ if err != nil {
+ log.Warnln("Couldn't fetch Amazon Simple Queue Service cost and usage:", err)
+ }
for {
output, err := sqsClient.ListQueues(context.Background(), &config)
if err != nil {
@@ -140,6 +145,9 @@ func Queues(ctx context.Context, client ProviderClient) ([]Resource, error) {
Region: client.AWSClient.Region,
Name: queueName,
Cost: monthlyCost,
+ Metadata: map[string]string{
+ "serviceCost": fmt.Sprint(serviceCost),
+ },
Tags: tags,
FetchedAt: time.Now(),
Link: fmt.Sprintf("https://%s.console.aws.amazon.com/sqs/v2/home?region=%s#/queues/%s", client.AWSClient.Region, client.AWSClient.Region, queue),
diff --git a/providers/aws/systemsmanager/maintenance_window.go b/providers/aws/systemsmanager/maintenance_window.go
index bf87745aa..201235c01 100644
--- a/providers/aws/systemsmanager/maintenance_window.go
+++ b/providers/aws/systemsmanager/maintenance_window.go
@@ -11,6 +11,7 @@ import (
"github.com/aws/aws-sdk-go-v2/service/ssm"
"github.com/tailwarden/komiser/models"
"github.com/tailwarden/komiser/providers"
+ awsUtils "github.com/tailwarden/komiser/providers/aws/utils"
)
func MaintenanceWindows(ctx context.Context, client providers.ProviderClient) ([]models.Resource, error) {
@@ -19,6 +20,11 @@ func MaintenanceWindows(ctx context.Context, client providers.ProviderClient) ([
input := &ssm.DescribeMaintenanceWindowsInput{}
+ serviceCost, err := awsUtils.GetCostAndUsage(ctx, client.AWSClient.Region, "SystemsManager")
+ if err != nil {
+ log.Warnln("Couldn't fetch SystemsManager cost and usage:", err)
+ }
+
for {
maintenanceWindows, err := ssmClient.DescribeMaintenanceWindows(ctx, input)
if err != nil {
@@ -39,6 +45,7 @@ func MaintenanceWindows(ctx context.Context, client providers.ProviderClient) ([
Link: fmt.Sprintf("https://%s.console.aws.amazon.com/systems-manager/maintenance-windows/%s/details", client.AWSClient.Region, aws.ToString(window.WindowId)),
Metadata: map[string]string{
"Description": aws.ToString(window.Description),
+ "serviceCost": fmt.Sprint(serviceCost),
},
})
}
diff --git a/providers/gcp/redis/instances.go b/providers/gcp/redis/instances.go
index 3af7c20d8..65b115c28 100644
--- a/providers/gcp/redis/instances.go
+++ b/providers/gcp/redis/instances.go
@@ -2,6 +2,7 @@ package redis
import (
"context"
+
"fmt"
"regexp"
"time"
@@ -18,7 +19,14 @@ import (
"github.com/tailwarden/komiser/utils"
)
+
+
func Instances(ctx context.Context, client providers.ProviderClient) ([]models.Resource, error) {
+ pricing, err := FetchPricing()
+ if err != nil {
+ return nil, err
+ }
+
resources := make([]models.Resource, 0)
regions, err := utils.FetchGCPRegionsInRealtime(client.GCPClient.Credentials.ProjectID, option.WithCredentials(client.GCPClient.Credentials))
@@ -57,6 +65,8 @@ RegionsLoop:
re := regexp.MustCompile(`instances\/(.+)$`)
redisInstanceName := re.FindStringSubmatch(redis.Name)[1]
+ cost := calculateRedisCost(redis, pricing)
+
resources = append(resources, models.Resource{
Provider: "GCP",
Account: client.Name,
@@ -65,7 +75,7 @@ RegionsLoop:
Name: redis.DisplayName,
Region: regionName,
CreatedAt: redis.CreateTime.AsTime(),
- Cost: 0,
+ Cost: cost,
FetchedAt: time.Now(),
Link: fmt.Sprintf("https://console.cloud.google.com/memorystore/redis/locations/%s/instances/%s/details/overview?project=%s", regionName, redisInstanceName, client.GCPClient.Credentials.ProjectID),
})
diff --git a/providers/gcp/redis/pricing.go b/providers/gcp/redis/pricing.go
new file mode 100644
index 000000000..8d8cd99c4
--- /dev/null
+++ b/providers/gcp/redis/pricing.go
@@ -0,0 +1,112 @@
+package redis
+
+import (
+ "encoding/json"
+ "fmt"
+ "math"
+ "net/http"
+ "time"
+
+ "cloud.google.com/go/redis/apiv1/redispb"
+)
+
+const (
+ M1GbLimit = 4
+ M2GbLimit = 10
+ M3GbLimit = 35
+ M4GbLimit = 100
+)
+
+type RedisPrice []struct {
+ Val int `json:"val"`
+ Currency string `json:"currnecy"`
+ Nanos float64 `json:"nanos"`
+}
+
+type RegionBasedPricing struct {
+ Regions map[string]struct {
+ Price RedisPrice `json:"price"`
+ } `json:"regions"`
+}
+
+type GcpDatabasePricing struct {
+ Gcp struct {
+ Databases struct {
+ CloudMemorystore struct {
+ Redis struct {
+ Basic map[string]RegionBasedPricing `json:"basic"`
+ Standard map[string]RegionBasedPricing `json:"standard"`
+ } `json:"redis"`
+ } `json:"cloud_memorystore"`
+ } `json:"databases"`
+ } `json:"gcp"`
+}
+
+func FetchPricing() (*GcpDatabasePricing, error) {
+ res, err := http.Get("https://www.gstatic.com/cloud-site-ux/pricing/data/gcp-databases.json")
+ if err != nil {
+ return nil, err
+ }
+
+ var pricing GcpDatabasePricing
+ err = json.NewDecoder(res.Body).Decode(&pricing)
+ if err != nil {
+ return nil, err
+ }
+
+ return &pricing, nil
+}
+
+func calculateRedisCost(redis *redispb.Instance, pricing *GcpDatabasePricing) float64 {
+ var priceMap map[string]RegionBasedPricing
+ var priceKey string
+
+ prices := []int32{M1GbLimit, M2GbLimit, M3GbLimit, M4GbLimit}
+ capacityTier := getCapacityTier(redis.MemorySizeGb, prices)
+
+ if redis.Tier == redispb.Instance_BASIC {
+ priceMap = pricing.Gcp.Databases.CloudMemorystore.Redis.Basic
+ priceKey = fmt.Sprintf("Rediscapacitybasicm%ddefault", capacityTier)
+ } else if redis.Tier == redispb.Instance_STANDARD_HA {
+ priceMap = pricing.Gcp.Databases.CloudMemorystore.Redis.Standard
+ if redis.ReadReplicasMode == redispb.Instance_READ_REPLICAS_DISABLED {
+ priceKey = fmt.Sprintf("Rediscapacitystandardm%ddefault", capacityTier)
+ } else {
+ priceKey = fmt.Sprintf("Rediscapacitystandardnodem%d", capacityTier)
+ }
+ }
+
+ pricePerHrPerGbInNanos := priceMap[priceKey].Regions[redis.LocationId].Price[0].Nanos
+ pricePerHrPerGbInDollars := pricePerHrPerGbInNanos / math.Pow(10, 9)
+
+ now := time.Now().UTC()
+ startTime := getStartTime(redis.GetCreateTime().AsTime(), now)
+
+ hours := now.Sub(startTime).Hours()
+
+ cost := hours * pricePerHrPerGbInDollars
+
+ if redis.ReadReplicasMode == redispb.Instance_READ_REPLICAS_ENABLED {
+ cost *= float64(redis.ReplicaCount)
+ }
+
+ return cost
+}
+
+func getStartTime(createTime, now time.Time) time.Time {
+ firstOfCurrentMonth := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, time.UTC)
+ if createTime.After(firstOfCurrentMonth) {
+ return createTime
+ }
+ return firstOfCurrentMonth
+}
+
+func getCapacityTier(memorySizeGb int32, prices []int32) int {
+ capacityTier := 5
+ for idx, price := range prices {
+ if memorySizeGb <= price {
+ capacityTier = idx + 1
+ }
+ }
+ return capacityTier
+}