Skip to content
Draft
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: 0 additions & 1 deletion cloudtrail-api/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ require (
github.com/aws/aws-sdk-go-v2 v1.38.3
github.com/aws/aws-sdk-go-v2/config v1.30.3
github.com/aws/aws-sdk-go-v2/service/cloudtrail v1.51.0
github.com/aws/aws-sdk-go-v2/service/costexplorer v1.54.0
github.com/aws/aws-sdk-go-v2/service/ec2 v1.251.0
github.com/aws/aws-sdk-go-v2/service/s3 v1.86.0
github.com/aws/aws-sdk-go-v2/service/sts v1.36.0
Expand Down
2 changes: 0 additions & 2 deletions cloudtrail-api/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.2 h1:sBpc8Ph6CpfZsEdkz/8bfg8WhKlW
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.2/go.mod h1:Z2lDojZB+92Wo6EKiZZmJid9pPrDJW2NNIXSlaEfVlU=
github.com/aws/aws-sdk-go-v2/service/cloudtrail v1.51.0 h1:mEDXhybFN7q39EBrN3SiZt0sebBU18ZNUuvOPftYI84=
github.com/aws/aws-sdk-go-v2/service/cloudtrail v1.51.0/go.mod h1:bAz9Mfw6YqILCw087zDfCyDuZNs4wK4S+G+JSHBSyW0=
github.com/aws/aws-sdk-go-v2/service/costexplorer v1.54.0 h1:5e/C1PaQywGtklpMotdHKon/8MfsDyzJ9WFh0ge8G38=
github.com/aws/aws-sdk-go-v2/service/costexplorer v1.54.0/go.mod h1:tR04F/rUvoQ/5YFp3XS+SDB6pWc/Ls0f19WKA8PauDI=
github.com/aws/aws-sdk-go-v2/service/ec2 v1.251.0 h1:hGHSNZDTFnhLGUpRkQORM8uBY9R/FOkxCkuUUJBEOQ4=
github.com/aws/aws-sdk-go-v2/service/ec2 v1.251.0/go.mod h1:SmMqzfS4HVsOD58lwLZ79oxF58f8zVe5YdK3o+/o1Ck=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1 h1:oegbebPEMA/1Jny7kvwejowCaHz1FWZAQ94WXFNCyTM=
Expand Down
17 changes: 0 additions & 17 deletions cloudtrail-api/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,23 +70,6 @@ func main() {
}
_ = utils.StoreCreateEventSqlite(db, event)
}

// Aggregate costs per user and resource
userResources, err := utils.GetUserResourcesSqlite(db)
utils.Err("error getting user resources: %v", err)

result := make(map[string]map[string]float64)
for user, resources := range userResources {
costs, err := utils.GetResourceCosts(cfg, resources)
if err != nil {
continue
}
result[user] = costs
}

out, err := json.MarshalIndent(result, "", " ")
utils.Err("error marshalling result: %s", err)
fmt.Println(string(out))
}

