-
Notifications
You must be signed in to change notification settings - Fork 9
/
explorer.go
118 lines (112 loc) · 3.69 KB
/
explorer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
package cliaws
import (
"context"
"fmt"
"regexp"
"strings"
"time"
"github.com/alexflint/go-arg"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/costexplorer"
"github.com/nathants/libaws/lib"
)
func init() {
lib.Commands["cost-explorer"] = costExplorer
lib.Args["cost-explorer"] = costExplorerArgs{}
}
type costExplorerArgs struct {
AccountNum string `arg:"-a,--account,required"`
Region string `arg:"-r,--region"`
DaysAgoStart int `arg:"-d,--days-ago-start" default:"7"`
Daily bool `arg:"--daily" help:"use daily instead of hourly granularity"`
}
func (costExplorerArgs) Description() string {
return "\ncost explorer, caching data locally by hour since the api is very expensive\n"
}
func costExplorer() {
var args costExplorerArgs
arg.MustParse(&args)
ctx := context.Background()
start := time.Now().UTC().Add(time.Duration(args.DaysAgoStart) * -1 * 24 * time.Hour).Truncate(time.Hour)
end := time.Now().UTC()
filter := &costexplorer.Expression{
Dimensions: &costexplorer.DimensionValues{
Key: aws.String(costexplorer.DimensionLinkedAccount),
Values: []*string{aws.String(args.AccountNum)},
},
}
if args.Region != "" {
filter = &costexplorer.Expression{
And: []*costexplorer.Expression{
{
Dimensions: &costexplorer.DimensionValues{
Key: aws.String(costexplorer.DimensionLinkedAccount),
Values: []*string{aws.String(args.AccountNum)},
},
},
{
Dimensions: &costexplorer.DimensionValues{
Key: aws.String(costexplorer.DimensionRegion),
Values: []*string{aws.String(args.Region)},
},
},
},
}
}
var results []*costexplorer.ResultByTime
var token *string
granularity := aws.String(costexplorer.GranularityHourly)
formatDate := func(s string) string { return s }
if args.Daily {
granularity = aws.String(costexplorer.GranularityDaily)
formatDate = func(s string) string { return s[:10] } // yyyy-MM-dd
}
for {
out, err := lib.CostExplorerClient().GetCostAndUsageWithContext(ctx, &costexplorer.GetCostAndUsageInput{
NextPageToken: token,
Filter: filter,
Granularity: granularity,
GroupBy: []*costexplorer.GroupDefinition{{
Type: aws.String(costexplorer.GroupDefinitionTypeDimension),
Key: aws.String(costexplorer.DimensionService),
}},
Metrics: []*string{
aws.String(costexplorer.MetricUnblendedCost),
},
TimePeriod: &costexplorer.DateInterval{
Start: aws.String(formatDate(start.Format(time.RFC3339))),
End: aws.String(formatDate(end.Format(time.RFC3339))),
},
})
if err != nil {
lib.Logger.Fatal("error: ", err)
}
results = append(results, out.ResultsByTime...)
if out.NextPageToken == nil {
break
}
token = out.NextPageToken
}
dashes := regexp.MustCompile(`\-+`)
for _, result := range results {
var vals []string
for _, group := range result.Groups {
if len(group.Keys) != 1 {
panic(lib.PformatAlways(group))
}
key := *group.Keys[0]
key = strings.ReplaceAll(key, "AWS ", "")
key = strings.ReplaceAll(key, "Amazon Simple Storage Service", "S3")
key = strings.ReplaceAll(key, "Amazon EC2 Container Registry (ECR)", "ECR")
key = strings.ReplaceAll(key, "Amazon ", "")
key = strings.ReplaceAll(key, "Simple Queue Service ", "SQS")
key = strings.ReplaceAll(key, "API Gateway", "ApiGateway")
key = strings.ReplaceAll(key, "AmazonCloudWatch", "Cloudwatch")
key = strings.ReplaceAll(key, " ", "-")
key = dashes.ReplaceAllString(key, "-")
key = strings.ReplaceAll(key, "Elastic-Compute-Cloud-Compute", "EC2")
vals = append(vals, fmt.Sprintf("%s=%s", key, *group.Metrics["UnblendedCost"].Amount))
}
fmt.Println("timestamp="+*result.TimePeriod.Start, strings.Join(vals, " "))
}
}