Skip to content

Commit

Permalink
fix(gcp): Update cost calculation of PVC's to convert size to GiB (#154)
Browse files Browse the repository at this point in the history
Adds a new method to `Disks` to convert the disk size from GB to GiB and implements unit tests for said method.
Updates documentation and metric name to clarify that the metric already takes into account the size and cost of the disk. Other metrics export the raw cost per hour.

This is largely necessary because the pricing from GCP is in GiB, but the disk's size is reported in GB. 

- closes #153
  • Loading branch information
Pokom authored Apr 19, 2024
1 parent db257d8 commit ea1d557
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 7 deletions.
2 changes: 1 addition & 1 deletion docs/metrics/gcp/gke.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
|------------------------------------------------------------|-------------|---------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| cloudcost_gcp_gke_instance_cpu_usd_per_core_hour | Gauge | The processing cost of a GCP Compute Instance, associated to a GKE cluster, in USD/(core*h) | `cluster_name`=&lt;name of the cluster the instance is associated with&gt; <br/> `instance`=&lt;name of the compute instance&gt; <br/> `region`=&lt;GCP region code&gt; <br/> `family`=&lt;broader compute family (n1, n2, c3 ...) &gt; <br/> `machine_type`=&lt;specific machine type, e.g.: n2-standard-2&gt; <br/> `project`=&lt;GCP project, where the instance is provisioned&gt; <br/> `price_tier`=&lt;spot\|ondemand&gt; |
| cloudcost_gcp_gke_compute_instance_memory_usd_per_gib_hour | Gauge | The memory cost of a GCP Compute Instance, associated to a GKE cluster, in USD/(GiB*h) | `cluster_name`=&lt;name of the cluster the instance is associated with&gt; <br/> `instance`=&lt;name of the compute instance&gt; <br/> `region`=&lt;GCP region code&gt; <br/> `family`=&lt;broader compute family (n1, n2, c3 ...) &gt; <br/> `machine_type`=&lt;specific machine type, e.g.: n2-standard-2&gt; <br/> `project`=&lt;GCP project, where the instance is provisioned&gt; <br/> `price_tier`=&lt;spot\|ondemand&gt; |
| cloudcost_gcp_gke_persistent_volume_usd_per_gib_hour | Gauge | The cost of a GKE Persistent Volume in USD/(GiB*h) | `cluster_name`=&lt;name of the cluster the instance is associated with&gt; <br/> `namespace`=&lt;The namespace the pvc was created for&gt; <br/> `persistentvolume`=&lt;Name of the persistent volume&gt; <br/> `region`=&lt;The region the pvc was created in&gt; <br/> `project`=&lt;GCP project, where the instance is provisioned&gt; <br/> `storage_class`=&lt;pd-standard\|pd-ssd\|pd-balanced\|pd-extreme&gt; <br/> `disk_type`=&lt;boot_disk\|persistent_volume&gt; |
| cloudcost_gcp_gke_persistent_volume_usd_per_hour | Gauge | The cost of a GKE Persistent Volume in USD/(GiB*h) | `cluster_name`=&lt;name of the cluster the instance is associated with&gt; <br/> `namespace`=&lt;The namespace the pvc was created for&gt; <br/> `persistentvolume`=&lt;Name of the persistent volume&gt; <br/> `region`=&lt;The region the pvc was created in&gt; <br/> `project`=&lt;GCP project, where the instance is provisioned&gt; <br/> `storage_class`=&lt;pd-standard\|pd-ssd\|pd-balanced\|pd-extreme&gt; <br/> `disk_type`=&lt;boot_disk\|persistent_volume&gt; |

## Persistent Volumes

Expand Down
13 changes: 13 additions & 0 deletions pkg/google/gke/disk.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ type Disk struct {
labels map[string]string
description map[string]string
diskType string // type is a reserved word, which is why we're using diskType
Size int64
}

