From ef63b9cb47e15314dd67d2cbe0e518700bbbf499 Mon Sep 17 00:00:00 2001 From: Cristian Magherusan-Stanciu Date: Tue, 20 Oct 2026 17:16:56 +0200 Subject: [PATCH 1/3] chore(lint): reorder fieldalignment structs in providers/gcp Reorder struct fields to the fieldalignment-optimal layout. SQLPricing, StoragePricing, RedisPricing and the three service Client structs keep their order with a nolint because open PR #471 modifies those files. Part of #100 Co-Authored-By: Claude Opus 5.5 --- providers/gcp/internal/billingguard/billingguard.go | 2 +- providers/gcp/provider.go | 4 ++-- providers/gcp/recommendations.go | 6 +++--- providers/gcp/services/cloudsql/client.go | 4 ++-- providers/gcp/services/cloudstorage/client.go | 4 ++-- providers/gcp/services/computeengine/client.go | 11 ++++++----- providers/gcp/services/computeengine/pricing.go | 10 +++++----- providers/gcp/services/memorystore/client.go | 4 ++-- 8 files changed, 23 insertions(+), 22 deletions(-) diff --git a/providers/gcp/internal/billingguard/billingguard.go b/providers/gcp/internal/billingguard/billingguard.go index 0ce21ce9..ca76e6c4 100644 --- a/providers/gcp/internal/billingguard/billingguard.go +++ b/providers/gcp/internal/billingguard/billingguard.go @@ -19,8 +19,8 @@ const marker = "failed to create billing service" type watcher struct { next io.Writer - mu sync.Mutex hits []string + mu sync.Mutex } func (w *watcher) Write(p []byte) (int, error) { diff --git a/providers/gcp/provider.go b/providers/gcp/provider.go index 3973fea2..361a6c0a 100644 --- a/providers/gcp/provider.go +++ b/providers/gcp/provider.go @@ -93,11 +93,11 @@ func (r *realResourceManagerService) ListProjects(ctx context.Context) ([]*cloud // Provider implements the provider.Provider interface for Google Cloud Platform. type Provider struct { - projectID string - clientOpts []option.ClientOption projectsClient ProjectsClient regionsClient RegionsClient resourceManagerService ResourceManagerService + projectID string + clientOpts []option.ClientOption } // NewProvider creates a new GCP provider. diff --git a/providers/gcp/recommendations.go b/providers/gcp/recommendations.go index 0d6eab58..0d12a4cb 100644 --- a/providers/gcp/recommendations.go +++ b/providers/gcp/recommendations.go @@ -54,6 +54,7 @@ func gcpRegionConcurrency() int { // only an explicit request reaches those clients, which return // common.ErrCommitmentPurchaseNotSupported. type regionResult struct { + lastErr error compute []common.Recommendation // attempted counts the service calls launched for this region (after the // params service filter), failed counts how many of those errored, and @@ -61,15 +62,14 @@ type regionResult struct { // these across regions for the all-attempted-failed guard (COR-03). attempted int failed int - lastErr error } // RecommendationsClientAdapter aggregates GCP CUD and commitment recommendations across all services. type RecommendationsClientAdapter struct { + // regionsFn replaces the live region lookup; tests only. + regionsFn func(context.Context) ([]string, error) projectID string clientOpts []option.ClientOption - // regionsFn replaces the live region lookup; tests only. - regionsFn func(context.Context) ([]string, error) } // GetRecommendations retrieves all GCP commitment recommendations across all diff --git a/providers/gcp/services/cloudsql/client.go b/providers/gcp/services/cloudsql/client.go index 0771cd08..393dba95 100644 --- a/providers/gcp/services/cloudsql/client.go +++ b/providers/gcp/services/cloudsql/client.go @@ -47,7 +47,7 @@ type RecommenderClient interface { } // Client handles GCP Cloud SQL commitments. -type Client struct { +type Client struct { //nolint:govet // fieldalignment: file owned by open PR #471 skuMu sync.Mutex skuCache *cloudbilling.ListSkusResponse ctx context.Context @@ -258,7 +258,7 @@ func (c *Client) GetValidResourceTypes(ctx context.Context) ([]string, error) { } // SQLPricing contains pricing information for Cloud SQL. -type SQLPricing struct { +type SQLPricing struct { //nolint:govet // fieldalignment: file owned by open PR #471 HourlyRate float64 CommitmentPrice float64 OnDemandPrice float64 diff --git a/providers/gcp/services/cloudstorage/client.go b/providers/gcp/services/cloudstorage/client.go index 7054c2ef..3b7574b7 100644 --- a/providers/gcp/services/cloudstorage/client.go +++ b/providers/gcp/services/cloudstorage/client.go @@ -59,7 +59,7 @@ type BillingService interface { } // Client handles GCP Cloud Storage commitments. -type Client struct { +type Client struct { //nolint:govet // fieldalignment: file owned by open PR #471 ctx context.Context projectID string region string @@ -219,7 +219,7 @@ func (c *Client) GetValidResourceTypes(ctx context.Context) ([]string, error) { } // StoragePricing contains pricing information for Cloud Storage. -type StoragePricing struct { +type StoragePricing struct { //nolint:govet // fieldalignment: file owned by open PR #471 HourlyRate float64 CommitmentPrice float64 OnDemandPrice float64 diff --git a/providers/gcp/services/computeengine/client.go b/providers/gcp/services/computeengine/client.go index 970459fa..405a5cf2 100644 --- a/providers/gcp/services/computeengine/client.go +++ b/providers/gcp/services/computeengine/client.go @@ -260,16 +260,17 @@ type RecommenderClient interface { // Client handles GCP Compute Engine Committed Use Discounts. type Client struct { - projectID string - region string - clientOpts []option.ClientOption commitmentsService CommitmentsService machineTypesService MachineTypesService billingService BillingService recommenderClient RecommenderClient - skuMu sync.Mutex - skuCache []*cloudbilling.Sku + projectID string + region string + clientOpts []option.ClientOption + skuCache []*cloudbilling.Sku + + skuMu sync.Mutex } // NewClient creates a new GCP Compute Engine client. diff --git a/providers/gcp/services/computeengine/pricing.go b/providers/gcp/services/computeengine/pricing.go index 261603d8..17cc24c4 100644 --- a/providers/gcp/services/computeengine/pricing.go +++ b/providers/gcp/services/computeengine/pricing.go @@ -25,13 +25,13 @@ const ( // ComputePricing is the price of a commitment's resources, for one term. type ComputePricing struct { + Currency string // HourlyRate is the committed price per hour for the whole resource set. HourlyRate float64 // CommitmentPrice is the committed price over the whole term. CommitmentPrice float64 // OnDemandPrice is the on-demand price of the same resources over the term. OnDemandPrice float64 - Currency string SavingsPercentage float64 } @@ -97,8 +97,8 @@ type skuRates struct { } type familyRates struct { - onDemand, commitment skuRates currency string + onDemand, commitment skuRates } // onDemandDescription matches e.g. "N2 Instance Core running in Americas", @@ -127,11 +127,11 @@ func legacyCommitmentDescription(label string) *regexp.Regexp { // rateMatcher recognizes the catalog SKUs of one family, region and term. type rateMatcher struct { - region string onDemand *regexp.Regexp - commitments []*regexp.Regexp + region string wantTerm string wantUsage string + commitments []*regexp.Regexp } func newRateMatcher(labels skuFamilyLabels, region string, termYears int) rateMatcher { @@ -190,8 +190,8 @@ func (r *rateSlot) set(price float64, sku *cloudbilling.Sku) error { // rateCollector accumulates the four prices wanted from matched SKUs. type rateCollector struct { - odCPU, odRAM, cCPU, cRAM rateSlot currency string + odCPU, odRAM, cCPU, cRAM rateSlot } func (c *rateCollector) add(sku *cloudbilling.Sku, isCommit, isRAM bool) error { diff --git a/providers/gcp/services/memorystore/client.go b/providers/gcp/services/memorystore/client.go index 486623ad..fc39b8de 100644 --- a/providers/gcp/services/memorystore/client.go +++ b/providers/gcp/services/memorystore/client.go @@ -53,7 +53,7 @@ type RecommenderClient interface { } // Client handles GCP Memorystore (Redis) commitments. -type Client struct { +type Client struct { //nolint:govet // fieldalignment: file owned by open PR #471 skuMu sync.Mutex skuCache *cloudbilling.ListSkusResponse ctx context.Context @@ -221,7 +221,7 @@ func (c *Client) GetValidResourceTypes(ctx context.Context) ([]string, error) { } // RedisPricing contains pricing information for Memorystore Redis. -type RedisPricing struct { +type RedisPricing struct { //nolint:govet // fieldalignment: file owned by open PR #471 HourlyRate float64 CommitmentPrice float64 OnDemandPrice float64 From 777edfb2a482d38133ef890ec395b54b91e29d87 Mon Sep 17 00:00:00 2001 From: Cristian Magherusan-Stanciu Date: Tue, 20 Oct 2026 17:17:03 +0200 Subject: [PATCH 2/3] chore(lint): re-enable govet fieldalignment Remove fieldalignment from govet.disable now that pkg and every provider module pass. Findings in _test.go files are excluded by message text, so other govet checks still run in tests; test-table layout is irrelevant and reordering positional table literals risks silent misassignment. Mirrors cloud-commitments-cli#2262. Closes #100 Co-Authored-By: Claude Opus 5.5 --- .golangci.yml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 5f3e5276..ea64197c 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -52,12 +52,6 @@ linters: pattern: (?i)passwd|pass|password|pwd|secret|token govet: enable-all: true - disable: - # fieldalignment: many production structs have alignment violations that - # pre-date this PR; fixing them is a separate concern. Suppress globally - # rather than per-struct since golangci-lint v2.10.x (CI) doesn't treat - # severity:warning as a non-fatal exit, unlike v2.11+. - - fieldalignment misspell: locale: US revive: @@ -93,12 +87,17 @@ linters: # unusedwrite: test fixture struct fields may be set for documentation # completeness even when only a subset of fields is read by a given subtest. # Scoped to test files so production unusedwrite bugs remain visible. - # Note: fieldalignment is disabled globally in govet.disable (many - # pre-existing production violations; separate cleanup PR needed). - linters: - govet text: "unusedwrite" path: _test\.go + # fieldalignment: test-table memory layout is irrelevant, and reordering + # positional table literals risks silent misassignment. Production code + # stays checked. + - linters: + - govet + text: "fieldalignment:" + path: _test\.go - linters: - errcheck - gosec From e9e4556c53f1b91903c55b0c223cb0656ef68887 Mon Sep 17 00:00:00 2001 From: Cristian Magherusan-Stanciu Date: Tue, 20 Oct 2026 17:57:40 +0200 Subject: [PATCH 3/3] chore(lint): keep field comments attached after fieldalignment reorder in providers/gcp Rebuild each reordered struct body so every field keeps its own doc comment directly above it and group headers head only the fields they describe (repeated per contiguous run where the layout splits a group). Documented fields are blank-line separated so no undocumented field sits under another field comment. Part of #100 Co-Authored-By: Claude Opus 5.5 --- providers/gcp/recommendations.go | 9 ++++++++- providers/gcp/services/computeengine/client.go | 12 +++++------- providers/gcp/services/computeengine/pricing.go | 6 +++++- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/providers/gcp/recommendations.go b/providers/gcp/recommendations.go index 0d12a4cb..7ca1febd 100644 --- a/providers/gcp/recommendations.go +++ b/providers/gcp/recommendations.go @@ -54,8 +54,14 @@ func gcpRegionConcurrency() int { // only an explicit request reaches those clients, which return // common.ErrCommitmentPurchaseNotSupported. type regionResult struct { + // attempted counts the service calls launched for this region (after the + // params service filter), failed counts how many of those errored, and + // lastErr keeps one representative error. mergeRegionResults aggregates + // these across regions for the all-attempted-failed guard (COR-03). lastErr error + compute []common.Recommendation + // attempted counts the service calls launched for this region (after the // params service filter), failed counts how many of those errored, and // lastErr keeps one representative error. mergeRegionResults aggregates @@ -67,7 +73,8 @@ type regionResult struct { // RecommendationsClientAdapter aggregates GCP CUD and commitment recommendations across all services. type RecommendationsClientAdapter struct { // regionsFn replaces the live region lookup; tests only. - regionsFn func(context.Context) ([]string, error) + regionsFn func(context.Context) ([]string, error) + projectID string clientOpts []option.ClientOption } diff --git a/providers/gcp/services/computeengine/client.go b/providers/gcp/services/computeengine/client.go index 405a5cf2..c214b7c7 100644 --- a/providers/gcp/services/computeengine/client.go +++ b/providers/gcp/services/computeengine/client.go @@ -264,13 +264,11 @@ type Client struct { machineTypesService MachineTypesService billingService BillingService recommenderClient RecommenderClient - - projectID string - region string - clientOpts []option.ClientOption - skuCache []*cloudbilling.Sku - - skuMu sync.Mutex + projectID string + region string + clientOpts []option.ClientOption + skuCache []*cloudbilling.Sku + skuMu sync.Mutex } // NewClient creates a new GCP Compute Engine client. diff --git a/providers/gcp/services/computeengine/pricing.go b/providers/gcp/services/computeengine/pricing.go index 17cc24c4..0a499cc5 100644 --- a/providers/gcp/services/computeengine/pricing.go +++ b/providers/gcp/services/computeengine/pricing.go @@ -26,12 +26,16 @@ const ( // ComputePricing is the price of a commitment's resources, for one term. type ComputePricing struct { Currency string + // HourlyRate is the committed price per hour for the whole resource set. HourlyRate float64 + // CommitmentPrice is the committed price over the whole term. CommitmentPrice float64 + // OnDemandPrice is the on-demand price of the same resources over the term. - OnDemandPrice float64 + OnDemandPrice float64 + SavingsPercentage float64 }