Files
certctl/internal/service/intermediate_ca_metrics.go
T
shankar0123 21aeed4f4e legal: addlicense headers + normalize legacy variants (Phase 0 RED-4)
Phase 0 closure (Path B2, post-rewrite):

addlicense sweep — adds the canonical certctl LLC copyright + BUSL-1.1
SPDX header to every production Go file. Template:

  // Copyright 2026 certctl LLC. All rights reserved.
  // SPDX-License-Identifier: BUSL-1.1

Coverage: 338 / 338 production Go files (cmd/ + internal/, excluding
*_test.go and **/testdata/**). Pre-sweep coverage was 22 / 338 (6.5%);
post-sweep is 338 / 338 (100%).

Normalized 22 pre-existing legacy headers (`// Copyright (c) certctl`
+ `// SPDX-License-Identifier: BSL-1.1`) and 1 file using a
`Certctl Contributors` attribution. The legacy SPDX ID `BSL-1.1`
is non-standard; the official SPDX identifier for Business Source
License 1.1 is `BUSL-1.1` (capital U). All 338 files now share the
canonical form.

Generated via:
  addlicense -c "certctl LLC" -y 2026 \
    -f cowork/legal/copyright-header.tpl \
    -ignore '**/testdata/**' -ignore '**/*_test.go' \
    cmd/ internal/

Verification:
  find cmd internal -name '*.go' -not -name '*_test.go' \
    -not -path '*/testdata/*' \
    -exec grep -L '^// Copyright 2026 certctl LLC' {} \; | wc -l

  Returns: 0

gofmt clean. Header additions are comments only, no compile impact.

Closes: cowork/certctl-architecture-diligence-audit.html#fix-RED-4
2026-05-13 21:23:35 +00:00

108 lines
2.8 KiB
Go

// Copyright 2026 certctl LLC. All rights reserved.
// SPDX-License-Identifier: BUSL-1.1
package service
import (
"sort"
"sync"
"sync/atomic"
)
// IntermediateCAMetrics is a thread-safe counter table for the CA-
// hierarchy management surface (Rank 8). Mirrors the
// ApprovalMetrics + ExpiryAlertMetrics shape: cmd/server/main.go
// constructs ONE instance, passes it to IntermediateCAService
// (recording side) AND metricsHandler (exposing side) so the
// snapshotter is the single source of truth.
//
// Dimensions:
//
// issuer_id — owning issuer (bounded cardinality; operators have
// <100 issuers in production).
// kind — closed enum:
// "create_root" — CreateRoot succeeded.
// "create_child" — CreateChild succeeded.
// "retire_<state>" — Retire transitioned state.
type IntermediateCAMetrics struct {
mu sync.RWMutex
counters map[intermediateCAKey]*atomic.Uint64
}
type intermediateCAKey struct {
IssuerID string
Kind string
}
// NewIntermediateCAMetrics returns a zero-value instance ready for
// concurrent use.
func NewIntermediateCAMetrics() *IntermediateCAMetrics {
return &IntermediateCAMetrics{
counters: make(map[intermediateCAKey]*atomic.Uint64),
}
}
// RecordCreate bumps the create-counter. role ∈ {"root", "child"}.
func (m *IntermediateCAMetrics) RecordCreate(issuerID, role string) {
m.bump(issuerID, "create_"+role)
}
// RecordRetire bumps the retire-counter. newState ∈
// {"retiring", "retired"}.
func (m *IntermediateCAMetrics) RecordRetire(issuerID, newState string) {
m.bump(issuerID, "retire_"+newState)
}
func (m *IntermediateCAMetrics) bump(issuerID, kind string) {
if m == nil {
return
}
key := intermediateCAKey{IssuerID: issuerID, Kind: kind}
m.mu.RLock()
c, ok := m.counters[key]
m.mu.RUnlock()
if !ok {
m.mu.Lock()
c, ok = m.counters[key]
if !ok {
c = &atomic.Uint64{}
m.counters[key] = c
}
m.mu.Unlock()
}
c.Add(1)
}
// IntermediateCAEntry is a single row of the SnapshotIntermediateCA
// output.
type IntermediateCAEntry struct {
IssuerID string
Kind string
Count uint64
}
// SnapshotIntermediateCA returns the current counter table sorted by
// (issuer_id, kind) for deterministic Prometheus exposition.
func (m *IntermediateCAMetrics) SnapshotIntermediateCA() []IntermediateCAEntry {
if m == nil {
return nil
}
m.mu.RLock()
out := make([]IntermediateCAEntry, 0, len(m.counters))
for k, c := range m.counters {
out = append(out, IntermediateCAEntry{
IssuerID: k.IssuerID,
Kind: k.Kind,
Count: c.Load(),
})
}
m.mu.RUnlock()
sort.Slice(out, func(i, j int) bool {
if out[i].IssuerID != out[j].IssuerID {
return out[i].IssuerID < out[j].IssuerID
}
return out[i].Kind < out[j].Kind
})
return out
}