Merge remote-tracking branch 'remotes/from/ce/release/2.x.x' into release/2.x.x
Some checks failed
build / setup (push) Has been cancelled
build / hcp-setup (push) Has been cancelled
CI / setup (push) Has been cancelled
Run linters / Setup (push) Has been cancelled
Run linters / Semgrep (push) Has been cancelled
Check Copywrite Headers / copywrite (push) Has been cancelled
build / Check ce/* Pull Requests (push) Has been cancelled
build / ui (push) Has been cancelled
build / artifacts-ce (push) Has been cancelled
build / artifacts-ent (push) Has been cancelled
build / hcp-image (push) Has been cancelled
build / test (push) Has been cancelled
build / test-hcp-image (push) Has been cancelled
build / completed-successfully (push) Has been cancelled
CI / Run Autopilot upgrade tool (push) Has been cancelled
CI / Run Go tests (push) Has been cancelled
CI / Run Go tests tagged with testonly (push) Has been cancelled
CI / Run Go tests with data race detection (push) Has been cancelled
CI / Run Go tests with FIPS configuration (push) Has been cancelled
CI / Test UI (push) Has been cancelled
CI / tests-completed (push) Has been cancelled
Run linters / Deprecated functions (push) Has been cancelled
Run linters / Code checks (push) Has been cancelled
Run linters / Protobuf generate delta (push) Has been cancelled
Run linters / Format (push) Has been cancelled

This commit is contained in:
hc-github-team-secure-vault-core 2026-05-14 17:32:34 +00:00
commit d6e2fff8b3
8 changed files with 91 additions and 1 deletions

3
changelog/_14685.txt Normal file
View file

@ -0,0 +1,3 @@
```release-note:improvement
consumption-billing: Added consumption billing metrics for PKI External CA certificates.
```

View file

@ -39,6 +39,7 @@ const (
SSHCertificateMetric = "ssh/normalized-certs-issued"
SSHOTPMetric = "ssh/credential-count"
OidcDurationAdjustedCountPrefix = "oidcNormalizedTokenUnits/"
ExternalCaDurationAdjustedCountPrefix = "externalCaNormalizedCertsIssued/"
BillingWriteInterval = 10 * time.Minute
// pluginCountsSendTimeout is the timeout for sending plugin counts to the active node
@ -62,6 +63,9 @@ type ConsumptionBilling struct {
KmipSeenEnabledThisMonth atomic.Bool
IdentityTokenUnits IdentityTokenUnits
// ExternalCaCertUnits tracks duration-adjusted PKI external CA certificate units
ExternalCaCertUnits *uberatomic.Float64
}
type BillingConfig struct {
@ -145,6 +149,15 @@ func (s *ConsumptionBilling) WriteBillingData(ctx context.Context, mountType str
}
s.DataProtectionCallCounts.GcpKms.Add(val)
case "external-ca":
// External CA uses float64 for duration-adjusted units
val, ok := data["units"].(float64)
if !ok {
err := fmt.Errorf("invalid value type for external-ca")
return err
}
s.ExternalCaCertUnits.Add(val)
default:
err := fmt.Errorf("unknown metric type: %s", mountType)
return err

View file

@ -36,7 +36,8 @@ func (c *Core) setupConsumptionBilling(ctx context.Context) error {
OidcTokenDuration: uberAtomic.NewFloat64(0),
SpiffeJwt: uberAtomic.NewFloat64(0),
},
Logger: logger,
ExternalCaCertUnits: uberAtomic.NewFloat64(0),
Logger: logger,
}
if c.systemBarrierView != nil {
c.consumptionBillingSubView = c.systemBarrierView.SubView(billing.BillingSubPath)
@ -186,6 +187,7 @@ func (c *Core) resetInMemoryBillingMetrics() error {
c.consumptionBilling.DataProtectionCallCounts.GcpKms.Store(0)
c.consumptionBilling.KmipSeenEnabledThisMonth.Store(false)
c.consumptionBilling.IdentityTokenUnits.OidcTokenDuration.Store(0)
c.consumptionBilling.ExternalCaCertUnits.Store(0)
return nil
}
@ -294,5 +296,8 @@ func (c *Core) UpdateLocalAggregatedMetrics(ctx context.Context, currentMonth ti
if _, err := c.UpdateGcpKmsCallCounts(ctx, currentMonth); err != nil {
return fmt.Errorf("could not store GCP KMS data protection call counts: %w", err)
}
if _, err := c.UpdateExternalCaCertUnits(ctx, currentMonth); err != nil {
return fmt.Errorf("could not store external CA certificate units: %w", err)
}
return nil
}

View file

@ -248,3 +248,34 @@ func CreateMockOSHost(ctx context.Context, storage logical.Storage, hostName str
func DeleteMockOSHost(ctx context.Context, storage logical.Storage, hostName string) error {
return storage.Delete(ctx, "hosts/"+hostName)
}
func (c *Core) ResetInMemoryExternalCaCertUnits() {
c.consumptionBillingLock.RLock()
cb := c.consumptionBilling
c.consumptionBillingLock.RUnlock()
if cb != nil {
cb.ExternalCaCertUnits.Store(0)
}
}
func (c *Core) GetInMemoryExternalCaCertUnits() float64 {
c.consumptionBillingLock.RLock()
cb := c.consumptionBilling
c.consumptionBillingLock.RUnlock()
if cb != nil {
return cb.ExternalCaCertUnits.Load()
}
return 0
}
func (c *Core) SetInMemoryExternalCaCertUnits(count float64) {
c.consumptionBillingLock.RLock()
cb := c.consumptionBilling
c.consumptionBillingLock.RUnlock()
if cb != nil {
cb.ExternalCaCertUnits.Store(count)
}
}

View file

@ -28,3 +28,13 @@ func (c *Core) UpdateSpiffeJwtTokenUnits(ctx context.Context, currentMonth time.
// No-op in OSS
return 0, nil
}
func (c *Core) GetStoredExternalCaCertUnits(ctx context.Context, currentMonth time.Time) (float64, error) {
// No-op in OSS
return 0, nil
}
func (c *Core) UpdateExternalCaCertUnits(ctx context.Context, currentMonth time.Time) (float64, error) {
// No-op in OSS
return 0, nil
}

View file

@ -176,6 +176,7 @@ func Test_BillingOverview_EmptyCluster(t *testing.T) {
"managed_keys": false,
"ssh_units": false,
"id_token_units": false,
"external_ca_pki_units": false,
}
for _, metric := range currentMonth.UsageMetrics {

View file

@ -287,6 +287,12 @@ func (b *SystemBackend) buildMonthBillingData(ctx context.Context, month time.Ti
}
usageMetrics = append(usageMetrics, idTokenUnitsMetric)
externalCaMetric, err := b.buildExternalCaBillingMetric(ctx, month)
if err != nil {
return nil, err
}
usageMetrics = append(usageMetrics, externalCaMetric)
// Round all float64 values in usageMetrics to 4 decimal places.
// Rounding time for usage metrics is insignificant, so we can keep it centralized here.
// This prevents us from having to do it in each individual metric.
@ -519,6 +525,21 @@ func (b *SystemBackend) buildIdTokenUnitsBillingMetric(ctx context.Context, mont
}, nil
}
// buildExternalCaBillingMetric creates the billing metric for external CA certificate counts.
func (b *SystemBackend) buildExternalCaBillingMetric(ctx context.Context, month time.Time) (map[string]interface{}, error) {
count, err := b.Core.GetStoredExternalCaCertUnits(ctx, month)
if err != nil {
return nil, fmt.Errorf("error retrieving external CA certificate units for month: %w", err)
}
return map[string]interface{}{
"metric_name": "external_ca_pki_units",
"metric_data": map[string]interface{}{
"total": count,
},
}, nil
}
// getRoleCounts retrieves and combines role and managed key counts from replicated and local storage
func (c *Core) getRoleAndManagedKeyCounts(ctx context.Context, month time.Time) (*RoleCounts, *ManagedKeyCounts, error) {
var replicatedRoleCounts *RoleCounts

View file

@ -769,6 +769,7 @@ func TestSystemBackend_BillingOverview_EmptyMetrics(t *testing.T) {
"managed_keys": false,
"ssh_units": false,
"id_token_units": false,
"external_ca_pki_units": false,
}
for _, metric := range usageMetrics {
@ -888,6 +889,11 @@ func TestSystemBackend_BillingOverview_EmptyMetrics(t *testing.T) {
for typeName, found := range expectedTypes {
require.True(t, found, "type %s should be present", typeName)
}
case "external_ca_pki_units":
total, ok := metricData["total"].(float64)
require.True(t, ok, "external_ca_pki_units total should be float64")
require.Equal(t, float64(0), total, "external_ca_pki_units total should be 0")
}
}