From 66c6c61918d4e75ef7b3c3f232b6c02291ac348f Mon Sep 17 00:00:00 2001 From: Cristian Magherusan-Stanciu Date: Tue, 20 Oct 2026 07:43:49 +0200 Subject: [PATCH 1/2] feat(common): add shared HoursPerYear constant Part of #109 Co-Authored-By: Claude Opus 5.5 --- pkg/common/hours.go | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 pkg/common/hours.go diff --git a/pkg/common/hours.go b/pkg/common/hours.go new file mode 100644 index 00000000..58d85b9a --- /dev/null +++ b/pkg/common/hours.go @@ -0,0 +1,5 @@ +package common //nolint:revive // pkg/common is the module's core domain package; see breakeven.go + +// HoursPerYear is the 365-day year length (24 * 365) used to convert a +// reservation term in years into billable hours. +const HoursPerYear = 8760.0 From a1598c225900cf4a85a6a0f50db9cba23268bd28 Mon Sep 17 00:00:00 2001 From: Cristian Magherusan-Stanciu Date: Tue, 20 Oct 2026 07:56:29 +0200 Subject: [PATCH 2/2] refactor(azure,gcp): use common.HoursPerYear instead of 8760 literals Replace the bare 8760 term-hours literals in the azure cache, compute, cosmosdb, database, managedredis, search and synapse clients and the gcp computeengine pricing helper (which carried its own copy) with the shared constant, and pin pkg to the commit that adds it. No behavior change. Part of #109 Co-Authored-By: Claude Opus 5.5 --- providers/azure/go.mod | 2 +- providers/azure/go.sum | 4 ++-- providers/azure/services/cache/client.go | 2 +- providers/azure/services/compute/client.go | 2 +- providers/azure/services/cosmosdb/client.go | 2 +- providers/azure/services/database/client.go | 2 +- providers/azure/services/managedredis/client.go | 2 +- providers/azure/services/search/client.go | 2 +- providers/azure/services/synapse/client.go | 2 +- providers/gcp/go.mod | 2 +- providers/gcp/go.sum | 4 ++-- providers/gcp/services/computeengine/pricing.go | 4 +--- 12 files changed, 14 insertions(+), 16 deletions(-) diff --git a/providers/azure/go.mod b/providers/azure/go.mod index 4ea54ae5..d93eb608 100644 --- a/providers/azure/go.mod +++ b/providers/azure/go.mod @@ -15,7 +15,7 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armsubscriptions v1.3.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/search/armsearch v1.4.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/sql/armsql v1.2.0 - github.com/LeanerCloud/cloud-commitments-go/pkg v0.0.0-20261017001455-7383c3a7db7e + github.com/LeanerCloud/cloud-commitments-go/pkg v0.0.0-20261020054349-66c6c61918d4 github.com/stretchr/testify v1.11.1 golang.org/x/sync v0.23.0 ) diff --git a/providers/azure/go.sum b/providers/azure/go.sum index 932b78b5..de850a1a 100644 --- a/providers/azure/go.sum +++ b/providers/azure/go.sum @@ -38,8 +38,8 @@ github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJ github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE= github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 h1:XRzhVemXdgvJqCH0sFfrBUTnUJSBrBf7++ypk+twtRs= github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk= -github.com/LeanerCloud/cloud-commitments-go/pkg v0.0.0-20261017001455-7383c3a7db7e h1:SeuL9mqUKVNiDa4yXU9IdaKKlwNo/iaaDmDXMHClw+o= -github.com/LeanerCloud/cloud-commitments-go/pkg v0.0.0-20261017001455-7383c3a7db7e/go.mod h1:SCDEgHG6evjPh0RsoXof+J3aMmGXNFMrwbJIle7rMpQ= +github.com/LeanerCloud/cloud-commitments-go/pkg v0.0.0-20261020054349-66c6c61918d4 h1:8ZnV+uLXITP5oTimNlsUsB0KMW5sf4JgEMKEO0DOTCs= +github.com/LeanerCloud/cloud-commitments-go/pkg v0.0.0-20261020054349-66c6c61918d4/go.mod h1:SCDEgHG6evjPh0RsoXof+J3aMmGXNFMrwbJIle7rMpQ= github.com/aws/aws-sdk-go-v2 v1.41.5 h1:dj5kopbwUsVUVFgO4Fi5BIT3t4WyqIDjGKCangnV/yY= github.com/aws/aws-sdk-go-v2 v1.41.5/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o= github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng= diff --git a/providers/azure/services/cache/client.go b/providers/azure/services/cache/client.go index c21f809b..7cbc96f5 100644 --- a/providers/azure/services/cache/client.go +++ b/providers/azure/services/cache/client.go @@ -581,7 +581,7 @@ func (c *Client) getRedisPricing(ctx context.Context, sku, region string, termYe return nil, err } return &RedisPricing{ - HourlyRate: selected.RetailPrice / (8760 * float64(termYears)), + HourlyRate: selected.RetailPrice / (common.HoursPerYear * float64(termYears)), ReservationPrice: selected.RetailPrice, Currency: selected.CurrencyCode, }, nil diff --git a/providers/azure/services/compute/client.go b/providers/azure/services/compute/client.go index 44bf6ff5..2f25efa8 100644 --- a/providers/azure/services/compute/client.go +++ b/providers/azure/services/compute/client.go @@ -710,7 +710,7 @@ func (c *Client) getVMPricing(ctx context.Context, vmSize, region string, termYe } return &VMPricing{ - HourlyRate: item.RetailPrice / (8760.0 * float64(termYears)), + HourlyRate: item.RetailPrice / (common.HoursPerYear * float64(termYears)), ReservationPrice: item.RetailPrice, Currency: item.CurrencyCode, }, nil diff --git a/providers/azure/services/cosmosdb/client.go b/providers/azure/services/cosmosdb/client.go index d83a3a87..0d149f16 100644 --- a/providers/azure/services/cosmosdb/client.go +++ b/providers/azure/services/cosmosdb/client.go @@ -578,7 +578,7 @@ func (c *Client) getCosmosPricing(ctx context.Context, sku, region string, termY } return &CosmosPricing{ - HourlyRate: selected.RetailPrice / (8760.0 * float64(termYears)), + HourlyRate: selected.RetailPrice / (common.HoursPerYear * float64(termYears)), ReservationPrice: selected.RetailPrice, Currency: selected.CurrencyCode, }, nil diff --git a/providers/azure/services/database/client.go b/providers/azure/services/database/client.go index bee41042..1955d3d2 100644 --- a/providers/azure/services/database/client.go +++ b/providers/azure/services/database/client.go @@ -605,7 +605,7 @@ func (c *Client) getSQLPricing(ctx context.Context, sku, region string, termYear } return &SQLPricing{ - HourlyRate: reservation.RetailPrice / (8760.0 * float64(termYears)), + HourlyRate: reservation.RetailPrice / (common.HoursPerYear * float64(termYears)), ReservationPrice: reservation.RetailPrice, Currency: reservation.CurrencyCode, }, nil diff --git a/providers/azure/services/managedredis/client.go b/providers/azure/services/managedredis/client.go index 1e18b3cd..e258fcea 100644 --- a/providers/azure/services/managedredis/client.go +++ b/providers/azure/services/managedredis/client.go @@ -538,7 +538,7 @@ func (c *Client) getRedisPricing(ctx context.Context, sku, region string, termYe return nil, err } return &RedisPricing{ - HourlyRate: selected.RetailPrice / (8760 * float64(termYears)), + HourlyRate: selected.RetailPrice / (common.HoursPerYear * float64(termYears)), ReservationPrice: selected.RetailPrice, Currency: selected.CurrencyCode, }, nil diff --git a/providers/azure/services/search/client.go b/providers/azure/services/search/client.go index f73d7934..78b38cdb 100644 --- a/providers/azure/services/search/client.go +++ b/providers/azure/services/search/client.go @@ -466,7 +466,7 @@ func (c *Client) getSearchPricing(ctx context.Context, sku, region string, termY return nil, fmt.Errorf("azure search SKU %s in region %s: %w", sku, region, err) } - hoursInTerm := 8760.0 * float64(termYears) + hoursInTerm := common.HoursPerYear * float64(termYears) return &Pricing{ HourlyRate: selected.RetailPrice / hoursInTerm, ReservationPrice: selected.RetailPrice, diff --git a/providers/azure/services/synapse/client.go b/providers/azure/services/synapse/client.go index 351cc84f..b4a03b3c 100644 --- a/providers/azure/services/synapse/client.go +++ b/providers/azure/services/synapse/client.go @@ -478,7 +478,7 @@ func (c *Client) getSynapsePricing(ctx context.Context, sku, region string, term } return &Pricing{ - HourlyRate: selected.RetailPrice / (8760.0 * float64(termYears)), + HourlyRate: selected.RetailPrice / (common.HoursPerYear * float64(termYears)), ReservationPrice: selected.RetailPrice, Currency: selected.CurrencyCode, }, nil diff --git a/providers/gcp/go.mod b/providers/gcp/go.mod index 9380eeb0..70b297bd 100644 --- a/providers/gcp/go.mod +++ b/providers/gcp/go.mod @@ -8,7 +8,7 @@ require ( cloud.google.com/go/redis v1.18.3 cloud.google.com/go/resourcemanager v1.10.7 cloud.google.com/go/storage v1.59.2 - github.com/LeanerCloud/cloud-commitments-go/pkg v0.0.0-20261017012250-2bf23fbd8c2a + github.com/LeanerCloud/cloud-commitments-go/pkg v0.0.0-20261020054349-66c6c61918d4 github.com/googleapis/gax-go/v2 v2.21.0 github.com/stretchr/testify v1.11.1 golang.org/x/oauth2 v0.36.0 diff --git a/providers/gcp/go.sum b/providers/gcp/go.sum index 9983261f..06c7412a 100644 --- a/providers/gcp/go.sum +++ b/providers/gcp/go.sum @@ -36,8 +36,8 @@ github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0 github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.54.0/go.mod h1:vB2GH9GAYYJTO3mEn8oYwzEdhlayZIdQz6zdzgUIRvA= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.54.0 h1:s0WlVbf9qpvkh1c/uDAPElam0WrL7fHRIidgZJ7UqZI= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.54.0/go.mod h1:Mf6O40IAyB9zR/1J8nGDDPirZQQPbYJni8Yisy7NTMc= -github.com/LeanerCloud/cloud-commitments-go/pkg v0.0.0-20261017012250-2bf23fbd8c2a h1:TggoJtZRb3MHRRhJ8Q1uu5SGGmhL1YbHiG08Xv9hqDw= -github.com/LeanerCloud/cloud-commitments-go/pkg v0.0.0-20261017012250-2bf23fbd8c2a/go.mod h1:SCDEgHG6evjPh0RsoXof+J3aMmGXNFMrwbJIle7rMpQ= +github.com/LeanerCloud/cloud-commitments-go/pkg v0.0.0-20261020054349-66c6c61918d4 h1:8ZnV+uLXITP5oTimNlsUsB0KMW5sf4JgEMKEO0DOTCs= +github.com/LeanerCloud/cloud-commitments-go/pkg v0.0.0-20261020054349-66c6c61918d4/go.mod h1:SCDEgHG6evjPh0RsoXof+J3aMmGXNFMrwbJIle7rMpQ= github.com/aws/aws-sdk-go-v2 v1.41.5 h1:dj5kopbwUsVUVFgO4Fi5BIT3t4WyqIDjGKCangnV/yY= github.com/aws/aws-sdk-go-v2 v1.41.5/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o= github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng= diff --git a/providers/gcp/services/computeengine/pricing.go b/providers/gcp/services/computeengine/pricing.go index db4a215a..472ddb93 100644 --- a/providers/gcp/services/computeengine/pricing.go +++ b/providers/gcp/services/computeengine/pricing.go @@ -16,8 +16,6 @@ const ( // computeServiceID is the Cloud Billing catalog service for Compute Engine. computeServiceID = "services/6F81-5844-456A" - hoursPerYear = 8760.0 - usageUnitHour = "h" usageUnitGiBHour = "GiBy.h" @@ -298,7 +296,7 @@ func (c *Client) getComputePricing(ctx context.Context, machineType, region stri return nil, err } - hours := hoursPerYear * float64(termYears) + hours := common.HoursPerYear * float64(termYears) commitHourly := float64(res.vcpus)*rates.commitment.cpu + res.memoryGiB*rates.commitment.ram onDemandHourly := float64(res.vcpus)*rates.onDemand.cpu + res.memoryGiB*rates.onDemand.ram