var events = []string{
Expand Down
95 changes: 2 additions & 93 deletions cloudtrail-api/utils/aws_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,14 @@ package utils

import (
"context"
"database/sql"
_ "github.com/mattn/go-sqlite3"
"log"
"strconv"
"strings"
"time"

_ "github.com/mattn/go-sqlite3"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/costexplorer"
costexplorerTypes "github.com/aws/aws-sdk-go-v2/service/costexplorer/types"
"github.com/aws/aws-sdk-go-v2/service/s3"
"github.com/aws/aws-sdk-go-v2/service/sts"
)
Expand Down Expand Up @@ -55,91 +52,3 @@ type CreateEvent struct {
ResourceId string
Timestamp time.Time
}

const SqliteDBPath = "finleap_events.db"

// Initialize SQLite DB and create table if not exists
func InitSqliteDB() (*sql.DB, error) {
db, err := sql.Open("sqlite3", SqliteDBPath)
if err != nil {
return nil, err
}
createTable := `CREATE TABLE IF NOT EXISTS events (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT,
resource_id TEXT,
timestamp TEXT
);`
_, err = db.Exec(createTable)
if err != nil {
return nil, err
}
return db, nil
}

// Store a create event in SQLite
func StoreCreateEventSqlite(db *sql.DB, event CreateEvent) error {
stmt := `INSERT INTO events (username, resource_id, timestamp) VALUES (?, ?, ?)`
_, err := db.Exec(stmt, event.Username, event.ResourceId, event.Timestamp.Format(time.RFC3339))
return err
}

// Get all resources created by each user from SQLite
func GetUserResourcesSqlite(db *sql.DB) (map[string][]string, error) {
result := make(map[string][]string)
rows, err := db.Query(`SELECT username, resource_id FROM events`)
if err != nil {
return nil, err
}
defer rows.Close()
for rows.Next() {
var username, resourceId string
if err := rows.Scan(&username, &resourceId); err != nil {
continue
}
result[username] = append(result[username], resourceId)
}
return result, nil
}

// Get cost for a list of resources using Cost Explorer
func GetResourceCosts(cfg aws.Config, resources []string) (map[string]float64, error) {
ce := costexplorer.NewFromConfig(cfg)
costs := make(map[string]float64)
end := time.Now().Format("2006-01-02")
start := "2023-01-01" // Change as needed
for _, resource := range resources {
input := &costexplorer.GetCostAndUsageInput{
TimePeriod: &costexplorerTypes.DateInterval{
Start: &start,
End: &end,
},
Granularity: costexplorerTypes.GranularityMonthly,
Metrics: []string{"UnblendedCost"},
Filter: &costexplorerTypes.Expression{
Dimensions: &costexplorerTypes.DimensionValues{
Key: costexplorerTypes.DimensionResourceId,
Values: []string{resource},
},
},
}
resp, err := ce.GetCostAndUsage(context.TODO(), input)
if err != nil {
costs[resource] = 0.0
continue
}
var total float64
for _, res := range resp.ResultsByTime {
for _, grp := range res.Groups {
for _, met := range grp.Metrics {
amt, err := strconv.ParseFloat(*met.Amount, 64)
if err == nil {
total += amt
}
}
}
}
costs[resource] = total
}
return costs, nil
}
58 changes: 58 additions & 0 deletions cloudtrail-api/utils/database.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package utils

import (
"database/sql"
"time"
)

const SqliteDBPath = "finleap_events.db"

// Initialize SQLite DB and create table if not exists
func InitSqliteDB() (*sql.DB, error) {
// TODO: remove this after using actual data from ../../cloudtrail-dynamodb instead of SQLite

db, err := sql.Open("sqlite3", SqliteDBPath)
if err != nil {
return nil, err
}
createTable := `CREATE TABLE IF NOT EXISTS events (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT,
resource_id TEXT,
timestamp TEXT
);`
_, err = db.Exec(createTable)
if err != nil {
return nil, err
}
return db, nil
}

// Store a create event in SQLite
func StoreCreateEventSqlite(db *sql.DB, event CreateEvent) error {
// TODO: remove this after using actual data from ../../cloudtrail-dynamodb instead of SQLite

stmt := `INSERT INTO events (username, resource_id, timestamp) VALUES (?, ?, ?)`
_, err := db.Exec(stmt, event.Username, event.ResourceId, event.Timestamp.Format(time.RFC3339))
return err
}

// Get all resources created by each user from SQLite
func GetUserResourcesSqlite(db *sql.DB) (map[string][]string, error) {
// TODO: use actual data from ../../cloudtrail-dynamodb instead of SQLite

result := make(map[string][]string)
rows, err := db.Query(`SELECT username, resource_id FROM events`)
if err != nil {
return nil, err
}
defer rows.Close()
for rows.Next() {
var username, resourceId string
if err := rows.Scan(&username, &resourceId); err != nil {
continue
}
result[username] = append(result[username], resourceId)
}
return result, nil
}