diff --git a/cmd/plan.go b/cmd/plan.go index e97bab1..8163797 100644 --- a/cmd/plan.go +++ b/cmd/plan.go @@ -44,7 +44,7 @@ Example usages: log.Fatal(err) } - input := workdir + var input string if len(args) != 0 { input = args[0] if !filepath.IsAbs(input) { diff --git a/internal/data/data/energy_coefficients.json b/internal/data/data/energy_coefficients.json index b3a726e..7699d28 100644 --- a/internal/data/data/energy_coefficients.json +++ b/internal/data/data/energy_coefficients.json @@ -1,7 +1,7 @@ { "AWS": { "cpu_min_wh": 0.74, - "cpu_max_wh": 3.5, + "cpu_max_wh": 5.23, "storage_hdd_wh_tb": 0.65, "storage_ssd_wh_tb": 1.2, "networking_wh_gb": 1, diff --git a/internal/estimate/coefficients/coefficients.go b/internal/estimate/coefficients/coefficients.go index 6fe6df4..feb5128 100644 --- a/internal/estimate/coefficients/coefficients.go +++ b/internal/estimate/coefficients/coefficients.go @@ -49,6 +49,7 @@ func (cps *CoefficientsProviders) GetByProvider(provider providers.Provider) Coe func (cps *CoefficientsProviders) getByProviderName(name string) Coefficients { r := reflect.ValueOf(cps) - coefficients := reflect.Indirect(r).FieldByName(name) - return coefficients.Interface().(Coefficients) + coefficientsVal := reflect.Indirect(r).FieldByName(name) + coefficients := coefficientsVal.Interface().(Coefficients) + return coefficients } diff --git a/internal/estimate/estimate.go b/internal/estimate/estimate.go index 82c4660..afbc0f4 100644 --- a/internal/estimate/estimate.go +++ b/internal/estimate/estimate.go @@ -45,7 +45,7 @@ func EstimateResources(resourceList map[string]resources.Resource) estimation.Es return estimation.EstimationReport{ Info: estimation.EstimationInfo{ UnitTime: viper.Get("unit.time").(string), - UnitWattTime: fmt.Sprintf("%s%s", viper.Get("unit.power"), viper.Get("unit.time")), + UnitWattTime: fmt.Sprintf("%s%s", "W", viper.Get("unit.time")), UnitCarbonEmissionsTime: fmt.Sprintf("%sCO2eq/%s", viper.Get("unit.carbon"), viper.Get("unit.time")), DateTime: time.Now(), InfoByProvider: map[providers.Provider]estimation.InfoByProvider{ diff --git a/internal/estimate/estimate/Energy.go b/internal/estimate/estimate/Energy.go index 0aaa679..7af4ae4 100644 --- a/internal/estimate/estimate/Energy.go +++ b/internal/estimate/estimate/Energy.go @@ -18,7 +18,8 @@ func estimateWattHour(resource *resources.ComputeResource) decimal.Decimal { log.Debugf("%v.%v Storage in Wh: %v", resource.Identification.ResourceType, resource.Identification.Name, storageInWh) gpuEstimationInWh := EstimateWattGPU(resource) log.Debugf("%v.%v GPUs in Wh: %v", resource.Identification.ResourceType, resource.Identification.Name, gpuEstimationInWh) - pue := coefficients.GetEnergyCoefficients().GCP.PueAverage + pue := coefficients.GetEnergyCoefficients().GetByProvider(resource.Identification.Provider).PueAverage + log.Debugf("%v.%v PUE %v", resource.Identification.ResourceType, resource.Identification.Name, pue) rawWattEstimate := decimal.Sum( cpuEstimationInWh, diff --git a/internal/estimate/estimate/Resource.go b/internal/estimate/estimate/Resource.go index ba8c867..c4bfada 100644 --- a/internal/estimate/estimate/Resource.go +++ b/internal/estimate/estimate/Resource.go @@ -1,6 +1,8 @@ package estimate import ( + "strings" + "github.com/carboniferio/carbonifer/internal/estimate/coefficients" "github.com/carboniferio/carbonifer/internal/estimate/estimation" @@ -15,56 +17,55 @@ func EstimateSupportedResource(resource resources.Resource) *estimation.Estimati var computeResource resources.ComputeResource = resource.(resources.ComputeResource) // Electric power used per unit of time - avgWatt := estimateWattHour(&computeResource) // Watt hour - if viper.Get("unit.power").(string) == "kW" { - avgWatt = avgWatt.Div(decimal.NewFromInt(1000)) - } - if viper.Get("unit.time").(string) == "m" { - avgWatt = avgWatt.Mul(decimal.NewFromInt(24 * 30)) - } - if viper.Get("unit.time").(string) == "y" { - avgWatt = avgWatt.Mul(decimal.NewFromInt(24 * 365)) - } - avgWattStr := avgWatt.String() + // It's computed first in watt per hour + avgWattHour := estimateWattHour(&computeResource) // Watt hour + avgKWattHour := avgWattHour.Div(decimal.NewFromInt(1000)) // Regional grid emission per unit of time + // regionEmissions are in gCO2/kWh regionEmissions, err := coefficients.RegionEmission(resource.GetIdentification().Provider, resource.GetIdentification().Region) // gCO2eq /kWh if err != nil { log.Fatalf("Error while getting region emissions for %v: %v", resource.GetAddress(), err) } - if viper.Get("unit.power").(string) == "W" { - regionEmissions.GridCarbonIntensity = regionEmissions.GridCarbonIntensity.Div(decimal.NewFromInt(1000)) + + // Carbon Emissions + carbonEmissionInGCO2PerH := avgKWattHour.Mul(regionEmissions.GridCarbonIntensity) + carbonEmissionPerTime := carbonEmissionInGCO2PerH + if strings.ToLower(viper.GetString("unit.time")) == "d" { + carbonEmissionPerTime = carbonEmissionPerTime.Mul(decimal.NewFromInt(24)) } - if viper.Get("unit.time").(string) == "m" { - regionEmissions.GridCarbonIntensity = regionEmissions.GridCarbonIntensity.Mul(decimal.NewFromInt(24 * 30)) + if strings.ToLower(viper.GetString("unit.time")) == "m" { + carbonEmissionPerTime = carbonEmissionPerTime.Mul(decimal.NewFromInt(24 * 30)) } - if viper.Get("unit.time").(string) == "y" { - regionEmissions.GridCarbonIntensity = regionEmissions.GridCarbonIntensity.Mul(decimal.NewFromInt(24 * 365)) + if strings.ToLower(viper.GetString("unit.time")) == "y" { + carbonEmissionPerTime = carbonEmissionPerTime.Mul(decimal.NewFromInt(24 * 365)) } - if viper.Get("unit.carbon").(string) == "kg" { - regionEmissions.GridCarbonIntensity = regionEmissions.GridCarbonIntensity.Div(decimal.NewFromInt(1000)) + if strings.ToLower(viper.GetString("unit.carbon")) == "kg" { + carbonEmissionPerTime = carbonEmissionPerTime.Div(decimal.NewFromInt(1000)) } - - // Carbon Emissions - carbonEmissionPerTime := avgWatt.Mul(regionEmissions.GridCarbonIntensity) carbonEmissionPerTimeStr := carbonEmissionPerTime.String() log.Debugf( - "estimating resource %v.%v (%v): %v %v%v * %v %vCO2/%v%v = %v %vCO2/%v%v * %v", + "estimating resource %v.%v (%v): %v %v%v * %v %vCO2/%v%v = %v %vCO2/%v%v * %v = %v %vCO2/%v%v * %v", computeResource.Identification.ResourceType, computeResource.Identification.Name, regionEmissions.Region, - avgWattStr, - viper.Get("unit.power").(string), - viper.Get("unit.time").(string), + avgKWattHour.String(), + "kW", + "h", regionEmissions.GridCarbonIntensity, - viper.Get("unit.carbon").(string), - viper.Get("unit.power").(string), - viper.Get("unit.time").(string), + "g", + "kW", + "h", + carbonEmissionInGCO2PerH, + "g", + "kW", + "h", + resource.GetIdentification().Count, carbonEmissionPerTimeStr, - viper.Get("unit.carbon").(string), - viper.Get("unit.power").(string), - viper.Get("unit.time").(string), + viper.GetString("unit.carbon"), + viper.GetString("unit.power"), + viper.GetString("unit.time"), resource.GetIdentification().Count, ) @@ -77,7 +78,7 @@ func EstimateSupportedResource(resource resources.Resource) *estimation.Estimati est := &estimation.EstimationResource{ Resource: &computeResource, - Power: avgWatt.RoundFloor(10), + Power: avgWattHour.RoundFloor(10), CarbonEmissions: carbonEmissionPerTime.RoundFloor(10), AverageCPUUsage: decimal.NewFromFloat(viper.GetFloat64("provider.gcp.avg_cpu_use")).RoundFloor(10), TotalCount: decimal.NewFromInt(count * replicationFactor), diff --git a/internal/estimate/estimate_test.go b/internal/estimate/estimate_test.go index 5694802..5d2bc6f 100644 --- a/internal/estimate/estimate_test.go +++ b/internal/estimate/estimate_test.go @@ -149,8 +149,8 @@ func TestEstimateResourceKilo(t *testing.T) { args: args{resourceGCPComputeBasic}, want: &estimation.EstimationResource{ Resource: &resourceGCPComputeBasic, - Power: decimal.NewFromFloat(5472.56448).RoundFloor(10), - CarbonEmissions: decimal.NewFromFloat(232.4745391104).RoundFloor(10), + Power: decimal.NewFromFloat(7.600784).RoundFloor(10), + CarbonEmissions: decimal.NewFromFloat(0.3228813043).RoundFloor(10), AverageCPUUsage: decimal.NewFromFloat(avgCPUUse), TotalCount: decimal.NewFromInt(1), }, @@ -160,8 +160,8 @@ func TestEstimateResourceKilo(t *testing.T) { args: args{resourceGCPComputeCPUType}, want: &estimation.EstimationResource{ Resource: &resourceGCPComputeCPUType, - Power: decimal.NewFromFloat(6880.7275733647).RoundFloor(10), - CarbonEmissions: decimal.NewFromFloat(292.2933073165).RoundFloor(10), + Power: decimal.NewFromFloat(9.5565660741).RoundFloor(10), + CarbonEmissions: decimal.NewFromFloat(0.4059629268).RoundFloor(10), AverageCPUUsage: decimal.NewFromFloat(avgCPUUse), TotalCount: decimal.NewFromInt(1), }, diff --git a/internal/terraform/terraform.go b/internal/terraform/terraform.go index 86ea16f..b5ca11d 100644 --- a/internal/terraform/terraform.go +++ b/internal/terraform/terraform.go @@ -120,11 +120,23 @@ func CarboniferPlan(input string) (*map[string]interface{}, error) { // If the path points to a file, run show if !fileInfo.IsDir() { parentDir := filepath.Dir(input) + // Add the .carbonifer folder in workdir + viper.AddConfigPath(filepath.Join(parentDir, ".carbonifer")) fileName := filepath.Base(input) viper.Set("workdir", parentDir) tfPlan, err := terraformShow(fileName) return tfPlan, err + } else { + viper.AddConfigPath(filepath.Join(input, ".carbonifer")) } + + // Refresh viper config + if err := viper.ReadInConfig(); err != nil { + if _, ok := err.(viper.ConfigFileNotFoundError); !ok { + log.Panic(err) + } + } + // If the path points to a directory, run plan viper.Set("workdir", input) tfPlan, err := TerraformPlan() diff --git a/internal/utils/config.go b/internal/utils/config.go index e2446f3..0ce99e8 100644 --- a/internal/utils/config.go +++ b/internal/utils/config.go @@ -56,6 +56,8 @@ func basePath() string { return filepath.Join(d, "../..") } +var WorkDir string + func initViper(configFilePath string) { loadViperDefaults() diff --git a/test/terraform/gcp_1/provider.tf b/test/terraform/gcp_1/provider.tf index 7fb6a3d..ee95f54 100644 --- a/test/terraform/gcp_1/provider.tf +++ b/test/terraform/gcp_1/provider.tf @@ -1,4 +1,5 @@ provider "google" { region = "europe-west9" + project = "dummy-project" } diff --git a/test/terraform/gcp_cit/provider.tf b/test/terraform/gcp_cit/provider.tf index 7fb6a3d..ee95f54 100644 --- a/test/terraform/gcp_cit/provider.tf +++ b/test/terraform/gcp_cit/provider.tf @@ -1,4 +1,5 @@ provider "google" { region = "europe-west9" + project = "dummy-project" } diff --git a/test/terraform/gcp_gke/variables.tf b/test/terraform/gcp_gke/variables.tf index c952a8d..4fc599e 100644 --- a/test/terraform/gcp_gke/variables.tf +++ b/test/terraform/gcp_gke/variables.tf @@ -2,6 +2,10 @@ variable "region" { default = "europe-west9" } +variable "project" { + default = "dummy-project" +} + variable "project_id" { default = "cbf-terraform" } diff --git a/test/terraform/gcp_global_module/global.tf b/test/terraform/gcp_global_module/global.tf index 9d4d743..3f06fc5 100644 --- a/test/terraform/gcp_global_module/global.tf +++ b/test/terraform/gcp_global_module/global.tf @@ -1,5 +1,6 @@ provider "google" { region = local.common_region + project = "dummy-project" } locals { diff --git a/test/terraform/gcp_group/main.tf b/test/terraform/gcp_group/main.tf index 8c2095f..dd340f8 100644 --- a/test/terraform/gcp_group/main.tf +++ b/test/terraform/gcp_group/main.tf @@ -38,6 +38,7 @@ resource "google_compute_instance_group_manager" "my-group-manager" { resource "google_compute_autoscaler" "autoscaler" { name = "my-autoscaler" target = google_compute_instance_group_manager.my-group-manager.id + zone = "europe-west9-a" autoscaling_policy { max_replicas = 10 diff --git a/test/terraform/gcp_group/provider.tf b/test/terraform/gcp_group/provider.tf index 7fb6a3d..ee95f54 100644 --- a/test/terraform/gcp_group/provider.tf +++ b/test/terraform/gcp_group/provider.tf @@ -1,4 +1,5 @@ provider "google" { region = "europe-west9" + project = "dummy-project" } diff --git a/test/terraform/gcp_images/provider.tf b/test/terraform/gcp_images/provider.tf index 7fb6a3d..ee95f54 100644 --- a/test/terraform/gcp_images/provider.tf +++ b/test/terraform/gcp_images/provider.tf @@ -1,4 +1,5 @@ provider "google" { region = "europe-west9" + project = "dummy-project" } diff --git a/test/terraform/planRaw/plan.tfplan b/test/terraform/planRaw/plan.tfplan index e6d7ba6..bf67182 100644 Binary files a/test/terraform/planRaw/plan.tfplan and b/test/terraform/planRaw/plan.tfplan differ diff --git a/test/terraform/planRaw/provider.tf b/test/terraform/planRaw/provider.tf index 7fb6a3d..ee95f54 100644 --- a/test/terraform/planRaw/provider.tf +++ b/test/terraform/planRaw/provider.tf @@ -1,4 +1,5 @@ provider "google" { region = "europe-west9" + project = "dummy-project" }