| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171 |
- package carbon
- import (
- "embed"
- "encoding/csv"
- "fmt"
- "strconv"
- "strings"
- "github.com/opencost/opencost/core/pkg/log"
- "github.com/opencost/opencost/core/pkg/opencost"
- "github.com/opencost/opencost/core/pkg/util"
- )
- //go:embed carbonlookupdata.csv
- var f embed.FS
- type carbonLookupKeyDisk struct {
- provider string
- region string
- }
- type carbonLookupKeyNode struct {
- provider string
- region string
- instanceType string
- }
- var carbonLookupNode map[carbonLookupKeyNode]float64
- var carbonLookupDisk map[carbonLookupKeyDisk]float64
- // Opencost does not build network types
- var carbonValidInstanceTypes map[string]string
- var carbonValidRegions map[string]string
- func init() {
- carbonData, err := f.ReadFile("carbonlookupdata.csv")
- if err != nil {
- log.Errorf("Error getting content of carbon lookup file: %s", err)
- return
- }
- reader := csv.NewReader(strings.NewReader(string(carbonData)))
- // skip header
- _, err = reader.Read()
- if err != nil {
- log.Errorf("Error reading carbon lookup data: %s", err)
- return
- }
- dat, err := reader.ReadAll()
- if err != nil {
- log.Errorf("Error reading carbon lookup data: %s", err)
- return
- }
- carbonLookupNode = make(map[carbonLookupKeyNode]float64)
- carbonLookupDisk = make(map[carbonLookupKeyDisk]float64)
- carbonValidInstanceTypes = make(map[string]string)
- carbonValidRegions = make(map[string]string)
- for _, carbonItem := range dat {
- if coeff, err := strconv.ParseFloat(carbonItem[5], 64); err != nil {
- panic(fmt.Errorf("error setting up carbon lookup table: malformed carbon cost '%s'", carbonItem[5]))
- } else {
- provider := carbonItem[0]
- region := carbonItem[1]
- instanceType := carbonItem[2]
- assetType := carbonItem[3]
- switch assetType {
- case "Node":
- carbonLookupNode[carbonLookupKeyNode{
- provider: provider,
- region: region,
- instanceType: instanceType,
- }] = coeff
- case "Disk":
- carbonLookupDisk[carbonLookupKeyDisk{
- provider: provider,
- region: region,
- }] = coeff
- }
- carbonValidInstanceTypes[instanceType] = provider
- carbonValidRegions[region] = provider
- }
- }
- }
- type CarbonRow struct {
- Co2e float64 `json:"co2e"`
- }
- func RelateCarbonAssets(as *opencost.AssetSet) (map[string]CarbonRow, error) {
- res := make(map[string]CarbonRow)
- for key, asset := range as.Assets {
- // If no valid region, default to per-provider calculated average
- region, _ := util.GetRegion(asset.GetLabels())
- if _, ok := carbonValidRegions[region]; !ok {
- region = "average-region"
- }
- // If no valid instance type, also default to per-provider calculated average
- instanceType, _ := util.GetInstanceType(asset.GetLabels())
- if _, ok := carbonValidInstanceTypes[instanceType]; !ok {
- region = "average-region"
- }
- provider := getProviderFromProviderID(asset.GetProperties().ProviderID)
- // If we're not able to parse the provider id, try to fetch the provider from the carbon data
- if provider == "" && region != "average-region" {
- provider = carbonValidRegions[region]
- } else {
- if asset.Type() == opencost.NodeAssetType || asset.Type() == opencost.DiskAssetType {
- log.DedupedErrorf(10, "Cannot infer region information for asset '%s'", asset.GetProperties().ProviderID)
- }
- }
- var carbonCoeff float64
- switch asset.Type() {
- case opencost.NodeAssetType:
- carbonCoeff = carbonLookupNode[carbonLookupKeyNode{
- provider: provider,
- region: region,
- instanceType: instanceType,
- }]
- case opencost.DiskAssetType:
- carbonCoeff = carbonLookupDisk[carbonLookupKeyDisk{
- provider: provider,
- region: region,
- }]
- }
- res[key] = CarbonRow{
- Co2e: carbonCoeff * asset.Minutes() / 60,
- }
- }
- return res, nil
- }
- func getProviderFromProviderID(providerid string) string {
- if strings.HasPrefix(providerid, "gke") {
- return opencost.GCPProvider
- } else if strings.HasPrefix(providerid, "i-") {
- return opencost.AWSProvider
- } else if strings.HasPrefix(providerid, "azure") {
- return opencost.AzureProvider
- }
- return ""
- }
|