func NewDisk(disk *compute.Disk, project string) *Disk {
Expand All @@ -39,6 +40,7 @@ func NewDisk(disk *compute.Disk, project string) *Disk {
diskType: disk.Type,
labels: disk.Labels,
description: make(map[string]string),
Size: disk.SizeGb,
}
err := extractLabelsFromDesc(disk.Description, d.description)
if err != nil {
Expand Down Expand Up @@ -124,3 +126,14 @@ func (d Disk) DiskType() string {
}
return "persistent_volume"
}

// GBPerGIB is a helper const to convert from GB to GiB
// 1 << 30 is the number of bytes in a GiB
// 1e9 is the number of bytes in a GB
const GBPerGIB = 1e9 / (1 << 30)

// SizeInGib is used to convert the size of the disk from GigaBytes to GibiBytes. This is particularly important when
// calculating the cost of the disk since the pricing is in GiB.
func (d Disk) SizeInGib() float64 {
return float64(d.Size) * GBPerGIB
}
27 changes: 27 additions & 0 deletions pkg/google/gke/disk_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,3 +242,30 @@ func Test_DiskType(t *testing.T) {
})
}
}

func TestDisk_SizeInGib(t *testing.T) {
tests := map[string]struct {
disk *Disk
want float64
}{
"Disk with size 0 should return 0": {
disk: NewDisk(&computev1.Disk{
SizeGb: 0,
}, ""),
want: 0,
},
"Disk with size 1GB should return 1GiB": {
disk: NewDisk(&computev1.Disk{
SizeGb: 1,
}, ""),
want: 0.9313225746154785,
},
}
for name, tt := range tests {
t.Run(name, func(t *testing.T) {
if got := tt.disk.SizeInGib(); got != tt.want {
t.Errorf("SizeInGib() = %v, want %v", got, tt.want)
}
})
}
}
6 changes: 3 additions & 3 deletions pkg/google/gke/gke.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ var (
nil,
)
persistentVolumeHourlyCostDesc = prometheus.NewDesc(
prometheus.BuildFQName(cloudcostexporter.MetricPrefix, subsystem, "persistent_volume_usd_per_gib_hour"),
"The cost of a GKE Persistent Volume in USD/(GiB*h)",
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,
)
Expand Down Expand Up @@ -193,7 +193,7 @@ func (c *Collector) Collect(ch chan<- prometheus.Metric) error {
ch <- prometheus.MustNewConstMetric(
persistentVolumeHourlyCostDesc,
prometheus.GaugeValue,
float64(disk.SizeGb)*price,
d.SizeInGib()*price,
labelValues...,
)
}
Expand Down
6 changes: 3 additions & 3 deletions pkg/google/gke/gke_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ func TestCollector_Collect(t *testing.T) {
MetricType: prometheus.GaugeValue,
},
{
FqName: "cloudcost_gcp_gke_persistent_volume_usd_per_gib_hour",
FqName: "cloudcost_gcp_gke_persistent_volume_usd_per_hour",
Labels: map[string]string{
"cluster_name": "test",
"namespace": "cloudcost-exporter",
Expand All @@ -176,7 +176,7 @@ func TestCollector_Collect(t *testing.T) {
MetricType: prometheus.GaugeValue,
},
{
FqName: "cloudcost_gcp_gke_persistent_volume_usd_per_gib_hour",
FqName: "cloudcost_gcp_gke_persistent_volume_usd_per_hour",
Labels: map[string]string{
"cluster_name": "test",
"namespace": "cloudcost-exporter",
Expand All @@ -186,7 +186,7 @@ func TestCollector_Collect(t *testing.T) {
"storage_class": "pd-ssd",
"disk_type": "persistent_volume",
},
Value: 0.15359342915811086,
Value: 0.14304502788755194,
MetricType: prometheus.GaugeValue,
},
{
Expand Down

0 comments on commit ea1d557

Please sign in to comment.