diff --git a/cmd/exporter/config/config.go b/cmd/exporter/config/config.go index 9fdaab8d..24ccdb9f 100644 --- a/cmd/exporter/config/config.go +++ b/cmd/exporter/config/config.go @@ -5,10 +5,15 @@ import ( "time" ) +type CommonConfig struct { + ComputeInstanceLabel string +} + type Config struct { - Provider string - ProjectID string - Providers struct { + Provider string + ProjectID string + CommonConfig CommonConfig + Providers struct { AWS struct { Profile string Region string diff --git a/cmd/exporter/exporter.go b/cmd/exporter/exporter.go index b4d6ee83..77eb5cd7 100644 --- a/cmd/exporter/exporter.go +++ b/cmd/exporter/exporter.go @@ -31,6 +31,7 @@ import ( func main() { var cfg config.Config + commonFlags(&cfg) providerFlags(flag.CommandLine, &cfg) operationalFlags(&cfg) flag.Parse() @@ -78,6 +79,10 @@ func providerFlags(fs *flag.FlagSet, cfg *config.Config) { flag.IntVar(&cfg.Providers.GCP.DefaultGCSDiscount, "gcp.default-discount", 19, "GCP default discount") } +func commonFlags(cfg *config.Config) { + flag.StringVar(&cfg.CommonConfig.ComputeInstanceLabel, "compute.instance-label", "instance", "instance label name") +} + // operationalFlags is a helper method that is responsible for setting up the flags that are used to configure the operational aspects of the application. // TODO: This should probably be moved over to the config package. func operationalFlags(cfg *config.Config) { @@ -169,6 +174,7 @@ func selectProvider(ctx context.Context, cfg *config.Config) (provider.Provider, switch cfg.Provider { case "azure": return azure.New(ctx, &azure.Config{ + CommonConfig: &cfg.CommonConfig, Logger: cfg.Logger, SubscriptionId: cfg.Providers.Azure.SubscriptionId, Services: cfg.Providers.Azure.Services, @@ -176,6 +182,7 @@ func selectProvider(ctx context.Context, cfg *config.Config) (provider.Provider, }) case "aws": return aws.New(ctx, &aws.Config{ + CommonConfig: &cfg.CommonConfig, Logger: cfg.Logger, Region: cfg.Providers.AWS.Region, Profile: cfg.Providers.AWS.Profile, @@ -185,6 +192,7 @@ func selectProvider(ctx context.Context, cfg *config.Config) (provider.Provider, case "gcp": return google.New(&google.Config{ + CommonConfig: &cfg.CommonConfig, Logger: cfg.Logger, ProjectId: cfg.ProjectID, Region: cfg.Providers.GCP.Region, diff --git a/pkg/aws/aws.go b/pkg/aws/aws.go index b24b276e..251816da 100644 --- a/pkg/aws/aws.go +++ b/pkg/aws/aws.go @@ -15,6 +15,7 @@ import ( "github.com/aws/aws-sdk-go-v2/service/pricing" "github.com/prometheus/client_golang/prometheus" + "github.com/grafana/cloudcost-exporter/cmd/exporter/config" ec2Collector "github.com/grafana/cloudcost-exporter/pkg/aws/ec2" cloudcost_exporter "github.com/grafana/cloudcost-exporter" @@ -28,6 +29,7 @@ type Config struct { Region string Profile string ScrapeInterval time.Duration + CommonConfig *config.CommonConfig Logger *slog.Logger } @@ -144,6 +146,7 @@ func New(ctx context.Context, config *Config) (*AWS, error) { regionClientMap[*r.RegionName] = client } collector := ec2Collector.New(&ec2Collector.Config{ + CommonConfig: config.CommonConfig, Regions: regions.Regions, RegionClients: regionClientMap, Logger: logger, diff --git a/pkg/aws/ec2/ec2.go b/pkg/aws/ec2/ec2.go index 277d7e58..c85619a8 100644 --- a/pkg/aws/ec2/ec2.go +++ b/pkg/aws/ec2/ec2.go @@ -14,6 +14,7 @@ import ( "golang.org/x/sync/errgroup" cloudcostexporter "github.com/grafana/cloudcost-exporter" + "github.com/grafana/cloudcost-exporter/cmd/exporter/config" ec2client "github.com/grafana/cloudcost-exporter/pkg/aws/services/ec2" pricingClient "github.com/grafana/cloudcost-exporter/pkg/aws/services/pricing" "github.com/grafana/cloudcost-exporter/pkg/provider" @@ -29,26 +30,11 @@ var ( ErrGeneratePricingMap = errors.New("error generating pricing map") ) -var ( - InstanceCPUHourlyCostDesc = prometheus.NewDesc( - prometheus.BuildFQName(cloudcostexporter.MetricPrefix, subsystem, "instance_cpu_usd_per_core_hour"), - "The cpu cost a ec2 instance in USD/(core*h)", - []string{"instance", "region", "family", "machine_type", "cluster_name", "price_tier"}, - nil, - ) - InstanceMemoryHourlyCostDesc = prometheus.NewDesc( - prometheus.BuildFQName(cloudcostexporter.MetricPrefix, subsystem, "instance_memory_usd_per_gib_hour"), - "The memory cost of a ec2 instance in USD/(GiB*h)", - []string{"instance", "region", "family", "machine_type", "cluster_name", "price_tier"}, - nil, - ) - InstanceTotalHourlyCostDesc = prometheus.NewDesc( - prometheus.BuildFQName(cloudcostexporter.MetricPrefix, subsystem, "instance_total_usd_per_hour"), - "The total cost of the ec2 instance in USD/h", - []string{"instance", "region", "family", "machine_type", "cluster_name", "price_tier"}, - nil, - ) -) +type CollectorMetrics struct { + InstanceCPUHourlyCostDesc *prometheus.Desc + InstanceMemoryHourlyCostDesc *prometheus.Desc + InstanceTotalHourlyCostDesc *prometheus.Desc +} // Collector is a prometheus collector that collects metrics from AWS EKS clusters. type Collector struct { @@ -58,16 +44,41 @@ type Collector struct { pricingService pricingClient.Pricing NextScrape time.Time ec2RegionClients map[string]ec2client.EC2 + metrics *CollectorMetrics logger *slog.Logger } type Config struct { + CommonConfig *config.CommonConfig ScrapeInterval time.Duration Regions []ec2Types.Region RegionClients map[string]ec2client.EC2 Logger *slog.Logger } +func newCollectorMetrics(instanceLabel string) *CollectorMetrics { + return &CollectorMetrics{ + InstanceCPUHourlyCostDesc: prometheus.NewDesc( + prometheus.BuildFQName(cloudcostexporter.MetricPrefix, subsystem, "instance_cpu_usd_per_core_hour"), + "The cpu cost a ec2 instance in USD/(core*h)", + []string{instanceLabel, "region", "family", "machine_type", "cluster_name", "price_tier"}, + nil, + ), + InstanceMemoryHourlyCostDesc: prometheus.NewDesc( + prometheus.BuildFQName(cloudcostexporter.MetricPrefix, subsystem, "instance_memory_usd_per_gib_hour"), + "The memory cost of a ec2 instance in USD/(GiB*h)", + []string{instanceLabel, "region", "family", "machine_type", "cluster_name", "price_tier"}, + nil, + ), + InstanceTotalHourlyCostDesc: prometheus.NewDesc( + prometheus.BuildFQName(cloudcostexporter.MetricPrefix, subsystem, "instance_total_usd_per_hour"), + "The total cost of the ec2 instance in USD/h", + []string{instanceLabel, "region", "family", "machine_type", "cluster_name", "price_tier"}, + nil, + ), + } +} + // New creates an ec2 collector func New(config *Config, ps pricingClient.Pricing) *Collector { logger := config.Logger.With("logger", "ec2") @@ -75,6 +86,7 @@ func New(config *Config, ps pricingClient.Pricing) *Collector { ScrapeInterval: config.ScrapeInterval, Regions: config.Regions, ec2RegionClients: config.RegionClients, + metrics: newCollectorMetrics(config.CommonConfig.ComputeInstanceLabel), logger: logger, pricingService: ps, pricingMap: NewStructuredPricingMap(), @@ -202,9 +214,9 @@ func (c *Collector) emitMetricsFromChannel(reservationsCh chan []ec2Types.Reserv clusterName, pricetier, } - ch <- prometheus.MustNewConstMetric(InstanceCPUHourlyCostDesc, prometheus.GaugeValue, price.Cpu, labelValues...) - ch <- prometheus.MustNewConstMetric(InstanceMemoryHourlyCostDesc, prometheus.GaugeValue, price.Ram, labelValues...) - ch <- prometheus.MustNewConstMetric(InstanceTotalHourlyCostDesc, prometheus.GaugeValue, price.Total, labelValues...) + ch <- prometheus.MustNewConstMetric(c.metrics.InstanceCPUHourlyCostDesc, prometheus.GaugeValue, price.Cpu, labelValues...) + ch <- prometheus.MustNewConstMetric(c.metrics.InstanceMemoryHourlyCostDesc, prometheus.GaugeValue, price.Ram, labelValues...) + ch <- prometheus.MustNewConstMetric(c.metrics.InstanceTotalHourlyCostDesc, prometheus.GaugeValue, price.Total, labelValues...) } } } @@ -215,9 +227,9 @@ func (c *Collector) CheckReadiness() bool { } func (c *Collector) Describe(ch chan<- *prometheus.Desc) error { - ch <- InstanceCPUHourlyCostDesc - ch <- InstanceMemoryHourlyCostDesc - ch <- InstanceTotalHourlyCostDesc + ch <- c.metrics.InstanceCPUHourlyCostDesc + ch <- c.metrics.InstanceMemoryHourlyCostDesc + ch <- c.metrics.InstanceTotalHourlyCostDesc return nil } diff --git a/pkg/aws/ec2/ec2_test.go b/pkg/aws/ec2/ec2_test.go index 67f3964c..dbfa2aab 100644 --- a/pkg/aws/ec2/ec2_test.go +++ b/pkg/aws/ec2/ec2_test.go @@ -15,6 +15,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" + "github.com/grafana/cloudcost-exporter/cmd/exporter/config" mockec2 "github.com/grafana/cloudcost-exporter/mocks/pkg/aws/services/ec2" mockpricing "github.com/grafana/cloudcost-exporter/mocks/pkg/aws/services/pricing" ec2client "github.com/grafana/cloudcost-exporter/pkg/aws/services/ec2" @@ -114,6 +115,7 @@ func TestCollector_ListOnDemandPrices(t *testing.T) { func TestNewCollector(t *testing.T) { tests := map[string]struct { + instanceLabel string region string profile string scrapeInternal time.Duration @@ -121,12 +123,14 @@ func TestNewCollector(t *testing.T) { ec2s ec2client.EC2 }{ "Empty Region and profile should return a collector": { + instanceLabel: "instance", region: "", profile: "", scrapeInternal: 0, ps: nil, }, "Region and profile should return a collector": { + instanceLabel: "instance", region: "us-east-1", profile: "default", scrapeInternal: 0, @@ -136,7 +140,8 @@ func TestNewCollector(t *testing.T) { for name, tt := range tests { t.Run(name, func(t *testing.T) { collector := New(&Config{ - Logger: logger, + CommonConfig: &config.CommonConfig{ComputeInstanceLabel: tt.instanceLabel}, + Logger: logger, }, tt.ps) assert.NotNil(t, collector) }) @@ -146,13 +151,15 @@ func TestNewCollector(t *testing.T) { func TestCollector_Name(t *testing.T) { t.Run("Name should return the same name as the subsystem const", func(t *testing.T) { collector := New(&Config{ - Logger: logger, + CommonConfig: &config.CommonConfig{ComputeInstanceLabel: "instance"}, + Logger: logger, }, nil) assert.Equal(t, subsystem, collector.Name()) }) } func TestCollector_Collect(t *testing.T) { + commonConfig := &config.CommonConfig{ComputeInstanceLabel: "node"} regions := []ec2Types.Region{ { RegionName: aws.String("us-east-1"), @@ -160,7 +167,8 @@ func TestCollector_Collect(t *testing.T) { } t.Run("Collect should return no error", func(t *testing.T) { collector := New(&Config{ - Logger: logger, + CommonConfig: commonConfig, + Logger: logger, }, nil) ch := make(chan prometheus.Metric) go func() { @@ -178,8 +186,9 @@ func TestCollector_Collect(t *testing.T) { return nil, assert.AnError }).Times(1) collector := New(&Config{ - Regions: regions, - Logger: logger, + CommonConfig: commonConfig, + Regions: regions, + Logger: logger, }, ps) ch := make(chan prometheus.Metric) err := collector.Collect(ch) @@ -196,8 +205,9 @@ func TestCollector_Collect(t *testing.T) { }, nil }).Times(1) collector := New(&Config{ - Regions: regions, - Logger: logger, + CommonConfig: commonConfig, + Regions: regions, + Logger: logger, }, ps) ch := make(chan prometheus.Metric) err := collector.Collect(ch) @@ -224,6 +234,7 @@ func TestCollector_Collect(t *testing.T) { regionClientMap[*r.RegionName] = ec2s } collector := New(&Config{ + CommonConfig: commonConfig, Regions: regions, RegionClients: regionClientMap, Logger: logger, @@ -263,6 +274,7 @@ func TestCollector_Collect(t *testing.T) { regionClientMap[*r.RegionName] = ec2s } collector := New(&Config{ + CommonConfig: commonConfig, Regions: regions, RegionClients: regionClientMap, Logger: logger, @@ -358,6 +370,7 @@ func TestCollector_Collect(t *testing.T) { regionClientMap[*r.RegionName] = ec2s } collector := New(&Config{ + CommonConfig: commonConfig, Regions: regions, RegionClients: regionClientMap, Logger: logger, @@ -377,5 +390,8 @@ func TestCollector_Collect(t *testing.T) { metrics = append(metrics, utils.ReadMetrics(metric)) } assert.Len(t, metrics, 6) + // Assert we properly set the instanceLabel + assert.Equal(t, "ip-172-31-0-1.ec2.internal", metrics[0].Labels["node"]) + assert.Equal(t, "", metrics[0].Labels["instance"]) }) } diff --git a/pkg/azure/aks/aks.go b/pkg/azure/aks/aks.go index 0429cbff..83728950 100644 --- a/pkg/azure/aks/aks.go +++ b/pkg/azure/aks/aks.go @@ -9,6 +9,7 @@ import ( "github.com/Azure/azure-sdk-for-go/sdk/azidentity" "github.com/prometheus/client_golang/prometheus" + "github.com/grafana/cloudcost-exporter/cmd/exporter/config" "github.com/grafana/cloudcost-exporter/pkg/provider" cloudcost_exporter "github.com/grafana/cloudcost-exporter" @@ -55,39 +56,50 @@ var ( ) // Prometheus Metrics -var ( - InstanceCPUHourlyCostDesc = prometheus.NewDesc( - prometheus.BuildFQName(cloudcost_exporter.MetricPrefix, subsystem, "instance_cpu_usd_per_core_hour"), - "The cpu cost a compute instance in USD/(core*h)", - []string{"instance", "region", "machine_type", "family", "cluster_name", "price_tier", "operating_system"}, - nil, - ) - InstanceMemoryHourlyCostDesc = prometheus.NewDesc( - prometheus.BuildFQName(cloudcost_exporter.MetricPrefix, subsystem, "instance_memory_usd_per_gib_hour"), - "The memory cost of a compute instance in USD/(GiB*h)", - []string{"instance", "region", "machine_type", "family", "cluster_name", "price_tier", "operating_system"}, - nil, - ) - InstanceTotalHourlyCostDesc = prometheus.NewDesc( - prometheus.BuildFQName(cloudcost_exporter.MetricPrefix, subsystem, "instance_total_usd_per_hour"), - "The total cost of an compute instance in USD/h", - []string{"instance", "region", "machine_type", "family", "cluster_name", "price_tier", "operating_system"}, - nil, - ) -) + +type CollectorMetrics struct { + InstanceCPUHourlyCostDesc *prometheus.Desc + InstanceMemoryHourlyCostDesc *prometheus.Desc + InstanceTotalHourlyCostDesc *prometheus.Desc +} + +func newCollectorMetrics(instanceLabel string) *CollectorMetrics { + return &CollectorMetrics{ + InstanceCPUHourlyCostDesc: prometheus.NewDesc( + prometheus.BuildFQName(cloudcost_exporter.MetricPrefix, subsystem, "instance_cpu_usd_per_core_hour"), + "The cpu cost a compute instance in USD/(core*h)", + []string{instanceLabel, "region", "machine_type", "family", "cluster_name", "price_tier", "operating_system"}, + nil, + ), + InstanceMemoryHourlyCostDesc: prometheus.NewDesc( + prometheus.BuildFQName(cloudcost_exporter.MetricPrefix, subsystem, "instance_memory_usd_per_gib_hour"), + "The memory cost of a compute instance in USD/(GiB*h)", + []string{instanceLabel, "region", "machine_type", "family", "cluster_name", "price_tier", "operating_system"}, + nil, + ), + InstanceTotalHourlyCostDesc: prometheus.NewDesc( + prometheus.BuildFQName(cloudcost_exporter.MetricPrefix, subsystem, "instance_total_usd_per_hour"), + "The total cost of an compute instance in USD/h", + []string{instanceLabel, "region", "machine_type", "family", "cluster_name", "price_tier", "operating_system"}, + nil, + ), + } +} // Collector is a prometheus collector that collects metrics from AKS clusters. type Collector struct { context context.Context logger *slog.Logger + metrics *CollectorMetrics PriceStore *PriceStore MachineStore *MachineStore } type Config struct { - Logger *slog.Logger - Credentials *azidentity.DefaultAzureCredential + CommonConfig *config.CommonConfig + Logger *slog.Logger + Credentials *azidentity.DefaultAzureCredential SubscriptionId string } @@ -131,6 +143,7 @@ func New(ctx context.Context, cfg *Config) (*Collector, error) { return &Collector{ context: ctx, logger: logger, + metrics: newCollectorMetrics(cfg.CommonConfig.ComputeInstanceLabel), PriceStore: priceStore, MachineStore: machineStore, @@ -185,9 +198,9 @@ func (c *Collector) Collect(ch chan<- prometheus.Metric) error { vmInfo.OperatingSystem.String(), } - ch <- prometheus.MustNewConstMetric(InstanceCPUHourlyCostDesc, prometheus.GaugeValue, price.MachinePricesBreakdown.PricePerCore, labelValues...) - ch <- prometheus.MustNewConstMetric(InstanceMemoryHourlyCostDesc, prometheus.GaugeValue, price.MachinePricesBreakdown.PricePerGiB, labelValues...) - ch <- prometheus.MustNewConstMetric(InstanceTotalHourlyCostDesc, prometheus.GaugeValue, price.RetailPrice, labelValues...) + ch <- prometheus.MustNewConstMetric(c.metrics.InstanceCPUHourlyCostDesc, prometheus.GaugeValue, price.MachinePricesBreakdown.PricePerCore, labelValues...) + ch <- prometheus.MustNewConstMetric(c.metrics.InstanceMemoryHourlyCostDesc, prometheus.GaugeValue, price.MachinePricesBreakdown.PricePerGiB, labelValues...) + ch <- prometheus.MustNewConstMetric(c.metrics.InstanceTotalHourlyCostDesc, prometheus.GaugeValue, price.RetailPrice, labelValues...) } c.logger.LogAttrs(c.context, slog.LevelInfo, "metrics collected", slog.Duration("duration", time.Since(now))) @@ -195,9 +208,9 @@ func (c *Collector) Collect(ch chan<- prometheus.Metric) error { } func (c *Collector) Describe(ch chan<- *prometheus.Desc) error { - ch <- InstanceCPUHourlyCostDesc - ch <- InstanceMemoryHourlyCostDesc - ch <- InstanceTotalHourlyCostDesc + ch <- c.metrics.InstanceCPUHourlyCostDesc + ch <- c.metrics.InstanceMemoryHourlyCostDesc + ch <- c.metrics.InstanceTotalHourlyCostDesc return nil } diff --git a/pkg/azure/azure.go b/pkg/azure/azure.go index a8121674..c15f1b11 100644 --- a/pkg/azure/azure.go +++ b/pkg/azure/azure.go @@ -11,6 +11,7 @@ import ( "github.com/Azure/azure-sdk-for-go/sdk/azidentity" "github.com/prometheus/client_golang/prometheus" + "github.com/grafana/cloudcost-exporter/cmd/exporter/config" "github.com/grafana/cloudcost-exporter/pkg/azure/aks" "github.com/grafana/cloudcost-exporter/pkg/provider" @@ -96,7 +97,8 @@ type Azure struct { } type Config struct { - Logger *slog.Logger + Logger *slog.Logger + CommonConfig *config.CommonConfig SubscriptionId string @@ -126,6 +128,7 @@ func New(ctx context.Context, config *Config) (*Azure, error) { collector, err := aks.New(ctx, &aks.Config{ Credentials: creds, SubscriptionId: config.SubscriptionId, + CommonConfig: config.CommonConfig, Logger: logger, }) if err != nil { diff --git a/pkg/azure/azure_test.go b/pkg/azure/azure_test.go index abbfb501..5c4476cc 100644 --- a/pkg/azure/azure_test.go +++ b/pkg/azure/azure_test.go @@ -6,6 +6,7 @@ import ( "os" "testing" + "github.com/grafana/cloudcost-exporter/cmd/exporter/config" mock_provider "github.com/grafana/cloudcost-exporter/pkg/provider/mocks" "github.com/prometheus/client_golang/prometheus" "github.com/stretchr/testify/assert" @@ -19,6 +20,7 @@ var ( ) func Test_New(t *testing.T) { + commonConfig := &config.CommonConfig{ComputeInstanceLabel: "instance"} for _, tc := range []struct { name string expectedError error @@ -32,6 +34,7 @@ func Test_New(t *testing.T) { t.Run(tc.name, func(t *testing.T) { a, err := New(parentCtx, &Config{ Logger: testLogger, + CommonConfig: commonConfig, SubscriptionId: tc.subscriptionId, }) if tc.expectedError != nil { diff --git a/pkg/google/compute/compute.go b/pkg/google/compute/compute.go index 65416037..9646693d 100644 --- a/pkg/google/compute/compute.go +++ b/pkg/google/compute/compute.go @@ -14,6 +14,7 @@ import ( "google.golang.org/api/compute/v1" cloudcost_exporter "github.com/grafana/cloudcost-exporter" + "github.com/grafana/cloudcost-exporter/cmd/exporter/config" "github.com/grafana/cloudcost-exporter/pkg/google/billing" "github.com/grafana/cloudcost-exporter/pkg/provider" ) @@ -26,32 +27,18 @@ var ( ListInstancesError = errors.New("no list price was found for the sku") ) -var ( - NextScrapeDesc = prometheus.NewDesc( - prometheus.BuildFQName(cloudcost_exporter.ExporterName, subsystem, "next_scrape"), - "Next time GCP's compute submodule pricing map will be refreshed as unix timestamp", - nil, - nil, - ) - InstanceCPUHourlyCostDesc = prometheus.NewDesc( - prometheus.BuildFQName(cloudcost_exporter.MetricPrefix, subsystem, "instance_cpu_usd_per_core_hour"), - "The cpu cost a GCP Compute Instance in USD/(core*h)", - []string{"instance", "region", "family", "machine_type", "project", "price_tier"}, - nil, - ) - InstanceMemoryHourlyCostDesc = prometheus.NewDesc( - prometheus.BuildFQName(cloudcost_exporter.MetricPrefix, subsystem, "instance_ram_usd_per_gib_hour"), - "The memory cost of a GCP Compute Instance in USD/(GiB*h)", - []string{"instance", "region", "family", "machine_type", "project", "price_tier"}, - nil, - ) -) - type Config struct { + CommonConfig *config.CommonConfig Projects string ScrapeInterval time.Duration } +type CollectorMetrics struct { + NextScrapeDesc *prometheus.Desc + InstanceCPUHourlyCostDesc *prometheus.Desc + InstanceMemoryHourlyCostDesc *prometheus.Desc +} + // Collector implements the Collector interface for compute services in Compute. type Collector struct { computeService *compute.Service @@ -59,13 +46,37 @@ type Collector struct { PricingMap *StructuredPricingMap config *Config Projects []string + metrics *CollectorMetrics NextScrape time.Time } +func newCollectorMetrics(instanceLabel string) *CollectorMetrics { + return &CollectorMetrics{ + NextScrapeDesc: prometheus.NewDesc( + prometheus.BuildFQName(cloudcost_exporter.ExporterName, subsystem, "next_scrape"), + "Next time GCP's compute submodule pricing map will be refreshed as unix timestamp", + nil, + nil, + ), + InstanceCPUHourlyCostDesc: prometheus.NewDesc( + prometheus.BuildFQName(cloudcost_exporter.MetricPrefix, subsystem, "instance_cpu_usd_per_core_hour"), + "The cpu cost a GCP Compute Instance in USD/(core*h)", + []string{instanceLabel, "region", "family", "machine_type", "project", "price_tier"}, + nil, + ), + InstanceMemoryHourlyCostDesc: prometheus.NewDesc( + prometheus.BuildFQName(cloudcost_exporter.MetricPrefix, subsystem, "instance_ram_usd_per_gib_hour"), + "The memory cost of a GCP Compute Instance in USD/(GiB*h)", + []string{instanceLabel, "region", "family", "machine_type", "project", "price_tier"}, + nil, + ), + } +} + func (c *Collector) Describe(ch chan<- *prometheus.Desc) error { - ch <- NextScrapeDesc - ch <- InstanceCPUHourlyCostDesc - ch <- InstanceMemoryHourlyCostDesc + ch <- c.metrics.NextScrapeDesc + ch <- c.metrics.InstanceCPUHourlyCostDesc + ch <- c.metrics.InstanceMemoryHourlyCostDesc return nil } @@ -89,6 +100,7 @@ func New(config *Config, computeService *compute.Service, billingService *billin billingService: billingService, config: config, Projects: projects, + metrics: newCollectorMetrics(config.CommonConfig.ComputeInstanceLabel), } } @@ -152,7 +164,7 @@ func (c *Collector) CollectMetrics(ch chan<- prometheus.Metric) float64 { c.NextScrape = time.Now().Add(c.config.ScrapeInterval) log.Printf("Finished refreshing pricing map in %s", time.Since(start)) } - ch <- prometheus.MustNewConstMetric(NextScrapeDesc, prometheus.GaugeValue, float64(c.NextScrape.Unix())) + ch <- prometheus.MustNewConstMetric(c.metrics.NextScrapeDesc, prometheus.GaugeValue, float64(c.NextScrape.Unix())) for _, project := range c.Projects { zones, err := c.computeService.Zones.List(project).Do() if err != nil { @@ -190,7 +202,7 @@ func (c *Collector) CollectMetrics(ch chan<- prometheus.Metric) float64 { continue } ch <- prometheus.MustNewConstMetric( - InstanceCPUHourlyCostDesc, + c.metrics.InstanceCPUHourlyCostDesc, prometheus.GaugeValue, cpuCost, instance.Instance, @@ -199,7 +211,7 @@ func (c *Collector) CollectMetrics(ch chan<- prometheus.Metric) float64 { instance.MachineType, project, instance.PriceTier) - ch <- prometheus.MustNewConstMetric(InstanceMemoryHourlyCostDesc, + ch <- prometheus.MustNewConstMetric(c.metrics.InstanceMemoryHourlyCostDesc, prometheus.GaugeValue, ramCost, instance.Instance, diff --git a/pkg/google/compute/compute_test.go b/pkg/google/compute/compute_test.go index bbe0aec2..74b87bc9 100644 --- a/pkg/google/compute/compute_test.go +++ b/pkg/google/compute/compute_test.go @@ -22,6 +22,7 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" + "github.com/grafana/cloudcost-exporter/cmd/exporter/config" "github.com/grafana/cloudcost-exporter/pkg/google/billing" "github.com/grafana/cloudcost-exporter/pkg/utils" ) @@ -31,6 +32,7 @@ var collector *Collector func TestMain(m *testing.M) { ctx := context.Background() + commonConfig := &config.CommonConfig{ComputeInstanceLabel: "instance"} computeService, err := compute.NewService(ctx) if err != nil { // We silently fail here so that CI works. @@ -45,7 +47,8 @@ func TestMain(m *testing.M) { log.Printf("Error creating billing billingService: %s", err) } collector = New(&Config{ - Projects: "some_project", + CommonConfig: commonConfig, + Projects: "some_project", }, computeService, billingService) code := m.Run() os.Exit(code) @@ -123,6 +126,8 @@ func TestNewMachineSpec(t *testing.T) { } func TestCollector_Collect(t *testing.T) { + instanceLabel := "node" + commonConfig := &config.CommonConfig{ComputeInstanceLabel: instanceLabel} tests := map[string]struct { config *Config testServer *httptest.Server @@ -132,7 +137,8 @@ func TestCollector_Collect(t *testing.T) { }{ "Handle http error": { config: &Config{ - Projects: "testing", + CommonConfig: commonConfig, + Projects: "testing", }, testServer: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusInternalServerError) @@ -143,7 +149,8 @@ func TestCollector_Collect(t *testing.T) { }, "Parse out regular response": { config: &Config{ - Projects: "testing,testing-1", + CommonConfig: commonConfig, + Projects: "testing,testing-1", }, collectResponse: 1.0, expectedMetrics: []*utils.MetricResult{ @@ -151,7 +158,7 @@ func TestCollector_Collect(t *testing.T) { FqName: "cloudcost_gcp_compute_instance_cpu_usd_per_core_hour", Labels: map[string]string{ "family": "n1", - "instance": "test-n1", + instanceLabel: "test-n1", "machine_type": "n1-slim", "price_tier": "ondemand", "project": "testing", @@ -164,7 +171,7 @@ func TestCollector_Collect(t *testing.T) { FqName: "cloudcost_gcp_compute_instance_ram_usd_per_gib_hour", Labels: map[string]string{ "family": "n1", - "instance": "test-n1", + instanceLabel: "test-n1", "machine_type": "n1-slim", "price_tier": "ondemand", "project": "testing", @@ -177,7 +184,7 @@ func TestCollector_Collect(t *testing.T) { FqName: "cloudcost_gcp_compute_instance_cpu_usd_per_core_hour", Labels: map[string]string{ "family": "n2", - "instance": "test-n2", + instanceLabel: "test-n2", "machine_type": "n2-slim", "price_tier": "ondemand", "project": "testing", @@ -190,7 +197,7 @@ func TestCollector_Collect(t *testing.T) { FqName: "cloudcost_gcp_compute_instance_ram_usd_per_gib_hour", Labels: map[string]string{ "family": "n2", - "instance": "test-n2", + instanceLabel: "test-n2", "machine_type": "n2-slim", "price_tier": "ondemand", "project": "testing", @@ -203,7 +210,7 @@ func TestCollector_Collect(t *testing.T) { FqName: "cloudcost_gcp_compute_instance_cpu_usd_per_core_hour", Labels: map[string]string{ "family": "n1", - "instance": "test-n1-spot", + instanceLabel: "test-n1-spot", "machine_type": "n1-slim", "price_tier": "spot", "project": "testing", @@ -216,7 +223,7 @@ func TestCollector_Collect(t *testing.T) { FqName: "cloudcost_gcp_compute_instance_ram_usd_per_gib_hour", Labels: map[string]string{ "family": "n1", - "instance": "test-n1-spot", + instanceLabel: "test-n1-spot", "machine_type": "n1-slim", "price_tier": "spot", "project": "testing", @@ -229,7 +236,7 @@ func TestCollector_Collect(t *testing.T) { FqName: "cloudcost_gcp_compute_instance_cpu_usd_per_core_hour", Labels: map[string]string{ "family": "n2", - "instance": "test-n2-us-east1", + instanceLabel: "test-n2-us-east1", "machine_type": "n2-slim", "price_tier": "ondemand", "project": "testing", @@ -242,7 +249,7 @@ func TestCollector_Collect(t *testing.T) { FqName: "cloudcost_gcp_compute_instance_ram_usd_per_gib_hour", Labels: map[string]string{ "family": "n2", - "instance": "test-n2-us-east1", + instanceLabel: "test-n2-us-east1", "machine_type": "n2-slim", "price_tier": "ondemand", "project": "testing", @@ -255,7 +262,7 @@ func TestCollector_Collect(t *testing.T) { FqName: "cloudcost_gcp_compute_instance_cpu_usd_per_core_hour", Labels: map[string]string{ "family": "n1", - "instance": "test-n1", + instanceLabel: "test-n1", "machine_type": "n1-slim", "price_tier": "ondemand", "project": "testing-1", @@ -268,7 +275,7 @@ func TestCollector_Collect(t *testing.T) { FqName: "cloudcost_gcp_compute_instance_ram_usd_per_gib_hour", Labels: map[string]string{ "family": "n1", - "instance": "test-n1", + instanceLabel: "test-n1", "machine_type": "n1-slim", "price_tier": "ondemand", "project": "testing-1", @@ -281,7 +288,7 @@ func TestCollector_Collect(t *testing.T) { FqName: "cloudcost_gcp_compute_instance_cpu_usd_per_core_hour", Labels: map[string]string{ "family": "n2", - "instance": "test-n2", + instanceLabel: "test-n2", "machine_type": "n2-slim", "price_tier": "ondemand", "project": "testing-1", @@ -294,7 +301,7 @@ func TestCollector_Collect(t *testing.T) { FqName: "cloudcost_gcp_compute_instance_ram_usd_per_gib_hour", Labels: map[string]string{ "family": "n2", - "instance": "test-n2", + instanceLabel: "test-n2", "machine_type": "n2-slim", "price_tier": "ondemand", "project": "testing-1", @@ -307,7 +314,7 @@ func TestCollector_Collect(t *testing.T) { FqName: "cloudcost_gcp_compute_instance_cpu_usd_per_core_hour", Labels: map[string]string{ "family": "n1", - "instance": "test-n1-spot", + instanceLabel: "test-n1-spot", "machine_type": "n1-slim", "price_tier": "spot", "project": "testing-1", @@ -320,7 +327,7 @@ func TestCollector_Collect(t *testing.T) { FqName: "cloudcost_gcp_compute_instance_ram_usd_per_gib_hour", Labels: map[string]string{ "family": "n1", - "instance": "test-n1-spot", + instanceLabel: "test-n1-spot", "machine_type": "n1-slim", "price_tier": "spot", "project": "testing-1", @@ -333,7 +340,7 @@ func TestCollector_Collect(t *testing.T) { FqName: "cloudcost_gcp_compute_instance_cpu_usd_per_core_hour", Labels: map[string]string{ "family": "n2", - "instance": "test-n2-us-east1", + instanceLabel: "test-n2-us-east1", "machine_type": "n2-slim", "price_tier": "ondemand", "project": "testing-1", @@ -346,7 +353,7 @@ func TestCollector_Collect(t *testing.T) { FqName: "cloudcost_gcp_compute_instance_ram_usd_per_gib_hour", Labels: map[string]string{ "family": "n2", - "instance": "test-n2-us-east1", + instanceLabel: "test-n2-us-east1", "machine_type": "n2-slim", "price_tier": "ondemand", "project": "testing-1", @@ -457,6 +464,7 @@ func TestCollector_Collect(t *testing.T) { } func TestCollector_GetPricing(t *testing.T) { + commonConfig := &config.CommonConfig{ComputeInstanceLabel: "instance"} testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var buf interface{} switch r.URL.Path { @@ -513,7 +521,8 @@ func TestCollector_GetPricing(t *testing.T) { require.NoError(t, err) // Create the collector with a nil billing service so we can override it on each test case collector := New(&Config{ - Projects: "testing", + CommonConfig: commonConfig, + Projects: "testing", }, computeService, nil) var pricingMap *StructuredPricingMap diff --git a/pkg/google/gcp.go b/pkg/google/gcp.go index ec8ddd82..4000d6e1 100644 --- a/pkg/google/gcp.go +++ b/pkg/google/gcp.go @@ -15,6 +15,7 @@ import ( computev1 "google.golang.org/api/compute/v1" cloudcost_exporter "github.com/grafana/cloudcost-exporter" + "github.com/grafana/cloudcost-exporter/cmd/exporter/config" "github.com/grafana/cloudcost-exporter/pkg/google/compute" "github.com/grafana/cloudcost-exporter/pkg/google/gcs" "github.com/grafana/cloudcost-exporter/pkg/google/gke" @@ -91,6 +92,7 @@ type Config struct { Services []string ScrapeInterval time.Duration DefaultDiscount int + CommonConfig *config.CommonConfig Logger *slog.Logger } @@ -143,11 +145,13 @@ func New(config *Config) (*GCP, error) { } case "COMPUTE": collector = compute.New(&compute.Config{ + CommonConfig: config.CommonConfig, Projects: config.Projects, ScrapeInterval: config.ScrapeInterval, }, computeService, cloudCatalogClient) case "GKE": collector = gke.New(&gke.Config{ + CommonConfig: config.CommonConfig, Projects: config.Projects, ScrapeInterval: config.ScrapeInterval, }, computeService, cloudCatalogClient) diff --git a/pkg/google/gke/gke.go b/pkg/google/gke/gke.go index c09f571c..8ce5ed53 100644 --- a/pkg/google/gke/gke.go +++ b/pkg/google/gke/gke.go @@ -12,6 +12,7 @@ import ( "github.com/prometheus/client_golang/prometheus" "google.golang.org/api/compute/v1" + "github.com/grafana/cloudcost-exporter/cmd/exporter/config" "github.com/grafana/cloudcost-exporter/pkg/google/billing" gcpCompute "github.com/grafana/cloudcost-exporter/pkg/google/compute" @@ -23,31 +24,14 @@ const ( subsystem = "gcp_gke" ) -var ( - gkeNodeMemoryHourlyCostDesc = prometheus.NewDesc( - prometheus.BuildFQName(cloudcostexporter.MetricPrefix, subsystem, "instance_memory_usd_per_gib_hour"), - - "The cpu cost a GKE Instance in USD/(core*h)", - // Cannot simply do cluster because many metric scrapers will add a label for cluster and would interfere with the label we want to add - []string{"cluster_name", "instance", "region", "family", "machine_type", "project", "price_tier"}, - nil, - ) - gkeNodeCPUHourlyCostDesc = prometheus.NewDesc( - prometheus.BuildFQName(cloudcostexporter.MetricPrefix, subsystem, "instance_cpu_usd_per_core_hour"), - "The memory cost of a GKE Instance in USD/(GiB*h)", - // Cannot simply do cluster because many metric scrapers will add a label for cluster and would interfere with the label we want to add - []string{"cluster_name", "instance", "region", "family", "machine_type", "project", "price_tier"}, - nil, - ) - persistentVolumeHourlyCostDesc = prometheus.NewDesc( - prometheus.BuildFQName(cloudcostexporter.MetricPrefix, subsystem, "persistent_volume_usd_per_hour"), - "The cost of a GKE Persistent Volume in USD.", - []string{"cluster_name", "namespace", "persistentvolume", "region", "project", "storage_class", "disk_type"}, - nil, - ) -) +type CollectorMetrics struct { + gkeNodeMemoryHourlyCostDesc *prometheus.Desc + gkeNodeCPUHourlyCostDesc *prometheus.Desc + persistentVolumeHourlyCostDesc *prometheus.Desc +} type Config struct { + CommonConfig *config.CommonConfig Projects string ScrapeInterval time.Duration } @@ -58,9 +42,35 @@ type Collector struct { config *Config Projects []string ComputePricingMap *gcpCompute.StructuredPricingMap + metrics *CollectorMetrics NextScrape time.Time } +func newCollectorMetrics(instanceLabel string) *CollectorMetrics { + return &CollectorMetrics{ + gkeNodeMemoryHourlyCostDesc: prometheus.NewDesc( + prometheus.BuildFQName(cloudcostexporter.MetricPrefix, subsystem, "instance_memory_usd_per_gib_hour"), + + "The cpu cost a GKE Instance in USD/(core*h)", + // Cannot simply do cluster because many metric scrapers will add a label for cluster and would interfere with the label we want to add + []string{"cluster_name", instanceLabel, "region", "family", "machine_type", "project", "price_tier"}, + nil, + ), + gkeNodeCPUHourlyCostDesc: prometheus.NewDesc( + prometheus.BuildFQName(cloudcostexporter.MetricPrefix, subsystem, "instance_cpu_usd_per_core_hour"), + "The memory cost of a GKE Instance in USD/(GiB*h)", + // Cannot simply do cluster because many metric scrapers will add a label for cluster and would interfere with the label we want to add + []string{"cluster_name", instanceLabel, "region", "family", "machine_type", "project", "price_tier"}, + nil, + ), + persistentVolumeHourlyCostDesc: prometheus.NewDesc( + prometheus.BuildFQName(cloudcostexporter.MetricPrefix, subsystem, "persistent_volume_usd_per_hour"), + "The cost of a GKE Persistent Volume in USD.", + []string{"cluster_name", "namespace", "persistentvolume", "region", "project", "storage_class", "disk_type"}, + nil, + ), + } +} func (c *Collector) Register(_ provider.Registry) error { return nil } @@ -156,13 +166,13 @@ func (c *Collector) Collect(ch chan<- prometheus.Metric) error { return err } ch <- prometheus.MustNewConstMetric( - gkeNodeCPUHourlyCostDesc, + c.metrics.gkeNodeCPUHourlyCostDesc, prometheus.GaugeValue, cpuCost, labelValues..., ) ch <- prometheus.MustNewConstMetric( - gkeNodeMemoryHourlyCostDesc, + c.metrics.gkeNodeMemoryHourlyCostDesc, prometheus.GaugeValue, ramCost, labelValues..., @@ -196,7 +206,7 @@ func (c *Collector) Collect(ch chan<- prometheus.Metric) error { continue } ch <- prometheus.MustNewConstMetric( - persistentVolumeHourlyCostDesc, + c.metrics.persistentVolumeHourlyCostDesc, prometheus.GaugeValue, float64(d.Size)*price, labelValues..., @@ -214,6 +224,7 @@ func New(config *Config, computeService *compute.Service, billingService *billin billingService: billingService, config: config, Projects: projects, + metrics: newCollectorMetrics(config.CommonConfig.ComputeInstanceLabel), } } @@ -222,8 +233,8 @@ func (c *Collector) Name() string { } func (c *Collector) Describe(ch chan<- *prometheus.Desc) error { - ch <- gkeNodeCPUHourlyCostDesc - ch <- gkeNodeMemoryHourlyCostDesc + ch <- c.metrics.gkeNodeCPUHourlyCostDesc + ch <- c.metrics.gkeNodeMemoryHourlyCostDesc return nil } diff --git a/pkg/google/gke/gke_test.go b/pkg/google/gke/gke_test.go index 2711092a..33fd99aa 100644 --- a/pkg/google/gke/gke_test.go +++ b/pkg/google/gke/gke_test.go @@ -18,12 +18,15 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" + "github.com/grafana/cloudcost-exporter/cmd/exporter/config" "github.com/grafana/cloudcost-exporter/pkg/google/billing" "github.com/grafana/cloudcost-exporter/pkg/google/compute" "github.com/grafana/cloudcost-exporter/pkg/utils" ) func TestCollector_Collect(t *testing.T) { + instanceLabel := "node" + commonConfig := &config.CommonConfig{ComputeInstanceLabel: instanceLabel} tests := map[string]struct { config *Config testServer *httptest.Server @@ -33,7 +36,8 @@ func TestCollector_Collect(t *testing.T) { }{ "Handle http error": { config: &Config{ - Projects: "testing", + CommonConfig: commonConfig, + Projects: "testing", }, testServer: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusInternalServerError) @@ -44,7 +48,8 @@ func TestCollector_Collect(t *testing.T) { }, "Parse our regular response": { config: &Config{ - Projects: "testing,testing-1", + CommonConfig: commonConfig, + Projects: "testing,testing-1", }, collectResponse: 1.0, expectedMetrics: []*utils.MetricResult{ @@ -53,7 +58,7 @@ func TestCollector_Collect(t *testing.T) { FqName: "cloudcost_gcp_gke_instance_cpu_usd_per_core_hour", Labels: map[string]string{ "family": "n1", - "instance": "test-n1", + instanceLabel: "test-n1", "machine_type": "n1-slim", "price_tier": "ondemand", "project": "testing", @@ -67,7 +72,7 @@ func TestCollector_Collect(t *testing.T) { FqName: "cloudcost_gcp_gke_instance_memory_usd_per_gib_hour", Labels: map[string]string{ "family": "n1", - "instance": "test-n1", + instanceLabel: "test-n1", "machine_type": "n1-slim", "price_tier": "ondemand", "project": "testing", @@ -81,7 +86,7 @@ func TestCollector_Collect(t *testing.T) { FqName: "cloudcost_gcp_gke_instance_cpu_usd_per_core_hour", Labels: map[string]string{ "family": "n2", - "instance": "test-n2", + instanceLabel: "test-n2", "machine_type": "n2-slim", "price_tier": "ondemand", "project": "testing", @@ -95,7 +100,7 @@ func TestCollector_Collect(t *testing.T) { FqName: "cloudcost_gcp_gke_instance_memory_usd_per_gib_hour", Labels: map[string]string{ "family": "n2", - "instance": "test-n2", + instanceLabel: "test-n2", "machine_type": "n2-slim", "price_tier": "ondemand", "project": "testing", @@ -109,7 +114,7 @@ func TestCollector_Collect(t *testing.T) { FqName: "cloudcost_gcp_gke_instance_cpu_usd_per_core_hour", Labels: map[string]string{ "family": "n1", - "instance": "test-n1-spot", + instanceLabel: "test-n1-spot", "machine_type": "n1-slim", "price_tier": "spot", "project": "testing", @@ -123,7 +128,7 @@ func TestCollector_Collect(t *testing.T) { FqName: "cloudcost_gcp_gke_instance_memory_usd_per_gib_hour", Labels: map[string]string{ "family": "n1", - "instance": "test-n1-spot", + instanceLabel: "test-n1-spot", "machine_type": "n1-slim", "price_tier": "spot", "project": "testing", @@ -137,7 +142,7 @@ func TestCollector_Collect(t *testing.T) { FqName: "cloudcost_gcp_gke_instance_cpu_usd_per_core_hour", Labels: map[string]string{ "family": "n2", - "instance": "test-n2-us-east1", + instanceLabel: "test-n2-us-east1", "machine_type": "n2-slim", "price_tier": "ondemand", "project": "testing", @@ -151,7 +156,7 @@ func TestCollector_Collect(t *testing.T) { FqName: "cloudcost_gcp_gke_instance_memory_usd_per_gib_hour", Labels: map[string]string{ "family": "n2", - "instance": "test-n2-us-east1", + instanceLabel: "test-n2-us-east1", "machine_type": "n2-slim", "price_tier": "ondemand", "project": "testing", @@ -194,7 +199,7 @@ func TestCollector_Collect(t *testing.T) { FqName: "cloudcost_gcp_gke_instance_cpu_usd_per_core_hour", Labels: map[string]string{ "family": "n1", - "instance": "test-n1", + instanceLabel: "test-n1", "machine_type": "n1-slim", "price_tier": "ondemand", "project": "testing-1", @@ -208,7 +213,7 @@ func TestCollector_Collect(t *testing.T) { FqName: "cloudcost_gcp_gke_instance_memory_usd_per_gib_hour", Labels: map[string]string{ "family": "n1", - "instance": "test-n1", + instanceLabel: "test-n1", "machine_type": "n1-slim", "price_tier": "ondemand", "project": "testing-1", @@ -222,7 +227,7 @@ func TestCollector_Collect(t *testing.T) { FqName: "cloudcost_gcp_gke_instance_cpu_usd_per_core_hour", Labels: map[string]string{ "family": "n2", - "instance": "test-n2", + instanceLabel: "test-n2", "machine_type": "n2-slim", "price_tier": "ondemand", "project": "testing-1", @@ -236,7 +241,7 @@ func TestCollector_Collect(t *testing.T) { FqName: "cloudcost_gcp_gke_instance_memory_usd_per_gib_hour", Labels: map[string]string{ "family": "n2", - "instance": "test-n2", + instanceLabel: "test-n2", "machine_type": "n2-slim", "price_tier": "ondemand", "project": "testing-1", @@ -250,7 +255,7 @@ func TestCollector_Collect(t *testing.T) { FqName: "cloudcost_gcp_gke_instance_cpu_usd_per_core_hour", Labels: map[string]string{ "family": "n1", - "instance": "test-n1-spot", + instanceLabel: "test-n1-spot", "machine_type": "n1-slim", "price_tier": "spot", "project": "testing-1", @@ -264,7 +269,7 @@ func TestCollector_Collect(t *testing.T) { FqName: "cloudcost_gcp_gke_instance_memory_usd_per_gib_hour", Labels: map[string]string{ "family": "n1", - "instance": "test-n1-spot", + instanceLabel: "test-n1-spot", "machine_type": "n1-slim", "price_tier": "spot", "project": "testing-1", @@ -278,7 +283,7 @@ func TestCollector_Collect(t *testing.T) { FqName: "cloudcost_gcp_gke_instance_cpu_usd_per_core_hour", Labels: map[string]string{ "family": "n2", - "instance": "test-n2-us-east1", + instanceLabel: "test-n2-us-east1", "machine_type": "n2-slim", "price_tier": "ondemand", "project": "testing-1", @@ -292,7 +297,7 @@ func TestCollector_Collect(t *testing.T) { FqName: "cloudcost_gcp_gke_instance_memory_usd_per_gib_hour", Labels: map[string]string{ "family": "n2", - "instance": "test-n2-us-east1", + instanceLabel: "test-n2-us-east1", "machine_type": "n2-slim", "price_tier": "ondemand", "project": "testing-1",