From bcf8d3a40a88b37b95ef18b7f52b97eed117f523 Mon Sep 17 00:00:00 2001 From: Edvin Norling Date: Mon, 16 Feb 2026 13:04:58 +0100 Subject: [PATCH 01/13] test: add LRU cache unit and integration tests (TDD Red) Add tests for the upcoming LRU+TTL cache implementation (#647). Unit tests cover set/get, TTL expiry, eviction, key isolation, error skipping, and resource version invalidation. Integration tests verify ValidatePolicy cache hit/miss behavior. All tests currently fail to compile (NewLRUCache undefined), confirming the TDD Red phase. Co-Authored-By: Claude Opus 4.6 Signed-off-by: Edvin Norling --- pkg/webhook/lrucache_test.go | 190 +++++++++++++++++++++++ pkg/webhook/validator_test.go | 280 ++++++++++++++++++++++++++++++++++ 2 files changed, 470 insertions(+) create mode 100644 pkg/webhook/lrucache_test.go diff --git a/pkg/webhook/lrucache_test.go b/pkg/webhook/lrucache_test.go new file mode 100644 index 000000000..ee19b5a0a --- /dev/null +++ b/pkg/webhook/lrucache_test.go @@ -0,0 +1,190 @@ +// +// Copyright 2022 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package webhook + +import ( + "context" + "errors" + "testing" + "time" +) + +func TestLRUCacheSetGet(t *testing.T) { + cache := NewLRUCache(10, 1*time.Hour) + ctx := context.Background() + + want := &CacheResult{ + PolicyResult: &PolicyResult{ + AuthorityMatches: map[string]AuthorityMatch{}, + }, + } + cache.Set(ctx, "gcr.io/foo/bar@sha256:abc", "my-policy", "uid-1", "v1", want) + + got := cache.Get(ctx, "gcr.io/foo/bar@sha256:abc", "uid-1", "v1") + if got == nil { + t.Fatal("expected cache hit, got nil") + } + if got.PolicyResult == nil { + t.Fatal("expected PolicyResult, got nil") + } +} + +func TestLRUCacheMiss(t *testing.T) { + cache := NewLRUCache(10, 1*time.Hour) + ctx := context.Background() + + got := cache.Get(ctx, "gcr.io/foo/bar@sha256:abc", "uid-1", "v1") + if got != nil { + t.Fatalf("expected cache miss (nil), got %v", got) + } +} + +func TestLRUCacheSkipsErrors(t *testing.T) { + cache := NewLRUCache(10, 1*time.Hour) + ctx := context.Background() + + // Failed validation: PolicyResult is nil, only errors present. + // This is the case when no authorities matched (validator.go:590-591). + cache.Set(ctx, "gcr.io/foo/bar@sha256:abc", "my-policy", "uid-1", "v1", &CacheResult{ + Errors: []error{errors.New("image not signed")}, + }) + + got := cache.Get(ctx, "gcr.io/foo/bar@sha256:abc", "uid-1", "v1") + if got != nil { + t.Fatalf("expected cache miss for failed validation, got %v", got) + } +} + +func TestLRUCachePartialSuccess(t *testing.T) { + cache := NewLRUCache(10, 1*time.Hour) + ctx := context.Background() + + // Partial success: PolicyResult is non-nil (at least one authority matched) + // but there are also errors from authorities that didn't match. + // This is the common case with multi-authority CIPs (validator.go:641). + cache.Set(ctx, "gcr.io/foo/bar@sha256:abc", "my-policy", "uid-1", "v1", &CacheResult{ + PolicyResult: &PolicyResult{ + AuthorityMatches: map[string]AuthorityMatch{ + "authority-0": {Static: true}, + }, + }, + Errors: []error{errors.New("authority-1: signature invalid")}, + }) + + got := cache.Get(ctx, "gcr.io/foo/bar@sha256:abc", "uid-1", "v1") + if got == nil { + t.Fatal("expected cache hit for partial success (PolicyResult non-nil), got nil") + } + if got.PolicyResult == nil { + t.Fatal("expected PolicyResult in cached result") + } + if len(got.Errors) != 1 { + t.Fatalf("expected 1 error in cached result, got %d", len(got.Errors)) + } +} + +func TestLRUCacheTTLExpiry(t *testing.T) { + cache := NewLRUCache(10, 50*time.Millisecond) + ctx := context.Background() + + cache.Set(ctx, "gcr.io/foo/bar@sha256:abc", "my-policy", "uid-1", "v1", &CacheResult{ + PolicyResult: &PolicyResult{ + AuthorityMatches: map[string]AuthorityMatch{}, + }, + }) + + // Should hit immediately + if got := cache.Get(ctx, "gcr.io/foo/bar@sha256:abc", "uid-1", "v1"); got == nil { + t.Fatal("expected cache hit before TTL expiry") + } + + // Wait for TTL to expire + time.Sleep(100 * time.Millisecond) + + if got := cache.Get(ctx, "gcr.io/foo/bar@sha256:abc", "uid-1", "v1"); got != nil { + t.Fatalf("expected cache miss after TTL expiry, got %v", got) + } +} + +func TestLRUCacheEviction(t *testing.T) { + cache := NewLRUCache(2, 1*time.Hour) + ctx := context.Background() + result := &CacheResult{ + PolicyResult: &PolicyResult{ + AuthorityMatches: map[string]AuthorityMatch{}, + }, + } + + cache.Set(ctx, "image-1", "p", "uid-1", "v1", result) + cache.Set(ctx, "image-2", "p", "uid-1", "v1", result) + cache.Set(ctx, "image-3", "p", "uid-1", "v1", result) // evicts image-1 + + if got := cache.Get(ctx, "image-1", "uid-1", "v1"); got != nil { + t.Fatal("expected image-1 to be evicted") + } + if got := cache.Get(ctx, "image-2", "uid-1", "v1"); got == nil { + t.Fatal("expected image-2 to still be cached") + } + if got := cache.Get(ctx, "image-3", "uid-1", "v1"); got == nil { + t.Fatal("expected image-3 to still be cached") + } +} + +func TestLRUCacheKeyIsolation(t *testing.T) { + cache := NewLRUCache(10, 1*time.Hour) + ctx := context.Background() + result := &CacheResult{ + PolicyResult: &PolicyResult{ + AuthorityMatches: map[string]AuthorityMatch{}, + }, + } + + cache.Set(ctx, "image-a", "p", "uid-1", "v1", result) + + // Different image + if got := cache.Get(ctx, "image-b", "uid-1", "v1"); got != nil { + t.Fatal("expected miss for different image") + } + // Different UID + if got := cache.Get(ctx, "image-a", "uid-2", "v1"); got != nil { + t.Fatal("expected miss for different UID") + } + // Correct key + if got := cache.Get(ctx, "image-a", "uid-1", "v1"); got == nil { + t.Fatal("expected hit for matching key") + } +} + +func TestLRUCacheResourceVersionInvalidation(t *testing.T) { + cache := NewLRUCache(10, 1*time.Hour) + ctx := context.Background() + result := &CacheResult{ + PolicyResult: &PolicyResult{ + AuthorityMatches: map[string]AuthorityMatch{}, + }, + } + + cache.Set(ctx, "image-a", "my-policy", "uid-1", "v1", result) + + // Same image+uid but new resourceVersion (policy was updated) + if got := cache.Get(ctx, "image-a", "uid-1", "v2"); got != nil { + t.Fatal("expected miss for updated resourceVersion") + } + // Original version still hits + if got := cache.Get(ctx, "image-a", "uid-1", "v1"); got == nil { + t.Fatal("expected hit for original resourceVersion") + } +} diff --git a/pkg/webhook/validator_test.go b/pkg/webhook/validator_test.go index c04d42a96..d4d7ef63a 100644 --- a/pkg/webhook/validator_test.go +++ b/pkg/webhook/validator_test.go @@ -4234,3 +4234,283 @@ func TestDiscoverAttestationsOCI11PartialProcessingFailure(t *testing.T) { t.Errorf("Expected 2 signatures (second failed), got %d", len(sigs)) } } + +func TestValidatePolicyCacheHit(t *testing.T) { + // Save and restore global mock functions + origCVS := cosignVerifySignatures + defer func() { cosignVerifySignatures = origCVS }() + + callCount := 0 + cosignVerifySignatures = func(_ context.Context, _ name.Reference, _ *cosign.CheckOpts) ([]oci.Signature, bool, error) { + callCount++ + sig, err := static.NewSignature(nil, "") + if err != nil { + return nil, false, err + } + return []oci.Signature{sig}, true, nil + } + + var authorityKeyCosignPub *ecdsa.PublicKey + pems := parsePems([]byte(authorityKeyCosignPubString)) + if len(pems) > 0 { + key, _ := x509.ParsePKIXPublicKey(pems[0].Bytes) + authorityKeyCosignPub = key.(*ecdsa.PublicKey) + } else { + t.Fatal("Error parsing authority key from string") + } + + digest := name.MustParseReference("gcr.io/distroless/static:nonroot@sha256:be5d77c62dbe7fedfb0a4e5ec2f91078080800ab1f18358e5f31fcc8faa023c4") + + ctx := context.Background() + kc, err := k8schain.NewNoClient(ctx) + if err != nil { + t.Fatal(err) + } + + // Inject a real cache into context + cache := NewLRUCache(10, 1*time.Hour) + ctx = ToContext(ctx, cache) + + cip := webhookcip.ClusterImagePolicy{ + Authorities: []webhookcip.Authority{{ + Key: &webhookcip.KeyRef{ + Data: authorityKeyCosignPubString, + PublicKeys: []crypto.PublicKey{authorityKeyCosignPub}, + HashAlgorithm: signaturealgo.DefaultSignatureAlgorithm, + HashAlgorithmCode: crypto.SHA256, + }, + }}, + } + cip.UID = "test-uid" + cip.ResourceVersion = "v1" + + // First call - should invoke cosign + result1, errs1 := ValidatePolicy(ctx, system.Namespace(), digest, cip, kc) + if len(errs1) > 0 { + t.Fatalf("unexpected errors: %v", errs1) + } + if result1 == nil { + t.Fatal("expected non-nil PolicyResult") + } + if callCount != 1 { + t.Fatalf("expected cosign to be called once, got %d", callCount) + } + + // Second call - should return cached result without calling cosign + result2, errs2 := ValidatePolicy(ctx, system.Namespace(), digest, cip, kc) + if len(errs2) > 0 { + t.Fatalf("unexpected errors: %v", errs2) + } + if result2 == nil { + t.Fatal("expected non-nil cached PolicyResult") + } + if callCount != 1 { + t.Fatalf("expected cosign NOT to be called again (cache hit), got %d calls", callCount) + } +} + +func TestValidatePolicyCacheSkipsErrors(t *testing.T) { + origCVS := cosignVerifySignatures + defer func() { cosignVerifySignatures = origCVS }() + + callCount := 0 + cosignVerifySignatures = func(_ context.Context, _ name.Reference, _ *cosign.CheckOpts) ([]oci.Signature, bool, error) { + callCount++ + return nil, false, errors.New("image not signed") + } + + var authorityKeyCosignPub *ecdsa.PublicKey + pems := parsePems([]byte(authorityKeyCosignPubString)) + if len(pems) > 0 { + key, _ := x509.ParsePKIXPublicKey(pems[0].Bytes) + authorityKeyCosignPub = key.(*ecdsa.PublicKey) + } else { + t.Fatal("Error parsing authority key from string") + } + + digest := name.MustParseReference("gcr.io/distroless/static:nonroot@sha256:be5d77c62dbe7fedfb0a4e5ec2f91078080800ab1f18358e5f31fcc8faa023c4") + + ctx := context.Background() + kc, err := k8schain.NewNoClient(ctx) + if err != nil { + t.Fatal(err) + } + + cache := NewLRUCache(10, 1*time.Hour) + ctx = ToContext(ctx, cache) + + cip := webhookcip.ClusterImagePolicy{ + Authorities: []webhookcip.Authority{{ + Key: &webhookcip.KeyRef{ + Data: authorityKeyCosignPubString, + PublicKeys: []crypto.PublicKey{authorityKeyCosignPub}, + HashAlgorithm: signaturealgo.DefaultSignatureAlgorithm, + HashAlgorithmCode: crypto.SHA256, + }, + }}, + } + cip.UID = "test-uid" + cip.ResourceVersion = "v1" + + // First call - should fail and NOT cache the error + _, errs1 := ValidatePolicy(ctx, system.Namespace(), digest, cip, kc) + if len(errs1) == 0 { + t.Fatal("expected errors on first call") + } + if callCount != 1 { + t.Fatalf("expected cosign to be called once, got %d", callCount) + } + + // Second call - should call cosign again because errors aren't cached + _, errs2 := ValidatePolicy(ctx, system.Namespace(), digest, cip, kc) + if len(errs2) == 0 { + t.Fatal("expected errors on second call") + } + if callCount != 2 { + t.Fatalf("expected cosign to be called again (errors not cached), got %d calls", callCount) + } +} + +func TestValidatePolicyCachePartialSuccess(t *testing.T) { + // Multi-authority CIP: one authority fails (cosign), one passes (static). + // ValidatePolicy returns non-nil PolicyResult AND non-empty errors. + // This partial success SHOULD be cached. + origCVS := cosignVerifySignatures + defer func() { cosignVerifySignatures = origCVS }() + + callCount := 0 + cosignVerifySignatures = func(_ context.Context, _ name.Reference, _ *cosign.CheckOpts) ([]oci.Signature, bool, error) { + callCount++ + return nil, false, errors.New("signature invalid") + } + + var authorityKeyCosignPub *ecdsa.PublicKey + pems := parsePems([]byte(authorityKeyCosignPubString)) + if len(pems) > 0 { + key, _ := x509.ParsePKIXPublicKey(pems[0].Bytes) + authorityKeyCosignPub = key.(*ecdsa.PublicKey) + } else { + t.Fatal("Error parsing authority key from string") + } + + digest := name.MustParseReference("gcr.io/distroless/static:nonroot@sha256:be5d77c62dbe7fedfb0a4e5ec2f91078080800ab1f18358e5f31fcc8faa023c4") + + ctx := context.Background() + kc, err := k8schain.NewNoClient(ctx) + if err != nil { + t.Fatal(err) + } + + cache := NewLRUCache(10, 1*time.Hour) + ctx = ToContext(ctx, cache) + + cip := webhookcip.ClusterImagePolicy{ + Authorities: []webhookcip.Authority{ + { + // This authority will fail (cosign mock returns error) + Key: &webhookcip.KeyRef{ + Data: authorityKeyCosignPubString, + PublicKeys: []crypto.PublicKey{authorityKeyCosignPub}, + HashAlgorithm: signaturealgo.DefaultSignatureAlgorithm, + HashAlgorithmCode: crypto.SHA256, + }, + }, + { + // This authority will pass (static action) + Static: &webhookcip.StaticRef{Action: "pass"}, + }, + }, + } + cip.UID = "test-uid" + cip.ResourceVersion = "v1" + + // First call - one authority fails, one passes -> partial success + result1, errs1 := ValidatePolicy(ctx, system.Namespace(), digest, cip, kc) + if result1 == nil { + t.Fatal("expected non-nil PolicyResult (partial success)") + } + if len(errs1) == 0 { + t.Fatal("expected errors from the failing authority") + } + if callCount != 1 { + t.Fatalf("expected cosign to be called once, got %d", callCount) + } + + // Second call - should return cached result, cosign NOT called again + result2, errs2 := ValidatePolicy(ctx, system.Namespace(), digest, cip, kc) + if result2 == nil { + t.Fatal("expected non-nil cached PolicyResult") + } + if len(errs2) == 0 { + t.Fatal("expected cached errors from the failing authority") + } + if callCount != 1 { + t.Fatalf("expected cosign NOT to be called again (cache hit), got %d calls", callCount) + } +} + +func TestValidatePolicyNoCacheDefault(t *testing.T) { + origCVS := cosignVerifySignatures + defer func() { cosignVerifySignatures = origCVS }() + + callCount := 0 + cosignVerifySignatures = func(_ context.Context, _ name.Reference, _ *cosign.CheckOpts) ([]oci.Signature, bool, error) { + callCount++ + sig, err := static.NewSignature(nil, "") + if err != nil { + return nil, false, err + } + return []oci.Signature{sig}, true, nil + } + + var authorityKeyCosignPub *ecdsa.PublicKey + pems := parsePems([]byte(authorityKeyCosignPubString)) + if len(pems) > 0 { + key, _ := x509.ParsePKIXPublicKey(pems[0].Bytes) + authorityKeyCosignPub = key.(*ecdsa.PublicKey) + } else { + t.Fatal("Error parsing authority key from string") + } + + digest := name.MustParseReference("gcr.io/distroless/static:nonroot@sha256:be5d77c62dbe7fedfb0a4e5ec2f91078080800ab1f18358e5f31fcc8faa023c4") + + ctx := context.Background() + kc, err := k8schain.NewNoClient(ctx) + if err != nil { + t.Fatal(err) + } + + // Do NOT inject a cache - this is the default behavior + // FromContext(ctx) should return NoCache + + cip := webhookcip.ClusterImagePolicy{ + Authorities: []webhookcip.Authority{{ + Key: &webhookcip.KeyRef{ + Data: authorityKeyCosignPubString, + PublicKeys: []crypto.PublicKey{authorityKeyCosignPub}, + HashAlgorithm: signaturealgo.DefaultSignatureAlgorithm, + HashAlgorithmCode: crypto.SHA256, + }, + }}, + } + cip.UID = "test-uid" + cip.ResourceVersion = "v1" + + // First call + _, errs1 := ValidatePolicy(ctx, system.Namespace(), digest, cip, kc) + if len(errs1) > 0 { + t.Fatalf("unexpected errors: %v", errs1) + } + if callCount != 1 { + t.Fatalf("expected cosign called once, got %d", callCount) + } + + // Second call - should call cosign again because there is no cache + _, errs2 := ValidatePolicy(ctx, system.Namespace(), digest, cip, kc) + if len(errs2) > 0 { + t.Fatalf("unexpected errors: %v", errs2) + } + if callCount != 2 { + t.Fatalf("expected cosign called twice (no cache), got %d calls", callCount) + } +} From ccd8e3b788d092806bb0179fe3ac92edfd68b636 Mon Sep 17 00:00:00 2001 From: Edvin Norling Date: Mon, 16 Feb 2026 13:14:47 +0100 Subject: [PATCH 02/13] feat: implement LRU+TTL cache for validation results Add LRUCache implementing ResultCache using hashicorp/golang-lru/v2 expirable. Only successful validations (PolicyResult non-nil) are cached; failed validations are skipped to allow immediate retries. Fix cache key mismatch bug: ref.Name() in Set vs ref.String() in Get caused cache to never hit. Both now use ref.String(). Move cache Set into ValidatePolicy so caching is self-contained regardless of call path. Co-Authored-By: Claude Opus 4.6 Signed-off-by: Edvin Norling --- go.mod | 1 + pkg/webhook/lrucache.go | 57 ++++++++++++++++++++++++++++++++++++++++ pkg/webhook/validator.go | 10 +++---- 3 files changed, 63 insertions(+), 5 deletions(-) create mode 100644 pkg/webhook/lrucache.go diff --git a/go.mod b/go.mod index 18b850d87..a1e67c177 100644 --- a/go.mod +++ b/go.mod @@ -62,6 +62,7 @@ require ( github.com/docker/docker-credential-helpers v0.9.3 github.com/docker/go-connections v0.5.0 github.com/go-jose/go-jose/v4 v4.1.0 + github.com/hashicorp/golang-lru/v2 v2.0.7 github.com/sigstore/protobuf-specs v0.4.1 github.com/sigstore/scaffolding v0.7.22 github.com/sigstore/sigstore-go v0.7.2 diff --git a/pkg/webhook/lrucache.go b/pkg/webhook/lrucache.go new file mode 100644 index 000000000..d24ef5b05 --- /dev/null +++ b/pkg/webhook/lrucache.go @@ -0,0 +1,57 @@ +// +// Copyright 2022 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package webhook + +import ( + "context" + "fmt" + "time" + + expirable "github.com/hashicorp/golang-lru/v2/expirable" +) + +// LRUCache implements ResultCache using an LRU cache with TTL expiration. +// Only successful validations (PolicyResult non-nil) are cached. +// Failed validations (PolicyResult nil) are not cached to allow retries. +type LRUCache struct { + cache *expirable.LRU[string, *CacheResult] +} + +// NewLRUCache creates a new LRU cache with the given size and TTL. +func NewLRUCache(size int, ttl time.Duration) *LRUCache { + return &LRUCache{ + cache: expirable.NewLRU[string, *CacheResult](size, nil, ttl), + } +} + +func cacheKeyFor(image, uid, resourceVersion string) string { + return fmt.Sprintf("%s/%s/%s", image, uid, resourceVersion) +} + +func (c *LRUCache) Get(_ context.Context, image, uid, resourceVersion string) *CacheResult { + result, ok := c.cache.Get(cacheKeyFor(image, uid, resourceVersion)) + if !ok { + return nil + } + return result +} + +func (c *LRUCache) Set(_ context.Context, image, name, uid, resourceVersion string, cacheResult *CacheResult) { //nolint: revive + if cacheResult.PolicyResult == nil { + return + } + c.cache.Add(cacheKeyFor(image, uid, resourceVersion), cacheResult) +} diff --git a/pkg/webhook/validator.go b/pkg/webhook/validator.go index c03a93a8c..68bb37fb8 100644 --- a/pkg/webhook/validator.go +++ b/pkg/webhook/validator.go @@ -418,11 +418,6 @@ func validatePolicies(ctx context.Context, namespace string, ref name.Reference, result := retChannelType{name: cipName} result.policyResult, result.errors = ValidatePolicy(ctx, namespace, ref, cip, kc, remoteOpts...) - // Cache the result. - FromContext(ctx).Set(ctx, ref.Name(), cipName, string(cip.UID), cip.ResourceVersion, &CacheResult{ - PolicyResult: result.policyResult, - Errors: result.errors, - }) results <- result }() } @@ -638,6 +633,11 @@ func ValidatePolicy(ctx context.Context, namespace string, ref name.Reference, c return nil, append(authorityErrors, asFieldError(cip.Mode == "warn", warn)) } } + // Cache the result. Set is a no-op when PolicyResult is nil. + FromContext(ctx).Set(ctx, ref.String(), "", string(cip.UID), cip.ResourceVersion, &CacheResult{ + PolicyResult: policyResult, + Errors: authorityErrors, + }) return policyResult, authorityErrors } From 481f870f916f4f336b5919a5e1415d4920d4c3d0 Mon Sep 17 00:00:00 2001 From: Edvin Norling Date: Mon, 16 Feb 2026 13:22:34 +0100 Subject: [PATCH 03/13] feat: add opt-in CLI flags to enable validation result cache Wire the LRU+TTL cache into the validating webhook via --enable-cache, --cache-size, and --cache-ttl flags. Cache is off by default and only injected into the validating admission controller context. Co-Authored-By: Claude Opus 4.6 Signed-off-by: Edvin Norling --- cmd/webhook/main.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/cmd/webhook/main.go b/cmd/webhook/main.go index 6f84894fd..35712cc39 100644 --- a/cmd/webhook/main.go +++ b/cmd/webhook/main.go @@ -103,6 +103,12 @@ var ( // trustrootResyncPeriod holds the interval which the TrustRoot will resync // This is essential for triggering a reconcile update for potentially stale TUF metadata. trustrootResyncPeriod = flag.Duration("trustroot-resync-period", 24*time.Hour, "The resync period for ClusterImagePolicies. The default is 24h.") + + // Cache configuration for validating webhook results. + // https://github.com/sigstore/policy-controller/issues/647 + enableCache = flag.Bool("enable-cache", false, "Enable in-memory LRU cache for validation results.") + cacheSize = flag.Int("cache-size", 1024, "Maximum number of entries in the validation result cache.") + cacheTTL = flag.Duration("cache-ttl", 1*time.Hour, "TTL for cached validation results.") ) func main() { @@ -238,6 +244,12 @@ func NewValidatingAdmissionController(ctx context.Context, cmw configmap.Watcher kc := kubeclient.Get(ctx) validator := cwebhook.NewValidator(ctx) + var cache cwebhook.ResultCache + if *enableCache { + cache = cwebhook.NewLRUCache(*cacheSize, *cacheTTL) + logging.FromContext(ctx).Infof("Validation result cache enabled: size=%d, ttl=%v", *cacheSize, *cacheTTL) + } + return validation.NewAdmissionController(ctx, // Name of the resource webhook. *webhookName, @@ -253,6 +265,9 @@ func NewValidatingAdmissionController(ctx context.Context, cmw configmap.Watcher ctx = context.WithValue(ctx, kubeclient.Key{}, kc) ctx = store.ToContext(ctx) ctx = policyControllerConfigStore.ToContext(ctx) + if cache != nil { + ctx = cwebhook.ToContext(ctx, cache) + } ctx = policyduckv1beta1.WithPodScalableValidator(ctx, validator.ValidatePodScalable) ctx = duckv1.WithPodValidator(ctx, validator.ValidatePod) ctx = duckv1.WithPodSpecValidator(ctx, validator.ValidatePodSpecable) From 8013c95bdd1fb4074e945b07bd5085279212430d Mon Sep 17 00:00:00 2001 From: Edvin Norling Date: Mon, 16 Feb 2026 14:11:44 +0100 Subject: [PATCH 04/13] fix: defensive copy in cache Set, update copyright, extract test helper - Copy CacheResult.Errors slice in LRUCache.Set to prevent callers from mutating cached entries through the shared backing array - Update copyright year to 2026 on new files (lrucache.go, lrucache_test.go) - Extract cacheTestFixtures helper to reduce boilerplate in cache integration tests Co-Authored-By: Claude Opus 4.6 Signed-off-by: Edvin Norling --- pkg/webhook/lrucache.go | 8 +++- pkg/webhook/lrucache_test.go | 2 +- pkg/webhook/validator_test.go | 84 ++++++++++++----------------------- 3 files changed, 35 insertions(+), 59 deletions(-) diff --git a/pkg/webhook/lrucache.go b/pkg/webhook/lrucache.go index d24ef5b05..86c7c1802 100644 --- a/pkg/webhook/lrucache.go +++ b/pkg/webhook/lrucache.go @@ -1,5 +1,5 @@ // -// Copyright 2022 The Sigstore Authors. +// Copyright 2026 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -53,5 +53,9 @@ func (c *LRUCache) Set(_ context.Context, image, name, uid, resourceVersion stri if cacheResult.PolicyResult == nil { return } - c.cache.Add(cacheKeyFor(image, uid, resourceVersion), cacheResult) + copied := &CacheResult{ + PolicyResult: cacheResult.PolicyResult, + Errors: append([]error(nil), cacheResult.Errors...), + } + c.cache.Add(cacheKeyFor(image, uid, resourceVersion), copied) } diff --git a/pkg/webhook/lrucache_test.go b/pkg/webhook/lrucache_test.go index ee19b5a0a..e42d6bdd6 100644 --- a/pkg/webhook/lrucache_test.go +++ b/pkg/webhook/lrucache_test.go @@ -1,5 +1,5 @@ // -// Copyright 2022 The Sigstore Authors. +// Copyright 2026 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/webhook/validator_test.go b/pkg/webhook/validator_test.go index d4d7ef63a..d97bbb7e6 100644 --- a/pkg/webhook/validator_test.go +++ b/pkg/webhook/validator_test.go @@ -35,6 +35,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" + "github.com/google/go-containerregistry/pkg/authn" "github.com/google/go-containerregistry/pkg/authn/k8schain" "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" @@ -4235,6 +4236,29 @@ func TestDiscoverAttestationsOCI11PartialProcessingFailure(t *testing.T) { } } +// cacheTestFixtures returns common test fixtures for cache integration tests. +func cacheTestFixtures(t *testing.T) (*ecdsa.PublicKey, name.Reference, authn.Keychain) { + t.Helper() + + var authorityKeyCosignPub *ecdsa.PublicKey + pems := parsePems([]byte(authorityKeyCosignPubString)) + if len(pems) > 0 { + key, _ := x509.ParsePKIXPublicKey(pems[0].Bytes) + authorityKeyCosignPub = key.(*ecdsa.PublicKey) + } else { + t.Fatal("Error parsing authority key from string") + } + + digest := name.MustParseReference("gcr.io/distroless/static:nonroot@sha256:be5d77c62dbe7fedfb0a4e5ec2f91078080800ab1f18358e5f31fcc8faa023c4") + + kc, err := k8schain.NewNoClient(context.Background()) + if err != nil { + t.Fatal(err) + } + + return authorityKeyCosignPub, digest, kc +} + func TestValidatePolicyCacheHit(t *testing.T) { // Save and restore global mock functions origCVS := cosignVerifySignatures @@ -4250,22 +4274,9 @@ func TestValidatePolicyCacheHit(t *testing.T) { return []oci.Signature{sig}, true, nil } - var authorityKeyCosignPub *ecdsa.PublicKey - pems := parsePems([]byte(authorityKeyCosignPubString)) - if len(pems) > 0 { - key, _ := x509.ParsePKIXPublicKey(pems[0].Bytes) - authorityKeyCosignPub = key.(*ecdsa.PublicKey) - } else { - t.Fatal("Error parsing authority key from string") - } - - digest := name.MustParseReference("gcr.io/distroless/static:nonroot@sha256:be5d77c62dbe7fedfb0a4e5ec2f91078080800ab1f18358e5f31fcc8faa023c4") + authorityKeyCosignPub, digest, kc := cacheTestFixtures(t) ctx := context.Background() - kc, err := k8schain.NewNoClient(ctx) - if err != nil { - t.Fatal(err) - } // Inject a real cache into context cache := NewLRUCache(10, 1*time.Hour) @@ -4319,22 +4330,9 @@ func TestValidatePolicyCacheSkipsErrors(t *testing.T) { return nil, false, errors.New("image not signed") } - var authorityKeyCosignPub *ecdsa.PublicKey - pems := parsePems([]byte(authorityKeyCosignPubString)) - if len(pems) > 0 { - key, _ := x509.ParsePKIXPublicKey(pems[0].Bytes) - authorityKeyCosignPub = key.(*ecdsa.PublicKey) - } else { - t.Fatal("Error parsing authority key from string") - } - - digest := name.MustParseReference("gcr.io/distroless/static:nonroot@sha256:be5d77c62dbe7fedfb0a4e5ec2f91078080800ab1f18358e5f31fcc8faa023c4") + authorityKeyCosignPub, digest, kc := cacheTestFixtures(t) ctx := context.Background() - kc, err := k8schain.NewNoClient(ctx) - if err != nil { - t.Fatal(err) - } cache := NewLRUCache(10, 1*time.Hour) ctx = ToContext(ctx, cache) @@ -4384,22 +4382,9 @@ func TestValidatePolicyCachePartialSuccess(t *testing.T) { return nil, false, errors.New("signature invalid") } - var authorityKeyCosignPub *ecdsa.PublicKey - pems := parsePems([]byte(authorityKeyCosignPubString)) - if len(pems) > 0 { - key, _ := x509.ParsePKIXPublicKey(pems[0].Bytes) - authorityKeyCosignPub = key.(*ecdsa.PublicKey) - } else { - t.Fatal("Error parsing authority key from string") - } - - digest := name.MustParseReference("gcr.io/distroless/static:nonroot@sha256:be5d77c62dbe7fedfb0a4e5ec2f91078080800ab1f18358e5f31fcc8faa023c4") + authorityKeyCosignPub, digest, kc := cacheTestFixtures(t) ctx := context.Background() - kc, err := k8schain.NewNoClient(ctx) - if err != nil { - t.Fatal(err) - } cache := NewLRUCache(10, 1*time.Hour) ctx = ToContext(ctx, cache) @@ -4463,22 +4448,9 @@ func TestValidatePolicyNoCacheDefault(t *testing.T) { return []oci.Signature{sig}, true, nil } - var authorityKeyCosignPub *ecdsa.PublicKey - pems := parsePems([]byte(authorityKeyCosignPubString)) - if len(pems) > 0 { - key, _ := x509.ParsePKIXPublicKey(pems[0].Bytes) - authorityKeyCosignPub = key.(*ecdsa.PublicKey) - } else { - t.Fatal("Error parsing authority key from string") - } - - digest := name.MustParseReference("gcr.io/distroless/static:nonroot@sha256:be5d77c62dbe7fedfb0a4e5ec2f91078080800ab1f18358e5f31fcc8faa023c4") + authorityKeyCosignPub, digest, kc := cacheTestFixtures(t) ctx := context.Background() - kc, err := k8schain.NewNoClient(ctx) - if err != nil { - t.Fatal(err) - } // Do NOT inject a cache - this is the default behavior // FromContext(ctx) should return NoCache From 2d1dd2646c4d1d3522e2420b0a9d8529d954fd59 Mon Sep 17 00:00:00 2001 From: Edvin Norling Date: Mon, 16 Feb 2026 19:53:16 +0100 Subject: [PATCH 05/13] feat: add cache hit/miss debug logging and e2e test resources Move cache observability into LRUCache.Get() so the implementation owns its own logging --- pkg/webhook/lrucache.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pkg/webhook/lrucache.go b/pkg/webhook/lrucache.go index 86c7c1802..fedb969a7 100644 --- a/pkg/webhook/lrucache.go +++ b/pkg/webhook/lrucache.go @@ -21,6 +21,7 @@ import ( "time" expirable "github.com/hashicorp/golang-lru/v2/expirable" + "knative.dev/pkg/logging" ) // LRUCache implements ResultCache using an LRU cache with TTL expiration. @@ -41,11 +42,13 @@ func cacheKeyFor(image, uid, resourceVersion string) string { return fmt.Sprintf("%s/%s/%s", image, uid, resourceVersion) } -func (c *LRUCache) Get(_ context.Context, image, uid, resourceVersion string) *CacheResult { +func (c *LRUCache) Get(ctx context.Context, image, uid, resourceVersion string) *CacheResult { result, ok := c.cache.Get(cacheKeyFor(image, uid, resourceVersion)) if !ok { + logging.FromContext(ctx).Debugf("cache miss for image %s, policy UID %s", image, uid) return nil } + logging.FromContext(ctx).Debugf("cache hit for image %s, policy UID %s", image, uid) return result } From 03c8dfe0d317b8c4a7b26aa2e617044b39bad6f2 Mon Sep 17 00:00:00 2001 From: Edvin Norling Date: Mon, 16 Feb 2026 22:07:11 +0100 Subject: [PATCH 06/13] intial reserach metrics --- ...2026-02-16-cache-metrics-implementation.md | 289 ++++++++++++++++++ 1 file changed, 289 insertions(+) create mode 100644 thoughts/research/2026-02-16-cache-metrics-implementation.md diff --git a/thoughts/research/2026-02-16-cache-metrics-implementation.md b/thoughts/research/2026-02-16-cache-metrics-implementation.md new file mode 100644 index 000000000..3e27f4c39 --- /dev/null +++ b/thoughts/research/2026-02-16-cache-metrics-implementation.md @@ -0,0 +1,289 @@ +--- +date: 2026-02-16T19:10:00+01:00 +researcher: Claude +git_commit: 2d1dd2646c4d1d3522e2420b0a9d8529d954fd59 +branch: metrics +repository: sigstore/policy-controller +topic: "Cache metrics implementation - library choice, metric suggestions, label-based patterns" +tags: [research, codebase, metrics, prometheus, cache, observability] +status: complete +last_updated: 2026-02-16 +last_updated_by: Claude +last_updated_note: "Updated library recommendation from prometheus/client_golang to OpenTelemetry based on knative/pkg migration" +--- + +# Research: Cache Metrics Implementation + +**Date**: 2026-02-16T19:10:00+01:00 +**Researcher**: Claude +**Git Commit**: 2d1dd2646c4d1d3522e2420b0a9d8529d954fd59 +**Branch**: metrics +**Repository**: sigstore/policy-controller + +## Research Question + +How to add Prometheus metrics for the LRU cache solution in policy-controller, which library to use, and what metrics to implement (with label-based "new style" patterns). + +## Summary + +The policy-controller currently uses `knative.dev/pkg v0.0.0-20230612155445-74c4be5e935e` (June 2023), which uses OpenCensus internally. However, **the latest knative/pkg (main branch, 2025+) has fully migrated to OpenTelemetry** (confirmed via GitHub issue knative/pkg#2174 and verified in source). The latest knative/pkg go.mod shows `go.opentelemetry.io/otel v1.39.0` as a direct dependency, with zero OpenCensus imports. Their metrics package (`observability/metrics/`) uses `go.opentelemetry.io/otel/metric` for instruments (Int64Counter, Float64Histogram, etc.) and `go.opentelemetry.io/otel/exporters/prometheus` for Prometheus export. + +**Recommendation: Use OpenTelemetry (`go.opentelemetry.io/otel/metric`)** to align with where knative/pkg is headed. This ensures forward compatibility when policy-controller eventually bumps its knative.dev/pkg dependency. The OTel Prometheus exporter will serve metrics on the same `/metrics` endpoint via `promhttp.Handler()`. + +The codebase currently has **zero custom metrics**. The cache has clear instrumentation points in `LRUCache.Get()` and `LRUCache.Set()`. + +## Detailed Findings + +### 1. Existing Observability Infrastructure + +#### Knative sharedmain provides automatic metrics +- `cmd/webhook/main.go:157` calls `sharedmain.MainWithContext()` which starts a metrics HTTP server on port 9090 +- `config/config-observability.yaml` has the template for `metrics.backend-destination: prometheus` +- `config/webhook.yaml:70-71` sets `METRICS_DOMAIN=sigstore.dev/policy` +- The `/metrics` endpoint is already live and serves controller queue/reconciliation metrics + +#### Dependencies already available (go.mod indirect) +- `github.com/prometheus/client_golang v1.21.1` (line 223) +- `github.com/prometheus/client_model v0.6.1` (line 224) +- `contrib.go.opencensus.io/exporter/prometheus v0.4.2` (line 87) +- `go.opencensus.io v0.24.0` (line 253) + +#### No custom metrics exist anywhere in the codebase +- Zero imports of `prometheus`, `opencensus`, or `opentelemetry` in any `.go` file +- No `stats.Record`, `view.Register`, `prometheus.NewCounter`, etc. calls + +### 2. Library Recommendation: OpenTelemetry (`go.opentelemetry.io/otel/metric`) + +#### knative/pkg has migrated to OpenTelemetry + +Verified by examining the latest knative/pkg source on GitHub (main branch): + +- **go.mod**: Direct dependencies on `go.opentelemetry.io/otel v1.39.0`, `go.opentelemetry.io/otel/metric v1.39.0`, `go.opentelemetry.io/otel/exporters/prometheus v0.61.0`. **Zero OpenCensus dependencies.** +- **observability/metrics/provider.go**: Uses `sdkmetric.NewMeterProvider()` from OTel SDK +- **observability/metrics/prometheus_enabled.go**: Uses `otelprom.New()` from `go.opentelemetry.io/otel/exporters/prometheus` to create a Prometheus exporter, served via `promhttp.Handler()` on port 9090 +- **observability/metrics/k8s/instruments.go**: Wraps OTel `metric.Int64Counter`, `metric.Float64Histogram`, `metric.Int64UpDownCounter`, `metric.Float64Gauge` for workqueue metrics +- **GitHub issue knative/pkg#2174**: Closed by maintainer @dprotaso with comment "We've migrated to OpenTelemetry so this isn't an issue anymore." + +#### Why OTel over prometheus/client_golang + +1. **Forward compatibility** - policy-controller currently pins `knative.dev/pkg v0.0.0-20230612155445-74c4be5e935e` (old, OpenCensus-era). When this is bumped, the metrics infrastructure will be OTel-native. Using OTel now avoids a migration later. +2. **Knative ecosystem alignment** - New knative/pkg metrics API is `go.opentelemetry.io/otel/metric`. Using the same API means custom metrics integrate cleanly with the framework's MeterProvider. +3. **OTel Prometheus exporter serves to same `/metrics` endpoint** - knative's new `prometheus_enabled.go` uses `promhttp.Handler()` which serves from `prometheus.DefaultRegistry`. The OTel Prometheus exporter automatically registers there. +4. **OTel is the CNCF standard** - not deprecated like OpenCensus, and has broader backend support than raw prometheus/client_golang. + +#### API pattern (from knative/pkg source) + +```go +import ( + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/metric" +) + +// Get a meter from the global provider +var meter = otel.Meter("policy-controller") + +// Create instruments +cacheOperations, _ := meter.Int64Counter( + "policy_controller_cache_operations", + metric.WithDescription("Total number of cache operations"), + metric.WithUnit("{operation}"), +) + +// Record with attributes (OTel equivalent of Prometheus labels) +cacheOperations.Add(ctx, 1, metric.WithAttributes( + attribute.String("result", "hit"), +)) +``` + +#### Note on current knative.dev/pkg version + +The policy-controller currently uses an old knative/pkg (June 2023) which still has OpenCensus. This means the existing `sharedmain.MainWithContext()` wires up OpenCensus exporters. For the OTel metrics to appear on `/metrics`, we need to either: +- **Option A**: Set up our own OTel MeterProvider with a Prometheus exporter (standalone, ~10 lines of setup code) +- **Option B**: Use `prometheus/client_golang` with `promauto` directly, which registers to `prometheus.DefaultRegistry` and appears via the existing OpenCensus Prometheus bridge + +**Practical recommendation**: Since bumping knative.dev/pkg is a separate effort and the current version uses OpenCensus, **use `prometheus/client_golang` with `promauto` for this PR** - it's the simplest path that works today. The metrics will appear on `/metrics` immediately. When knative/pkg is bumped, migrating to OTel instruments is straightforward (similar API, just different import). Add a TODO comment noting the planned migration. + +### 3. Cache Metrics Suggestions (Label-Based Style) + +Following the label-based pattern requested (e.g., single metric with labels rather than separate `_hit`/`_miss` metrics): + +#### Primary Cache Metrics (This PR) + +**a) `policy_controller_cache_operations_total`** - Counter with labels +```go +var cacheOperations = promauto.NewCounterVec( + prometheus.CounterOpts{ + Name: "policy_controller_cache_operations_total", + Help: "Total number of cache operations by result type.", + }, + []string{"result"}, // "hit", "miss" +) +``` +- Increment with `result="hit"` on cache hit in `LRUCache.Get()` +- Increment with `result="miss"` on cache miss in `LRUCache.Get()` +- Enables: hit rate = `rate(cache_operations_total{result="hit"}) / rate(cache_operations_total)` + +**b) `policy_controller_cache_writes_total`** - Counter with labels +```go +var cacheWrites = promauto.NewCounterVec( + prometheus.CounterOpts{ + Name: "policy_controller_cache_writes_total", + Help: "Total number of cache write operations by result type.", + }, + []string{"result"}, // "stored", "skipped" +) +``` +- `result="stored"` when a successful validation is cached in `LRUCache.Set()` +- `result="skipped"` when `PolicyResult` is nil (failed validation, not cached) + +**c) `policy_controller_cache_entries`** - Gauge +```go +var cacheEntries = prometheus.NewGauge( + prometheus.GaugeOpts{ + Name: "policy_controller_cache_entries", + Help: "Current number of entries in the validation result cache.", + }, +) +``` +- Updated on Set/eviction. The underlying `expirable.LRU` has a `Len()` method. +- Alternatively, sample on each Get/Set rather than on eviction callback. + +**d) `policy_controller_cache_evictions_total`** - Counter +```go +var cacheEvictions = promauto.NewCounter( + prometheus.CounterOpts{ + Name: "policy_controller_cache_evictions_total", + Help: "Total number of cache entries evicted (LRU or TTL).", + }, +) +``` +- The `expirable.NewLRU` constructor accepts an `onEvict` callback (currently `nil` on line 37 of `lrucache.go`). Wire this up to increment the counter. + +### 4. Future Metrics Suggestions (Not This PR) + +These are areas where metrics would add observability value to the broader policy-controller: + +#### Validation Metrics + +**a) `policy_controller_validation_duration_seconds`** - Histogram with labels +```go +var validationDuration = promauto.NewHistogramVec( + prometheus.HistogramOpts{ + Name: "policy_controller_validation_duration_seconds", + Help: "Duration of image validation in seconds.", + Buckets: prometheus.DefBuckets, + }, + []string{"result", "cached"}, + // result: "allow", "deny", "warn", "error" + // cached: "true", "false" +) +``` +- Instrument in `ValidatePolicy()` (validator.go:474) or `validateContainerImage()` (validator.go:1156) +- Shows how much time cache saves vs full validation + +**b) `policy_controller_policy_evaluations_total`** - Counter with labels +```go +var policyEvaluations = promauto.NewCounterVec( + prometheus.CounterOpts{ + Name: "policy_controller_policy_evaluations_total", + Help: "Total number of policy evaluations.", + }, + []string{"result", "mode"}, + // result: "pass", "fail" + // mode: "enforce", "warn" +) +``` +- Instrument in `validatePolicies()` (validator.go:393) + +**c) `policy_controller_images_validated_total`** - Counter with labels +```go +var imagesValidated = promauto.NewCounterVec( + prometheus.CounterOpts{ + Name: "policy_controller_images_validated_total", + Help: "Total number of container images validated.", + }, + []string{"result"}, + // result: "allow", "deny", "warn", "no_match" +) +``` +- Instrument in `validateContainerImage()` (validator.go:1156) + +#### Webhook Admission Metrics + +**d) `policy_controller_admission_requests_total`** - Counter with labels +```go +// result: "allow", "deny", "warn" +// resource_kind: "Pod", "Deployment", etc. +``` +- Instrument at the ValidatePodSpecable/ValidatePod/etc. level + +**e) `policy_controller_admission_duration_seconds`** - Histogram +- End-to-end webhook response time per admission request + +#### Signature/Attestation Verification Metrics + +**f) `policy_controller_signature_verifications_total`** - Counter with labels +```go +// type: "signature", "attestation" +// method: "key", "keyless", "static", "rfc3161" +// result: "success", "failure" +``` +- Instrument in `ValidatePolicySignaturesForAuthority()` and `ValidatePolicyAttestationsForAuthority()` + +### 5. Implementation Location + +The natural place for the metrics definitions is a new file: +``` +pkg/webhook/metrics.go +``` + +The instrumentation points are: +- `pkg/webhook/lrucache.go:45-53` (Get - hit/miss) +- `pkg/webhook/lrucache.go:55-64` (Set - stored/skipped) +- `pkg/webhook/lrucache.go:37` (onEvict callback in NewLRUCache) + +For the `NoCache` implementation (`pkg/webhook/nocache.go`), no metrics should be emitted since the cache is disabled. + +### 6. Metric Naming Conventions + +Following Prometheus naming best practices and the label-based style: +- Prefix: `policy_controller_` (matches the component name from `sharedmain.MainWithContext(ctx, "policy-controller", ...)`) +- Units in suffix: `_seconds`, `_bytes`, `_total` for counters +- Use labels for dimensions rather than separate metric names +- Example: `policy_controller_cache_operations_total{result="hit"}` not `policy_controller_cache_hits_total` + +## Code References + +- `cmd/webhook/main.go:157` - sharedmain.MainWithContext bootstrap (metrics server) +- `cmd/webhook/main.go:107-111` - Cache CLI flags (enable-cache, cache-size, cache-ttl) +- `cmd/webhook/main.go:247-251` - Cache initialization +- `pkg/webhook/cache.go:47-56` - ResultCache interface +- `pkg/webhook/lrucache.go:35-38` - NewLRUCache constructor (onEvict callback is nil) +- `pkg/webhook/lrucache.go:45-53` - Get() method (hit/miss instrumentation point) +- `pkg/webhook/lrucache.go:55-64` - Set() method (store/skip instrumentation point) +- `pkg/webhook/nocache.go:23-31` - NoCache implementation (no metrics should be emitted) +- `pkg/webhook/validator.go:474-479` - Cache lookup in ValidatePolicy +- `pkg/webhook/validator.go:637-640` - Cache write in ValidatePolicy +- `config/config-observability.yaml` - Observability config template +- `go.mod:223` - prometheus/client_golang v1.21.1 (indirect) + +## Architecture Documentation + +### Cache Flow +1. Admission request -> `validateContainerImage()` -> `validatePolicies()` -> `ValidatePolicy()` +2. `ValidatePolicy()` checks cache via `FromContext(ctx).Get()` (validator.go:476) +3. If cache miss, full validation occurs +4. If validation succeeds (PolicyResult != nil), result cached via `FromContext(ctx).Set()` (validator.go:637) +5. Failed validations (PolicyResult nil) are NOT cached, allowing retries + +### Metrics Endpoint +- knative's `sharedmain` starts metrics server on port 9090 +- Current knative.dev/pkg version (June 2023): OpenCensus Prometheus exporter bridges to `prometheus.DefaultRegistry` +- Latest knative.dev/pkg (2025+): OTel Prometheus exporter via `go.opentelemetry.io/otel/exporters/prometheus`, served via `promhttp.Handler()` on same port +- In both cases, metrics registered with `promauto` on `prometheus.DefaultRegistry` appear on `/metrics` + +## Open Questions + +1. **Should `NoCache` emit a "disabled" metric?** - Could add a gauge `policy_controller_cache_enabled{} 0/1` to indicate cache state +2. **Cardinality concerns** - Should any labels include image name or policy name? Probably not for cache metrics (high cardinality), but worth considering for future validation metrics +3. **Histogram buckets for validation duration** - What are typical validation times? This affects bucket configuration for future duration histograms From e02762304539794b69baffc5da6238df57744c4d Mon Sep 17 00:00:00 2001 From: Edvin Norling Date: Mon, 16 Feb 2026 22:33:10 +0100 Subject: [PATCH 07/13] chore(deps): bump knative.dev/pkg to latest for OTEL metrics Bump knative.dev/pkg from v0.0.0-20230612155445 to v0.0.0-20260213150858 to enable OTEL metrics support. Remove stale replace directives for k8s.io/code-generator and k8s.io/kube-openapi that were pinning old incompatible versions. Co-Authored-By: Claude Opus 4.6 --- go.mod | 145 ++++++------ go.sum | 698 ++++++++++++++------------------------------------------- 2 files changed, 245 insertions(+), 598 deletions(-) diff --git a/go.mod b/go.mod index a1e67c177..5b2fd7658 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,6 @@ go 1.25.0 require ( github.com/aws/aws-sdk-go v1.55.6 github.com/aws/aws-sdk-go-v2 v1.36.3 // indirect - github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/go-cmp v0.7.0 github.com/google/go-containerregistry v0.20.3 @@ -29,28 +28,28 @@ require ( github.com/sigstore/cosign/v2 v2.5.0 github.com/sigstore/rekor v1.3.10 github.com/sigstore/sigstore v1.9.4 - github.com/stretchr/testify v1.10.0 + github.com/stretchr/testify v1.11.1 github.com/theupdateframework/go-tuf v0.7.0 github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 - go.uber.org/zap v1.27.0 - golang.org/x/crypto v0.45.0 - golang.org/x/net v0.47.0 - golang.org/x/sys v0.38.0 // indirect + go.uber.org/zap v1.27.1 + golang.org/x/crypto v0.47.0 + golang.org/x/net v0.49.0 + golang.org/x/sys v0.40.0 // indirect golang.org/x/time v0.11.0 - google.golang.org/grpc v1.71.1 // indirect - google.golang.org/protobuf v1.36.6 + google.golang.org/grpc v1.77.0 // indirect + google.golang.org/protobuf v1.36.10 gopkg.in/yaml.v3 v3.0.1 - k8s.io/api v0.32.3 - k8s.io/apimachinery v0.32.3 - k8s.io/client-go v0.32.3 - k8s.io/code-generator v0.32.2 - k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f - knative.dev/hack v0.0.0-20240111013919-e89096d74d85 + k8s.io/api v0.35.0 + k8s.io/apimachinery v0.35.0 + k8s.io/client-go v0.35.0 + k8s.io/code-generator v0.35.0 + k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 + knative.dev/hack v0.0.0-20260212092700-0126b283bf20 sigs.k8s.io/release-utils v0.11.1 - sigs.k8s.io/yaml v1.4.0 + sigs.k8s.io/yaml v1.6.0 ) -require github.com/spf13/cobra v1.9.1 +require github.com/spf13/cobra v1.10.0 require ( github.com/Azure/azure-sdk-for-go v68.0.0+incompatible @@ -61,7 +60,7 @@ require ( github.com/docker/docker v28.1.1+incompatible github.com/docker/docker-credential-helpers v0.9.3 github.com/docker/go-connections v0.5.0 - github.com/go-jose/go-jose/v4 v4.1.0 + github.com/go-jose/go-jose/v4 v4.1.3 github.com/hashicorp/golang-lru/v2 v2.0.7 github.com/sigstore/protobuf-specs v0.4.1 github.com/sigstore/scaffolding v0.7.22 @@ -72,19 +71,17 @@ require ( github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.9.4 github.com/spf13/viper v1.20.1 knative.dev/hack/schema v0.0.0-20240607132042-09143140a254 - knative.dev/pkg v0.0.0-20230612155445-74c4be5e935e + knative.dev/pkg v0.0.0-20260213150858-6758a9ff4767 ) require ( cloud.google.com/go v0.120.0 // indirect cloud.google.com/go/auth v0.16.0 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect - cloud.google.com/go/compute/metadata v0.6.0 // indirect + cloud.google.com/go/compute/metadata v0.9.0 // indirect cloud.google.com/go/iam v1.5.0 // indirect cloud.google.com/go/kms v1.21.2 // indirect cloud.google.com/go/longrunning v0.6.6 // indirect - contrib.go.opencensus.io/exporter/ocagent v0.7.1-0.20200907061046-05415f1de66d // indirect - contrib.go.opencensus.io/exporter/prometheus v0.4.2 // indirect cuelang.org/go v0.12.1 // indirect github.com/AliyunContainerService/ack-ram-tool/pkg/credentials/provider v0.14.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect @@ -134,7 +131,7 @@ require ( github.com/blang/semver v3.5.1+incompatible // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/blendle/zapdriver v1.3.1 // indirect - github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect + github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/chrismellard/docker-credential-acr-env v0.0.0-20230304212654-82a0ddb27589 // indirect github.com/clbanning/mxj/v2 v2.7.0 // indirect @@ -152,18 +149,16 @@ require ( github.com/docker/distribution v2.8.3+incompatible // indirect github.com/docker/go-units v0.5.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect - github.com/emicklei/go-restful/v3 v3.11.0 // indirect - github.com/evanphx/json-patch v5.6.0+incompatible // indirect - github.com/evanphx/json-patch/v5 v5.7.0 // indirect + github.com/emicklei/go-restful/v3 v3.12.2 // indirect + github.com/evanphx/json-patch/v5 v5.9.11 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/fsnotify/fsnotify v1.8.0 // indirect - github.com/fxamacker/cbor/v2 v2.7.0 // indirect + github.com/fsnotify/fsnotify v1.9.0 // indirect + github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/go-chi/chi v4.1.2+incompatible // indirect github.com/go-ini/ini v1.67.0 // indirect - github.com/go-kit/log v0.2.1 // indirect - github.com/go-logfmt/logfmt v0.5.1 // indirect - github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-logr/zapr v1.3.0 // indirect github.com/go-openapi/analysis v0.23.0 // indirect github.com/go-openapi/errors v0.22.1 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect @@ -175,23 +170,21 @@ require ( github.com/go-openapi/swag v0.23.1 // indirect github.com/go-openapi/validate v0.24.0 // indirect github.com/go-viper/mapstructure/v2 v2.4.0 // indirect - github.com/gobuffalo/flect v1.0.2 // indirect + github.com/gobuffalo/flect v1.0.3 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.5.2 // indirect - github.com/golang-jwt/jwt/v5 v5.2.2 // indirect - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang-jwt/jwt/v5 v5.3.0 // indirect github.com/google/certificate-transparency-go v1.3.1 // indirect - github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect + github.com/google/gnostic-models v0.7.0 // indirect github.com/google/go-github/v55 v55.0.0 // indirect github.com/google/go-querystring v1.1.0 // indirect - github.com/google/gofuzz v1.2.0 // indirect github.com/google/s2a-go v0.1.9 // indirect github.com/google/uuid v1.6.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect github.com/googleapis/gax-go/v2 v2.14.1 // indirect github.com/gorilla/mux v1.8.1 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 // indirect github.com/hashicorp/vault/api v1.16.0 // indirect github.com/in-toto/attestation v1.1.1 // indirect github.com/in-toto/in-toto-golang v0.9.0 // indirect @@ -207,7 +200,7 @@ require ( github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/sys/atomicwriter v0.1.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect github.com/mozillazg/docker-credential-acr-helper v0.4.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481 // indirect @@ -220,11 +213,11 @@ require ( github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/client_golang v1.21.1 // indirect - github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.62.0 // indirect - github.com/prometheus/procfs v0.15.1 // indirect - github.com/prometheus/statsd_exporter v0.22.8 // indirect + github.com/prometheus/client_golang v1.23.2 // indirect + github.com/prometheus/client_model v0.6.2 // indirect + github.com/prometheus/common v0.67.4 // indirect + github.com/prometheus/otlptranslator v1.0.0 // indirect + github.com/prometheus/procfs v0.19.2 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/sagikazarmark/locafero v0.7.0 // indirect github.com/sassoftware/relic v7.2.1+incompatible // indirect @@ -235,7 +228,7 @@ require ( github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.12.0 // indirect github.com/spf13/cast v1.7.1 // indirect - github.com/spf13/pflag v1.0.6 // indirect + github.com/spf13/pflag v1.0.10 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect github.com/tchap/go-patricia/v2 v2.3.2 // indirect @@ -250,47 +243,51 @@ require ( github.com/yashtewari/glob-intersection v0.2.0 // indirect gitlab.com/gitlab-org/api/client-go v0.127.0 // indirect go.mongodb.org/mongo-driver v1.14.0 // indirect - go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect - go.opentelemetry.io/otel v1.35.0 // indirect - go.opentelemetry.io/otel/metric v1.35.0 // indirect - go.opentelemetry.io/otel/sdk v1.35.0 // indirect - go.opentelemetry.io/otel/trace v1.35.0 // indirect - go.uber.org/atomic v1.9.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0 // indirect + go.opentelemetry.io/contrib/instrumentation/runtime v0.64.0 // indirect + go.opentelemetry.io/otel v1.39.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.39.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.39.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.39.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.61.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.39.0 // indirect + go.opentelemetry.io/otel/metric v1.39.0 // indirect + go.opentelemetry.io/otel/sdk v1.39.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.39.0 // indirect + go.opentelemetry.io/otel/trace v1.39.0 // indirect + go.opentelemetry.io/proto/otlp v1.9.0 // indirect go.uber.org/automaxprocs v1.6.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect - golang.org/x/mod v0.29.0 // indirect - golang.org/x/oauth2 v0.29.0 // indirect - golang.org/x/sync v0.18.0 // indirect - golang.org/x/term v0.37.0 // indirect - golang.org/x/text v0.31.0 // indirect - golang.org/x/tools v0.38.0 // indirect - gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect + go.yaml.in/yaml/v2 v2.4.3 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac // indirect + golang.org/x/mod v0.32.0 // indirect + golang.org/x/oauth2 v0.32.0 // indirect + golang.org/x/sync v0.19.0 // indirect + golang.org/x/term v0.39.0 // indirect + golang.org/x/text v0.33.0 // indirect + golang.org/x/tools v0.41.0 // indirect + gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect google.golang.org/api v0.229.0 // indirect google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250414145226-207652e42e2e // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e // indirect - gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect + gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect - k8s.io/apiextensions-apiserver v0.27.6 // indirect - k8s.io/gengo v0.0.0-20230829151522-9cce18d56c01 // indirect + k8s.io/apiextensions-apiserver v0.35.0 // indirect + k8s.io/gengo/v2 v2.0.0-20250922181213-ec3ebc5fd46b // indirect k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/utils v0.0.0-20241210054802-24370beab758 // indirect - sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect + k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 // indirect + sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect + sigs.k8s.io/randfill v1.0.0 // indirect + sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect ) // TODO: this dependency causes issues on webhook startup due // to conflicting "log_dir" flags between this and klog (knative) replace github.com/golang/glog => github.com/jdolitsky/glog v0.0.0-20220729172235-78744e90d087 - -replace ( - // knative deps require to use an old k8s.io/gengo so we need to replace these ones - k8s.io/code-generator => k8s.io/code-generator v0.29.4 - k8s.io/kube-openapi => k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 -) diff --git a/go.sum b/go.sum index 898774e1a..72d73fbee 100644 --- a/go.sum +++ b/go.sum @@ -1,58 +1,22 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= cloud.google.com/go v0.120.0 h1:wc6bgG9DHyKqF5/vQvX1CiZrtHnxJjBlKUyF9nP6meA= cloud.google.com/go v0.120.0/go.mod h1:/beW32s8/pGRuj4IILWQNd4uuebeT4dkOhKmkfit64Q= cloud.google.com/go/auth v0.16.0 h1:Pd8P1s9WkcrBE2n/PhAwKsdrR35V3Sg2II9B+ndM3CU= cloud.google.com/go/auth v0.16.0/go.mod h1:1howDHJ5IETh/LwYs3ZxvlkXF48aSqqJUM+5o02dNOI= cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I= -cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= +cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= cloud.google.com/go/iam v1.5.0 h1:QlLcVMhbLGOjRcGe6VTGGTyQib8dRLK2B/kYNV0+2xs= cloud.google.com/go/iam v1.5.0/go.mod h1:U+DOtKQltF/LxPEtcDLoobcsZMilSRwR7mgNL7knOpo= cloud.google.com/go/kms v1.21.2 h1:c/PRUSMNQ8zXrc1sdAUnsenWWaNXN+PzTXfXOcSFdoE= cloud.google.com/go/kms v1.21.2/go.mod h1:8wkMtHV/9Z8mLXEXr1GK7xPSBdi6knuLXIhqjuWcI6w= cloud.google.com/go/longrunning v0.6.6 h1:XJNDo5MUfMM05xK3ewpbSdmt7R2Zw+aQEMbdQR65Rbw= cloud.google.com/go/longrunning v0.6.6/go.mod h1:hyeGJUrPHcx0u2Uu1UFSoYZLn4lkMrccJig0t4FI7yw= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -contrib.go.opencensus.io/exporter/ocagent v0.7.1-0.20200907061046-05415f1de66d h1:LblfooH1lKOpp1hIhukktmSAxFkqMPFk9KR6iZ0MJNI= -contrib.go.opencensus.io/exporter/ocagent v0.7.1-0.20200907061046-05415f1de66d/go.mod h1:IshRmMJBhDfFj5Y67nVhMYTTIze91RUeT73ipWKs/GY= -contrib.go.opencensus.io/exporter/prometheus v0.4.2 h1:sqfsYl5GIY/L570iT+l93ehxaWJs2/OwXtiWwew3oAg= -contrib.go.opencensus.io/exporter/prometheus v0.4.2/go.mod h1:dvEHbiKmgvbr5pjaF9fpw1KeYcjrnC1J8B+JKjsZyRQ= cuelabs.dev/go/oci/ociregistry v0.0.0-20241125120445-2c00c104c6e1 h1:mRwydyTyhtRX2wXS3mqYWzR2qlv6KsmoKXmlz5vInjg= cuelabs.dev/go/oci/ociregistry v0.0.0-20241125120445-2c00c104c6e1/go.mod h1:5A4xfTzHTXfeVJBU6RAUf+QrlfTCW+017q/QiW+sMLg= cuelang.org/go v0.12.1 h1:5I+zxmXim9MmiN2tqRapIqowQxABv2NKTgbOspud1Eo= cuelang.org/go v0.12.1/go.mod h1:B4+kjvGGQnbkz+GuAv1dq/R308gTkp0sO28FdMrJ2Kw= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/AdamKorcz/go-fuzz-headers-1 v0.0.0-20230919221257-8b5d3ce2d11d h1:zjqpY4C7H15HjRPEenkS4SAn3Jy2eRRjkjZbGR30TOg= @@ -103,7 +67,8 @@ github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mo github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs= github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= +github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c h1:kMFnB0vCcX7IL/m9Y5LO+KQYv+t1CQOiFe6+SV2J7bE= @@ -112,12 +77,6 @@ github.com/ThalesIgnite/crypto11 v1.2.5 h1:1IiIIEqYmBvUYFeMnHqRft4bwf/O36jryEUpY github.com/ThalesIgnite/crypto11 v1.2.5/go.mod h1:ILDKtnCKiQ7zRoNxcp36Y1ZR8LBPmR2E23+wTQe/MlE= github.com/agnivade/levenshtein v1.2.1 h1:EHBY3UOn1gwdy/VbFwgo4cxecRznFk7fKWN1KOX7eoM= github.com/agnivade/levenshtein v1.2.1/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0= github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.2/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc= @@ -162,7 +121,6 @@ github.com/alibabacloud-go/tea-xml v1.1.3/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCE github.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw= github.com/aliyun/credentials-go v1.3.2 h1:L4WppI9rctC8PdlMgyTkF8bBsy9pyKQEzBD1bHMRl+g= github.com/aliyun/credentials-go v1.3.2/go.mod h1:tlpz4uys4Rn7Ik4/piGRrTbXy2uLKvePgQJJduE+Y5c= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= @@ -203,8 +161,6 @@ github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ= github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.9.1 h1:50sS0RWhGpW/yZx2KcDNEb1u1MANv5BMEkJgcieEDTA= github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.9.1/go.mod h1:ErZOtbzuHabipRTDTor0inoRlYwbsV1ovwSxjGs/uJo= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= @@ -226,11 +182,9 @@ github.com/bytecodealliance/wasmtime-go/v3 v3.0.2 h1:3uZCA/BLTIu+DqCfguByNMJa2HV github.com/bytecodealliance/wasmtime-go/v3 v3.0.2/go.mod h1:RnUjnIXxEJcL6BgCvNyzCCRzZcxCgsZCi+RNlvYor5Q= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= +github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g= -github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chrismellard/docker-credential-acr-env v0.0.0-20230304212654-82a0ddb27589 h1:krfRl01rzPzxSxyLyrChD+U+MzsBXbm0OwYYB67uF+4= @@ -246,6 +200,8 @@ github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUK github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/xds/go v0.0.0-20251022180443-0feb69152e9f h1:Y8xYupdHxryycyPlc9Y+bSQAYZnetRJ70VMVKm5CKI0= +github.com/cncf/xds/go v0.0.0-20251022180443-0feb69152e9f/go.mod h1:HlzOvOjVBOfTGSRXRyY0OiCS/3J1akRGQQpRO/7zyF4= github.com/cockroachdb/apd/v3 v3.2.1 h1:U+8j7t0axsIgvQUqthuNm82HIrYXodOV2iWLWtEaIwg= github.com/cockroachdb/apd/v3 v3.2.1/go.mod h1:klXJcjp+FffLTHlhIG69tezTDvdP065naDsHzKhYSqc= github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE= @@ -298,18 +254,22 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4 github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= -github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU= +github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emicklei/proto v1.13.4 h1:myn1fyf8t7tAqIzV91Tj9qXpvyXXGXk8OS2H6IBSc9g= github.com/emicklei/proto v1.13.4/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.13.5-0.20251024222203-75eaa193e329 h1:K+fnvUM0VZ7ZFJf0n4L/BRlnsb9pL/GuDG6FqaH+PwM= +github.com/envoyproxy/go-control-plane/envoy v1.35.0 h1:ixjkELDE+ru6idPxcHLj8LBVc2bFP7iBytj353BoHUo= +github.com/envoyproxy/go-control-plane/envoy v1.35.0/go.mod h1:09qwbGVuSWWAyN5t/b3iyVfz5+z8QWGrzkoqm/8SbEs= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= -github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch/v5 v5.7.0 h1:nJqP7uwL84RJInrohHfW0Fx3awjbm8qZeFv0nW9SYGc= -github.com/evanphx/json-patch/v5 v5.7.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= +github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8= +github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU= +github.com/evanphx/json-patch v5.9.0+incompatible h1:fBXyNpNMuTTDdquAq/uisOr2lShz4oaXpDTX2bLe7ls= +github.com/evanphx/json-patch v5.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU= +github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= @@ -323,39 +283,25 @@ github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7z github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= -github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= -github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= -github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= -github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= +github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec= github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-jose/go-jose/v3 v3.0.4 h1:Wp5HA7bLQcKnf6YYao/4kpRpVMp/yf6+pJKV8WFSaNY= github.com/go-jose/go-jose/v3 v3.0.4/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ= -github.com/go-jose/go-jose/v4 v4.1.0 h1:cYSYxd3pw5zd2FSXk2vGdn9igQU2PS8MuxrCOCl0FdY= -github.com/go-jose/go-jose/v4 v4.1.0/go.mod h1:GG/vqmYm3Von2nYiB2vGTXzdoNKE5tix5tuc6iAd+sw= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= -github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= -github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs= +github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= -github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= +github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= github.com/go-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC03zFCU= github.com/go-openapi/analysis v0.23.0/go.mod h1:9mz9ZWaSlV8TvjQHLl2mUW2PbZtemkE8yA5v22ohupo= github.com/go-openapi/errors v0.22.1 h1:kslMRRnK7NCb/CvR1q1VWuEQCEIsBGn5GgKD9e+HYhU= @@ -382,7 +328,6 @@ github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7 github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= github.com/go-sql-driver/mysql v1.9.1 h1:FrjNGn/BsJQjVRuSa8CBrM5BWA9BWoXXat3KrtSb/iI= github.com/go-sql-driver/mysql v1.9.1/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= @@ -391,13 +336,12 @@ github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= -github.com/gobuffalo/flect v1.0.2 h1:eqjPGSo2WmjgY2XlpGwo2NXgL3RucAKo4k4qQMNA5sA= -github.com/gobuffalo/flect v1.0.2/go.mod h1:A5msMlrHtLqh9umBSnvabjsMrCcCpAyzglnDvkbYKHs= +github.com/gobuffalo/flect v1.0.3 h1:xeWBM2nui+qnVvNM4S3foBhCAL2XgPU+a7FdpelbTq4= +github.com/gobuffalo/flect v1.0.3/go.mod h1:A5msMlrHtLqh9umBSnvabjsMrCcCpAyzglnDvkbYKHs= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= @@ -405,61 +349,36 @@ github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzw github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= -github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= +github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/certificate-transparency-go v1.3.1 h1:akbcTfQg0iZlANZLn0L9xOeWtyCIdeoYhKrqi5iH3Go= github.com/google/certificate-transparency-go v1.3.1/go.mod h1:gg+UQlx6caKEDQ9EElFOujyxEQEfOiQzAt6782Bvi8k= github.com/google/flatbuffers v25.2.10+incompatible h1:F3vclr7C3HpB1k9mxCGRMXq6FdUalZ6H/pNX4FP1v0Q= github.com/google/flatbuffers v25.2.10+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= -github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 h1:0VpGH+cDhbDtdcweoyCVsF3fhN8kejK6rFe/2FFX2nU= -github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49/go.mod h1:BkkQ4L1KS1xMt2aWSPStnn55ChGC0DPOn2FQYj+f25M= +github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo= +github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-containerregistry v0.20.3 h1:oNx7IdTI936V8CQRveCjaxOiegWwvM7kqkbXTpyiovI= @@ -473,44 +392,29 @@ github.com/google/go-github/v55 v55.0.0/go.mod h1:JLahOTA1DnXzhxEymmFF5PP2tSS9JV github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= -github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= github.com/google/tink/go v1.7.0 h1:6Eox8zONGebBFcCBqkVmt60LaWZa6xg1cl/DwAh/J1w= github.com/google/tink/go v1.7.0/go.mod h1:GAUOd+QE3pgj9q8VKIGTCP33c/B7eb4NhxLcgTJZStM= github.com/google/trillian v1.7.1 h1:+zX8jLM3524bAMPS+VxaDIDgsMv3/ty6DuLWerHXcek= github.com/google/trillian v1.7.1/go.mod h1:E1UMAHqpZCA8AQdrKdWmHmtUfSeiD0sDWD1cv00Xa+c= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4= github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q= github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= -github.com/grpc-ecosystem/grpc-gateway v1.14.6/go.mod h1:zdiPV4Yse/1gnckTHtghG4GkDEdKCRJduHpTxT3/jcw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 h1:e9Rjr40Z98/clHv5Yg79Is0NtosR5LXRvdr7o/6NwbA= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 h1:NmZ1PKzSTQbuGHw9DGPFomqkkLWMC+vZCkfs+FHv1Vg= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3/go.mod h1:zQrxl1YP88HQlA6i9c63DSVPFklWpGX4OWAc9bFuaH4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -530,8 +434,6 @@ github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9 github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4= github.com/hashicorp/go-sockaddr v1.0.7 h1:G+pTkSO01HpR5qCxg7lxfsFEZaG+C0VssTy/9dbT+Fw= github.com/hashicorp/go-sockaddr v1.0.7/go.mod h1:FZQbEYa1pxkQ7WLpyXJ6cbjpT8q0YgQaK/JakXqGyWw= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= @@ -543,7 +445,6 @@ github.com/hashicorp/vault/api v1.16.0/go.mod h1:KhuUhzOD8lDSk29AtzNjgAu2kxRA9jL github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef h1:A9HsByNhogrvm9cWb28sjiS3i7tcKCkflWFEkHfuAgM= github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/in-toto/attestation v1.1.1 h1:QD3d+oATQ0dFsWoNh5oT0udQ3tUrOsZZ0Fc3tSgWbzI= github.com/in-toto/attestation v1.1.1/go.mod h1:Dcq1zVwA2V7Qin8I7rgOi+i837wEf/mOZwRm047Sjys= @@ -572,17 +473,10 @@ github.com/jmhodges/clock v1.2.0 h1:eq4kys+NI0PLngzaHEe7AmPT90XMGIEySD1JfV1PDIs= github.com/jmhodges/clock v1.2.0/go.mod h1:qKjhA7x7u/lQpPB1XAqX1b1lCI/w3/fNuYpI/ZjLynI= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU= @@ -591,11 +485,6 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -614,7 +503,6 @@ github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxec github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4= github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY= github.com/miekg/pkcs11 v1.0.3-0.20190429190417-a667d056470f/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= @@ -639,16 +527,15 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mozillazg/docker-credential-acr-helper v0.4.0 h1:Uoh3Z9CcpEDnLiozDx+D7oDgRq7X+R296vAqAumnOcw= github.com/mozillazg/docker-credential-acr-helper v0.4.0/go.mod h1:2kiicb3OlPytmlNC9XGkLvVC+f0qTiJw3f/mhmeeQBg= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481 h1:Up6+btDp321ZG5/zdSLo48H9Iaq0UQGthrhWC6pCxzE= github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481/go.mod h1:yKZQO8QE2bHlgozqWDiRVqTFlLQSj30K/6SAK8EeYFw= @@ -666,14 +553,14 @@ github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vv github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= -github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM= -github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= +github.com/onsi/ginkgo/v2 v2.27.2 h1:LzwLj0b89qtIy6SSASkzlNvX6WktqurSHwkk2ipF/Ns= +github.com/onsi/ginkgo/v2 v2.27.2/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= -github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= -github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= +github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= +github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= github.com/open-policy-agent/opa v1.4.0 h1:IGO3xt5HhQKQq2axfa9memIFx5lCyaBlG+fXcgHpd3A= github.com/open-policy-agent/opa v1.4.0/go.mod h1:DNzZPKqKh4U0n0ANxcCVlw8lCSv2c+h5G/3QvSYdWZ8= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= @@ -688,59 +575,35 @@ github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNH github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= -github.com/prometheus/client_golang v1.21.1 h1:DOvXXTqVzvkIewV/CDPFdejpMCGeMcbGCQ8YOmu+Ibk= -github.com/prometheus/client_golang v1.21.1/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= +github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= -github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.35.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= -github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= -github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= -github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= -github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= -github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= -github.com/prometheus/statsd_exporter v0.22.7/go.mod h1:N/TevpjkIh9ccs6nuzY3jQn9dFqnUakOjnEuMPJJJnI= -github.com/prometheus/statsd_exporter v0.22.8 h1:Qo2D9ZzaQG+id9i5NYNGmbf1aa/KxKbB9aKfMS+Yib0= -github.com/prometheus/statsd_exporter v0.22.8/go.mod h1:/DzwbTEaFTE0Ojz5PqcSk6+PFHOPWGxdXVr6yC8eFOM= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +github.com/prometheus/common v0.67.4 h1:yR3NqWO1/UyO1w2PhUvXlGQs/PtFmoveVO0KZ4+Lvsc= +github.com/prometheus/common v0.67.4/go.mod h1:gP0fq6YjjNCLssJCQp0yk4M8W6ikLURwkdd/YKtTbyI= +github.com/prometheus/otlptranslator v1.0.0 h1:s0LJW/iN9dkIH+EnhiD3BlkkP5QVIUVEoIwkU+A6qos= +github.com/prometheus/otlptranslator v1.0.0/go.mod h1:vRYWnXvI6aWGpsdY/mOT/cbeVRBlPWtBNDb7kGR3uKM= +github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws= +github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw= github.com/protocolbuffers/txtpbfmt v0.0.0-20241112170944-20d2c9ebc01d h1:HWfigq7lB31IeJL8iy7jkUmU/PG1Sr8jVGhS749dbUA= github.com/protocolbuffers/txtpbfmt v0.0.0-20241112170944-20d2c9ebc01d/go.mod h1:jgxiZysxFPM+iWKwQwPR+y+Jvo54ARd4EisXxKYpB5c= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM= github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.13.2-0.20241226121412-a5dc8ff20d0a h1:w3tdWGKbLGBPtR/8/oO74W6hmz0qE5q0z9aqSAewaaM= -github.com/rogpeppe/go-internal v1.13.2-0.20241226121412-a5dc8ff20d0a/go.mod h1:S8kfXMp+yh77OxPD4fdM6YUknrZpQxLhvxzS4gDHENY= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= @@ -782,9 +645,6 @@ github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.9.4 h1:t9yfb6yteIDv github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.9.4/go.mod h1:m7sQxVJmDa+rsmS1m6biQxaLX83pzNS7ThUEyjOqkCU= github.com/sigstore/timestamp-authority v1.2.5 h1:W22JmwRv1Salr/NFFuP7iJuhytcZszQjldoB8GiEdnw= github.com/sigstore/timestamp-authority v1.2.5/go.mod h1:gWPKWq4HMWgPCETre0AakgBzcr9DRqHrsgbrRqsigOs= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA= @@ -798,34 +658,31 @@ github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs= github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4= github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= -github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= -github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/cobra v1.10.0 h1:a5/WeUlSDCvV5a45ljW2ZFtV0bTDpkfSAj3uqB6Sc+0= +github.com/spf13/cobra v1.10.0/go.mod h1:9dhySC7dnTtEiqzmqfkLj47BslqLCUPMXjG2lj/NgoE= +github.com/spf13/pflag v1.0.8/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= -github.com/spiffe/go-spiffe/v2 v2.5.0 h1:N2I01KCUkv1FAjZXJMwh95KK1ZIQLYbPfhaxw8WS0hE= -github.com/spiffe/go-spiffe/v2 v2.5.0/go.mod h1:P+NxobPc6wXhVtINNtFjNWGBTreew1GBUCwT2wPmb7g= +github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo= +github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/stvp/go-udp-testing v0.0.0-20201019212854-469649b16807/go.mod h1:7jxmlfBCDBXRzr0eAQJ48XC1hBu1np4CS5+cHEYfwpc= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= @@ -861,57 +718,53 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/yashtewari/glob-intersection v0.2.0 h1:8iuHdN88yYuCzCdjt0gDe+6bAhUwBeEWqThExu54RFg= github.com/yashtewari/glob-intersection v0.2.0/go.mod h1:LK7pIC3piUjovexikBbJ26Yml7g8xa5bsjfx2v1fwok= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zalando/go-keyring v0.2.3 h1:v9CUu9phlABObO4LPWycf+zwMG7nlbb3t/B5wa97yms= github.com/zalando/go-keyring v0.2.3/go.mod h1:HL4k+OXQfJUWaMnqyuSOc0drfGPX2b51Du6K+MRgZMk= -github.com/zeebo/errs v1.4.0 h1:XNdoD/RRMKP7HD0UhJnIzUy74ISdGGxURlYG8HSWSfM= -github.com/zeebo/errs v1.4.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4= gitlab.com/gitlab-org/api/client-go v0.127.0 h1:8xnxcNKGF2gDazEoMs+hOZfOspSSw8D0vAoWhQk9U+U= gitlab.com/gitlab-org/api/client-go v0.127.0/go.mod h1:bYC6fPORKSmtuPRyD9Z2rtbAjE7UeNatu2VWHRf4/LE= go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80= go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= -go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= -go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ= -go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= -go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 h1:xJ2qHD0C1BeYVTLLR9sX12+Qb95kfeD/byKj6Ky1pXg= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0/go.mod h1:u5BF1xyjstDowA1R5QAO9JHzqK+ublenEW/dyqTjBVk= -go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= -go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= -go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= -go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= -go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o= -go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w= -go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= -go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= -go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4= -go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0 h1:ssfIgGNANqpVFCndZvcuyKbl0g+UAVcbBcqGkG28H0Y= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0/go.mod h1:GQ/474YrbE4Jx8gZ4q5I4hrhUzM6UPzyrqJYV2AqPoQ= +go.opentelemetry.io/contrib/instrumentation/runtime v0.64.0 h1:/+/+UjlXjFcdDlXxKL1PouzX8Z2Vl0OxolRKeBEgYDw= +go.opentelemetry.io/contrib/instrumentation/runtime v0.64.0/go.mod h1:Ldm/PDuzY2DP7IypudopCR3OCOW42NJlN9+mNEroevo= +go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= +go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.39.0 h1:cEf8jF6WbuGQWUVcqgyWtTR0kOOAWY1DYZ+UhvdmQPw= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.39.0/go.mod h1:k1lzV5n5U3HkGvTCJHraTAGJ7MqsgL1wrGwTj1Isfiw= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.39.0 h1:nKP4Z2ejtHn3yShBb+2KawiXgpn8In5cT7aO2wXuOTE= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.39.0/go.mod h1:NwjeBbNigsO4Aj9WgM0C+cKIrxsZUaRmZUO7A8I7u8o= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0 h1:f0cb2XPmrqn4XMy9PNliTgRKJgS5WcL/u0/WRYGz4t0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0/go.mod h1:vnakAaFckOMiMtOIhFI2MNH4FYrZzXCYxmb1LlhoGz8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0 h1:in9O8ESIOlwJAEGTkkf34DesGRAc/Pn8qJ7k3r/42LM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0/go.mod h1:Rp0EXBm5tfnv0WL+ARyO/PHBEaEAT8UUHQ6AGJcSq6c= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.39.0 h1:Ckwye2FpXkYgiHX7fyVrN1uA/UYd9ounqqTuSNAv0k4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.39.0/go.mod h1:teIFJh5pW2y+AN7riv6IBPX2DuesS3HgP39mwOspKwU= +go.opentelemetry.io/otel/exporters/prometheus v0.61.0 h1:cCyZS4dr67d30uDyh8etKM2QyDsQ4zC9ds3bdbrVoD0= +go.opentelemetry.io/otel/exporters/prometheus v0.61.0/go.mod h1:iivMuj3xpR2DkUrUya3TPS/Z9h3dz7h01GxU+fQBRNg= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.39.0 h1:8UPA4IbVZxpsD76ihGOQiFml99GPAEZLohDXvqHdi6U= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.39.0/go.mod h1:MZ1T/+51uIVKlRzGw1Fo46KEWThjlCBZKl2LzY5nv4g= +go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= +go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= +go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= +go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= +go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= +go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= +go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= +go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= +go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A= +go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4= go.step.sm/crypto v0.60.0 h1:UgSw8DFG5xUOGB3GUID17UA32G4j1iNQ4qoMhBmsVFw= go.step.sm/crypto v0.60.0/go.mod h1:Ep83Lv818L4gV0vhFTdPWRKnL6/5fRMpi8SaoP5ArSw= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= -go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= @@ -920,12 +773,13 @@ go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= -go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= +go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= +go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -939,83 +793,35 @@ golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58 golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= -golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= +golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= +golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo= -golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac h1:l5+whBCLH3iH2ZNHYLbAe58bo7yrN4mVcnkHDYz5vvs= +golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac/go.mod h1:hH+7mtFmImwwcMvScyxUhjuVHR3HGaDPMn9rMSUUbxo= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= -golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= +golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c= +golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= @@ -1025,81 +831,40 @@ golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= -golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= +golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= +golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98= -golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= +golang.org/x/oauth2 v0.32.0 h1:jsCblLleRMDrxMN29H3z/k1KliIvpLgCkE6R8FXXNgY= +golang.org/x/oauth2 v0.32.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= -golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1110,8 +875,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= -golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= +golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= @@ -1120,11 +885,9 @@ golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= -golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= -golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY= +golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -1135,173 +898,72 @@ golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= -golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= +golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= -golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= +golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc= +golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg= +golang.org/x/tools/go/expect v0.1.1-deprecated h1:jpBZDwmgPhXsKZC6WhL20P4b/wmnpsEAGHaNy0n/rJM= +golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY= +golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM= +golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated/go.mod h1:RVAQXBGNv1ib0J382/DPCRS/BPnsGebyM1Gj5VSDpG8= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= -gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.25.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0= +gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= google.golang.org/api v0.229.0 h1:p98ymMtqeJ5i3lIBMj5MpR9kzIIgzpHHh8vQ+vgAzx8= google.golang.org/api v0.229.0/go.mod h1:wyDfmq5g1wYJWn29O22FDWN48P7Xcz0xz+LBpptYvB0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200527145253-8367513e4ece/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb h1:ITgPrl429bc6+2ZraNSzMDk3I95nmQln2fuPstKwFDE= google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:sAo5UzpjUwgFBCzupwhcLcxHVDK7vG5IqI30YnwX2eE= -google.golang.org/genproto/googleapis/api v0.0.0-20250414145226-207652e42e2e h1:UdXH7Kzbj+Vzastr5nVfccbmFsmYNygVLSPk1pEfDoY= -google.golang.org/genproto/googleapis/api v0.0.0-20250414145226-207652e42e2e/go.mod h1:085qFyf2+XaZlRdCgKNCIZ3afY2p4HHZdoIRpId8F4A= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e h1:ztQaXfzEXTmCBvbtWYRhJxW+0iJcz2qXfd38/e9l7bA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 h1:fCvbg86sFXwdrl5LgVcTEvNC+2txB5mgROGmRL5mrls= +google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.71.1 h1:ffsFWr7ygTUscGPI0KKK6TLrGz0476KUvvsbqWK0rPI= -google.golang.org/grpc v1.71.1/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= +google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM= +google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= -google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= +google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= -gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= +gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo= +gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= @@ -1310,14 +972,10 @@ gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= @@ -1325,48 +983,40 @@ gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.1.0 h1:rVV8Tcg/8jHUkPUorwjaMTtemIMVXfIPKiOqnhEhakk= gotest.tools/v3 v3.1.0/go.mod h1:fHy7eyTmJFO5bQbUsEGQ1v4m2J3Jz9eWL54TP2/ZuYQ= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.32.3 h1:Hw7KqxRusq+6QSplE3NYG4MBxZw1BZnq4aP4cJVINls= -k8s.io/api v0.32.3/go.mod h1:2wEDTXADtm/HA7CCMD8D8bK4yuBUptzaRhYcYEEYA3k= -k8s.io/apiextensions-apiserver v0.27.6 h1:mOwSBJtThZhpJr+8gEkc3wFDIjq87E3JspR5mtZxIg8= -k8s.io/apiextensions-apiserver v0.27.6/go.mod h1:AVNlLYRrESG5Poo6ASRUhY2pvoKPcNt8y/IuZ4lx3o8= -k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U= -k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= -k8s.io/client-go v0.32.3 h1:RKPVltzopkSgHS7aS98QdscAgtgah/+zmpAogooIqVU= -k8s.io/client-go v0.32.3/go.mod h1:3v0+3k4IcT9bXTc4V2rt+d2ZPPG700Xy6Oi0Gdl2PaY= -k8s.io/code-generator v0.29.4 h1:8ESudFNbY5/9BzB8KOEFG2uV9Q0AQxkc4mrQESr30Ks= -k8s.io/code-generator v0.29.4/go.mod h1:7TYnI0dYItL2cKuhhgPSuF3WED9uMdELgbVXFfn/joE= -k8s.io/gengo v0.0.0-20230829151522-9cce18d56c01 h1:pWEwq4Asjm4vjW7vcsmijwBhOr1/shsbSYiWXmNGlks= -k8s.io/gengo v0.0.0-20230829151522-9cce18d56c01/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= -k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= +k8s.io/api v0.35.0 h1:iBAU5LTyBI9vw3L5glmat1njFK34srdLmktWwLTprlY= +k8s.io/api v0.35.0/go.mod h1:AQ0SNTzm4ZAczM03QH42c7l3bih1TbAXYo0DkF8ktnA= +k8s.io/apiextensions-apiserver v0.35.0 h1:3xHk2rTOdWXXJM+RDQZJvdx0yEOgC0FgQ1PlJatA5T4= +k8s.io/apiextensions-apiserver v0.35.0/go.mod h1:E1Ahk9SADaLQ4qtzYFkwUqusXTcaV2uw3l14aqpL2LU= +k8s.io/apimachinery v0.35.0 h1:Z2L3IHvPVv/MJ7xRxHEtk6GoJElaAqDCCU0S6ncYok8= +k8s.io/apimachinery v0.35.0/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns= +k8s.io/client-go v0.35.0 h1:IAW0ifFbfQQwQmga0UdoH0yvdqrbwMdq9vIFEhRpxBE= +k8s.io/client-go v0.35.0/go.mod h1:q2E5AAyqcbeLGPdoRB+Nxe3KYTfPce1Dnu1myQdqz9o= +k8s.io/code-generator v0.35.0 h1:TvrtfKYZTm9oDF2z+veFKSCcgZE3Igv0svY+ehCmjHQ= +k8s.io/code-generator v0.35.0/go.mod h1:iS1gvVf3c/T71N5DOGYO+Gt3PdJ6B9LYSvIyQ4FHzgc= +k8s.io/gengo/v2 v2.0.0-20250922181213-ec3ebc5fd46b h1:gMplByicHV/TJBizHd9aVEsTYoJBnnUAT5MHlTkbjhQ= +k8s.io/gengo/v2 v2.0.0-20250922181213-ec3ebc5fd46b/go.mod h1:CgujABENc3KuTrcsdpGmrrASjtQsWCT7R99mEV4U/fM= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780= -k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= -k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0= -k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -knative.dev/hack v0.0.0-20240111013919-e89096d74d85 h1:ERgPObDcW9LfaEPAeFvbW3UJcF3C3ul6B2ErNMv13OE= -knative.dev/hack v0.0.0-20240111013919-e89096d74d85/go.mod h1:yk2OjGDsbEnQjfxdm0/HJKS2WqTLEFg/N6nUs6Rqx3Q= +k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 h1:Y3gxNAuB0OBLImH611+UDZcmKS3g6CthxToOb37KgwE= +k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ= +k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzkbzn+gDM4X9T4Ck= +k8s.io/utils v0.0.0-20251002143259-bc988d571ff4/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +knative.dev/hack v0.0.0-20260212092700-0126b283bf20 h1:Ocya6ILPQxGrozD5gPELC4J2ASnqvTLvYGJjddKr4Fs= +knative.dev/hack v0.0.0-20260212092700-0126b283bf20/go.mod h1:L5RzHgbvam0u8QFHfzCX6MKxu/a/gIGEdaRBqNiVbl0= knative.dev/hack/schema v0.0.0-20240607132042-09143140a254 h1:b9hFHGtxx0Kpm4EEjSD72lL0jms91To3OEVBTbqfOYI= knative.dev/hack/schema v0.0.0-20240607132042-09143140a254/go.mod h1:3pWwBLnTZSM9psSgCAvhKOHIPTzqfEMlWRpDu6IYhK0= -knative.dev/pkg v0.0.0-20230612155445-74c4be5e935e h1:koM+NopG2Yw738NlJhQF3ZwpyS+HHznuLm294VYlUKg= -knative.dev/pkg v0.0.0-20230612155445-74c4be5e935e/go.mod h1:dqC6IrvyBE7E+oZocs5PkVhq1G59pDTA7r8U17EAKMk= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= -sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= +knative.dev/pkg v0.0.0-20260213150858-6758a9ff4767 h1:YdHqjwH17zWC07SHoMJpUGBXPgIvaT39L/xOf+zXauc= +knative.dev/pkg v0.0.0-20260213150858-6758a9ff4767/go.mod h1:5xKzUQ2tEzgv+onqMQr6oEiQj3RtgehEc36GqeA97Ms= +sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= +sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= +sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= +sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= sigs.k8s.io/release-utils v0.11.1 h1:hzvXGpHgHJfLOJB6TRuu14bzWc3XEglHmXHJqwClSZE= sigs.k8s.io/release-utils v0.11.1/go.mod h1:ybR2V/uQAOGxYfzYtBenSYeXWkBGNP2qnEiX77ACtpc= -sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA= -sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= -sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= -sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= +sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco= +sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= +sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= +sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= software.sslmate.com/src/go-pkcs12 v0.4.0 h1:H2g08FrTvSFKUj+D309j1DPfk5APnIdAQAB8aEykJ5k= software.sslmate.com/src/go-pkcs12 v0.4.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI= From cd4a38daa5a13f5da2bcc1db64ccbe873b2ac3e4 Mon Sep 17 00:00:00 2001 From: Edvin Norling Date: Mon, 16 Feb 2026 22:49:14 +0100 Subject: [PATCH 08/13] make reserach decent.... --- ...2026-02-16-cache-metrics-implementation.md | 300 ++++++++---------- 1 file changed, 133 insertions(+), 167 deletions(-) diff --git a/thoughts/research/2026-02-16-cache-metrics-implementation.md b/thoughts/research/2026-02-16-cache-metrics-implementation.md index 3e27f4c39..e8b321ff4 100644 --- a/thoughts/research/2026-02-16-cache-metrics-implementation.md +++ b/thoughts/research/2026-02-16-cache-metrics-implementation.md @@ -1,271 +1,237 @@ --- date: 2026-02-16T19:10:00+01:00 researcher: Claude -git_commit: 2d1dd2646c4d1d3522e2420b0a9d8529d954fd59 +git_commit: e0276230 branch: metrics repository: sigstore/policy-controller -topic: "Cache metrics implementation - library choice, metric suggestions, label-based patterns" -tags: [research, codebase, metrics, prometheus, cache, observability] +topic: "Cache metrics implementation - OTEL metrics with knative/pkg" +tags: [research, codebase, metrics, opentelemetry, cache, observability] status: complete last_updated: 2026-02-16 last_updated_by: Claude -last_updated_note: "Updated library recommendation from prometheus/client_golang to OpenTelemetry based on knative/pkg migration" +last_updated_note: "Rewrote after bumping knative/pkg to v0.0.0-20260213150858. Corrected metric naming to OTEL conventions, documented knative metrics infrastructure." --- # Research: Cache Metrics Implementation **Date**: 2026-02-16T19:10:00+01:00 **Researcher**: Claude -**Git Commit**: 2d1dd2646c4d1d3522e2420b0a9d8529d954fd59 +**Git Commit**: e0276230 **Branch**: metrics **Repository**: sigstore/policy-controller ## Research Question -How to add Prometheus metrics for the LRU cache solution in policy-controller, which library to use, and what metrics to implement (with label-based "new style" patterns). +How to add metrics for the LRU cache in policy-controller using OpenTelemetry, following label-based naming patterns (short metric names, dimensions as attributes). ## Summary -The policy-controller currently uses `knative.dev/pkg v0.0.0-20230612155445-74c4be5e935e` (June 2023), which uses OpenCensus internally. However, **the latest knative/pkg (main branch, 2025+) has fully migrated to OpenTelemetry** (confirmed via GitHub issue knative/pkg#2174 and verified in source). The latest knative/pkg go.mod shows `go.opentelemetry.io/otel v1.39.0` as a direct dependency, with zero OpenCensus imports. Their metrics package (`observability/metrics/`) uses `go.opentelemetry.io/otel/metric` for instruments (Int64Counter, Float64Histogram, etc.) and `go.opentelemetry.io/otel/exporters/prometheus` for Prometheus export. +With the knative/pkg bump to `v0.0.0-20260213150858` (Feb 2026), the metrics infrastructure is now fully OpenTelemetry-based. knative's `sharedmain.MainWithContext()` sets up an OTEL MeterProvider and, when configured with `metrics-protocol: prometheus`, serves a pull-based `/metrics` endpoint on port 9090 via the OTEL Prometheus exporter. This is the standard operator pattern. -**Recommendation: Use OpenTelemetry (`go.opentelemetry.io/otel/metric`)** to align with where knative/pkg is headed. This ensures forward compatibility when policy-controller eventually bumps its knative.dev/pkg dependency. The OTel Prometheus exporter will serve metrics on the same `/metrics` endpoint via `promhttp.Handler()`. +**Recommendation: Use `go.opentelemetry.io/otel/metric`** with the global MeterProvider that knative sets up. Use short, descriptive metric names with attributes for dimensions (e.g., `cache_operations{result="hit"}` not `policy_controller_cache_operations_total{result="hit"}`). The OTEL Prometheus exporter handles `_total` suffixes and unit suffixes automatically. The codebase currently has **zero custom metrics**. The cache has clear instrumentation points in `LRUCache.Get()` and `LRUCache.Set()`. ## Detailed Findings -### 1. Existing Observability Infrastructure +### 1. Knative Metrics Infrastructure (Post-Bump) -#### Knative sharedmain provides automatic metrics -- `cmd/webhook/main.go:157` calls `sharedmain.MainWithContext()` which starts a metrics HTTP server on port 9090 -- `config/config-observability.yaml` has the template for `metrics.backend-destination: prometheus` -- `config/webhook.yaml:70-71` sets `METRICS_DOMAIN=sigstore.dev/policy` -- The `/metrics` endpoint is already live and serves controller queue/reconciliation metrics +#### How sharedmain sets up metrics -#### Dependencies already available (go.mod indirect) -- `github.com/prometheus/client_golang v1.21.1` (line 223) -- `github.com/prometheus/client_model v0.6.1` (line 224) -- `contrib.go.opencensus.io/exporter/prometheus v0.4.2` (line 87) -- `go.opencensus.io v0.24.0` (line 253) +`sharedmain.MainWithContext()` calls `SetupObservabilityOrDie()` which: -#### No custom metrics exist anywhere in the codebase -- Zero imports of `prometheus`, `opencensus`, or `opentelemetry` in any `.go` file -- No `stats.Record`, `view.Register`, `prometheus.NewCounter`, etc. calls +1. Reads `config-observability` ConfigMap from the system namespace +2. Creates an OTEL `MeterProvider` based on the configured protocol +3. Registers it globally via `otel.SetMeterProvider()` +4. Sets up workqueue metrics, client-go metrics, and Go runtime metrics -### 2. Library Recommendation: OpenTelemetry (`go.opentelemetry.io/otel/metric`) +**Supported protocols** (from `observability/metrics/config.go`): -#### knative/pkg has migrated to OpenTelemetry +| Protocol | Type | Default Endpoint | ConfigMap value | +|----------|------|------------------|-----------------| +| `prometheus` | Pull-based | `0.0.0.0:9090` | `metrics-protocol: "prometheus"` | +| `grpc` | Push-based OTLP | (required) | `metrics-protocol: "grpc"` | +| `http/protobuf` | Push-based OTLP | (required) | `metrics-protocol: "http/protobuf"` | +| `none` | Disabled | N/A | `metrics-protocol: "none"` (default) | -Verified by examining the latest knative/pkg source on GitHub (main branch): +**Important**: The default is `none` (disabled). The existing `config-observability.yaml` references old OpenCensus-era keys (`metrics.backend-destination`) that the new knative/pkg no longer reads. It needs to be updated to use `metrics-protocol: prometheus` for metrics to work. -- **go.mod**: Direct dependencies on `go.opentelemetry.io/otel v1.39.0`, `go.opentelemetry.io/otel/metric v1.39.0`, `go.opentelemetry.io/otel/exporters/prometheus v0.61.0`. **Zero OpenCensus dependencies.** -- **observability/metrics/provider.go**: Uses `sdkmetric.NewMeterProvider()` from OTel SDK -- **observability/metrics/prometheus_enabled.go**: Uses `otelprom.New()` from `go.opentelemetry.io/otel/exporters/prometheus` to create a Prometheus exporter, served via `promhttp.Handler()` on port 9090 -- **observability/metrics/k8s/instruments.go**: Wraps OTel `metric.Int64Counter`, `metric.Float64Histogram`, `metric.Int64UpDownCounter`, `metric.Float64Gauge` for workqueue metrics -- **GitHub issue knative/pkg#2174**: Closed by maintainer @dprotaso with comment "We've migrated to OpenTelemetry so this isn't an issue anymore." +#### Prometheus mode details -#### Why OTel over prometheus/client_golang +When `metrics-protocol: prometheus` is set: +- `otelprom.New()` creates an OTEL Prometheus exporter with `UnderscoreEscapingWithSuffixes` translation +- A dedicated HTTP server starts on port 9090 serving `promhttp.Handler()` at `/metrics` +- Port can be overridden via `METRICS_PROMETHEUS_PORT` env var +- All metrics registered with the global OTEL MeterProvider appear on the endpoint automatically -1. **Forward compatibility** - policy-controller currently pins `knative.dev/pkg v0.0.0-20230612155445-74c4be5e935e` (old, OpenCensus-era). When this is bumped, the metrics infrastructure will be OTel-native. Using OTel now avoids a migration later. -2. **Knative ecosystem alignment** - New knative/pkg metrics API is `go.opentelemetry.io/otel/metric`. Using the same API means custom metrics integrate cleanly with the framework's MeterProvider. -3. **OTel Prometheus exporter serves to same `/metrics` endpoint** - knative's new `prometheus_enabled.go` uses `promhttp.Handler()` which serves from `prometheus.DefaultRegistry`. The OTel Prometheus exporter automatically registers there. -4. **OTel is the CNCF standard** - not deprecated like OpenCensus, and has broader backend support than raw prometheus/client_golang. +This is pull-based scraping - the standard pattern for Kubernetes operators. No push infrastructure needed. -#### API pattern (from knative/pkg source) +#### Key source files (in module cache) + +- `observability/metrics/config.go` - Config struct, protocol constants, `DefaultConfig()` +- `observability/metrics/provider.go` - `NewMeterProvider()`, protocol routing via `readerFor()` +- `observability/metrics/prometheus_enabled.go` - `buildPrometheus()`, OTEL exporter setup +- `observability/metrics/prometheus/server.go` - HTTP server for `/metrics` endpoint +- `injection/sharedmain/main.go:286` - `SetupObservabilityOrDie()` bootstrap + +### 2. Library: OpenTelemetry (`go.opentelemetry.io/otel/metric`) + +Since knative/pkg now sets up a global OTEL MeterProvider, custom metrics just need to: +1. Get a meter via `otel.Meter("scope-name")` +2. Create instruments (counters, gauges, histograms) +3. Record values with attributes + +The MeterProvider handles export to whatever backend is configured (Prometheus, OTLP, etc.). + +#### API pattern ```go import ( "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" ) -// Get a meter from the global provider +// Get a meter - scope name provides component context var meter = otel.Meter("policy-controller") -// Create instruments +// Create instruments - short names, no prefix, no _total suffix cacheOperations, _ := meter.Int64Counter( - "policy_controller_cache_operations", - metric.WithDescription("Total number of cache operations"), + "cache.operations", + metric.WithDescription("Number of cache lookup operations"), metric.WithUnit("{operation}"), ) -// Record with attributes (OTel equivalent of Prometheus labels) +// Record with attributes for dimensions cacheOperations.Add(ctx, 1, metric.WithAttributes( attribute.String("result", "hit"), )) ``` -#### Note on current knative.dev/pkg version +### 3. Metric Naming Conventions (OTEL Style) -The policy-controller currently uses an old knative/pkg (June 2023) which still has OpenCensus. This means the existing `sharedmain.MainWithContext()` wires up OpenCensus exporters. For the OTel metrics to appear on `/metrics`, we need to either: -- **Option A**: Set up our own OTel MeterProvider with a Prometheus exporter (standalone, ~10 lines of setup code) -- **Option B**: Use `prometheus/client_golang` with `promauto` directly, which registers to `prometheus.DefaultRegistry` and appears via the existing OpenCensus Prometheus bridge +OTEL naming differs from old Prometheus conventions: -**Practical recommendation**: Since bumping knative.dev/pkg is a separate effort and the current version uses OpenCensus, **use `prometheus/client_golang` with `promauto` for this PR** - it's the simplest path that works today. The metrics will appear on `/metrics` immediately. When knative/pkg is bumped, migrating to OTel instruments is straightforward (similar API, just different import). Add a TODO comment noting the planned migration. +- **No component prefix** - The meter scope name (`"policy-controller"`) provides component context, not the metric name +- **No `_total` suffix** - The OTEL Prometheus exporter adds `_total` for counters automatically +- **No `_seconds`/`_bytes` suffix for units** - Use `metric.WithUnit("s")` or `metric.WithUnit("By")` and the exporter handles suffixes +- **Use dots for namespacing** if needed (e.g., `cache.operations`) +- **Short, descriptive names** - Describe what is measured +- **Dimensions as attributes** - Use `attribute.String("key", "value")` for labels -### 3. Cache Metrics Suggestions (Label-Based Style) +Example of how OTEL names map to Prometheus output: -Following the label-based pattern requested (e.g., single metric with labels rather than separate `_hit`/`_miss` metrics): +| OTEL instrument | OTEL name | Unit | Prometheus output | +|-----------------|-----------|------|-------------------| +| Int64Counter | `cache.operations` | `{operation}` | `cache_operations_total{result="hit"}` | +| Int64Counter | `cache.evictions` | `{eviction}` | `cache_evictions_total` | +| Int64UpDownCounter | `cache.entries` | `{entry}` | `cache_entries{otel_scope_name="policy-controller"}` | +| Float64Histogram | `validation.duration` | `s` | `validation_duration_seconds{...}` | -#### Primary Cache Metrics (This PR) +### 4. Cache Metrics (This PR) -**a) `policy_controller_cache_operations_total`** - Counter with labels +#### a) `cache.operations` - Counter with `result` attribute ```go -var cacheOperations = promauto.NewCounterVec( - prometheus.CounterOpts{ - Name: "policy_controller_cache_operations_total", - Help: "Total number of cache operations by result type.", - }, - []string{"result"}, // "hit", "miss" +cacheOps, _ := meter.Int64Counter( + "cache.operations", + metric.WithDescription("Number of cache lookup operations"), + metric.WithUnit("{operation}"), ) ``` -- Increment with `result="hit"` on cache hit in `LRUCache.Get()` -- Increment with `result="miss"` on cache miss in `LRUCache.Get()` -- Enables: hit rate = `rate(cache_operations_total{result="hit"}) / rate(cache_operations_total)` +- `result="hit"` on cache hit in `LRUCache.Get()` +- `result="miss"` on cache miss in `LRUCache.Get()` +- Hit rate: `rate(cache_operations_total{result="hit"}) / rate(cache_operations_total)` -**b) `policy_controller_cache_writes_total`** - Counter with labels +#### b) `cache.writes` - Counter with `result` attribute ```go -var cacheWrites = promauto.NewCounterVec( - prometheus.CounterOpts{ - Name: "policy_controller_cache_writes_total", - Help: "Total number of cache write operations by result type.", - }, - []string{"result"}, // "stored", "skipped" +cacheWrites, _ := meter.Int64Counter( + "cache.writes", + metric.WithDescription("Number of cache write operations"), + metric.WithUnit("{operation}"), ) ``` - `result="stored"` when a successful validation is cached in `LRUCache.Set()` - `result="skipped"` when `PolicyResult` is nil (failed validation, not cached) -**c) `policy_controller_cache_entries`** - Gauge -```go -var cacheEntries = prometheus.NewGauge( - prometheus.GaugeOpts{ - Name: "policy_controller_cache_entries", - Help: "Current number of entries in the validation result cache.", - }, -) -``` -- Updated on Set/eviction. The underlying `expirable.LRU` has a `Len()` method. -- Alternatively, sample on each Get/Set rather than on eviction callback. - -**d) `policy_controller_cache_evictions_total`** - Counter -```go -var cacheEvictions = promauto.NewCounter( - prometheus.CounterOpts{ - Name: "policy_controller_cache_evictions_total", - Help: "Total number of cache entries evicted (LRU or TTL).", - }, -) -``` -- The `expirable.NewLRU` constructor accepts an `onEvict` callback (currently `nil` on line 37 of `lrucache.go`). Wire this up to increment the counter. - -### 4. Future Metrics Suggestions (Not This PR) - -These are areas where metrics would add observability value to the broader policy-controller: - -#### Validation Metrics - -**a) `policy_controller_validation_duration_seconds`** - Histogram with labels +#### c) `cache.entries` - UpDownCounter (gauge-like) ```go -var validationDuration = promauto.NewHistogramVec( - prometheus.HistogramOpts{ - Name: "policy_controller_validation_duration_seconds", - Help: "Duration of image validation in seconds.", - Buckets: prometheus.DefBuckets, - }, - []string{"result", "cached"}, - // result: "allow", "deny", "warn", "error" - // cached: "true", "false" +cacheEntries, _ := meter.Int64UpDownCounter( + "cache.entries", + metric.WithDescription("Current number of entries in the validation result cache"), + metric.WithUnit("{entry}"), ) ``` -- Instrument in `ValidatePolicy()` (validator.go:474) or `validateContainerImage()` (validator.go:1156) -- Shows how much time cache saves vs full validation +- Increment on Set (stored), decrement on eviction +- The `expirable.LRU` `onEvict` callback handles the decrement -**b) `policy_controller_policy_evaluations_total`** - Counter with labels +#### d) `cache.evictions` - Counter ```go -var policyEvaluations = promauto.NewCounterVec( - prometheus.CounterOpts{ - Name: "policy_controller_policy_evaluations_total", - Help: "Total number of policy evaluations.", - }, - []string{"result", "mode"}, - // result: "pass", "fail" - // mode: "enforce", "warn" +cacheEvictions, _ := meter.Int64Counter( + "cache.evictions", + metric.WithDescription("Number of cache entries evicted"), + metric.WithUnit("{eviction}"), ) ``` -- Instrument in `validatePolicies()` (validator.go:393) +- Wire up via the `onEvict` callback in `expirable.NewLRU` (currently `nil` at lrucache.go:37) -**c) `policy_controller_images_validated_total`** - Counter with labels -```go -var imagesValidated = promauto.NewCounterVec( - prometheus.CounterOpts{ - Name: "policy_controller_images_validated_total", - Help: "Total number of container images validated.", - }, - []string{"result"}, - // result: "allow", "deny", "warn", "no_match" -) -``` -- Instrument in `validateContainerImage()` (validator.go:1156) +### 5. Future Metrics (Not This PR) -#### Webhook Admission Metrics +#### Validation metrics +- `validation.duration` (Histogram, unit `s`) with attributes `result`, `cached` +- `policy.evaluations` (Counter) with attributes `result`, `mode` +- `image.validations` (Counter) with attributes `result` -**d) `policy_controller_admission_requests_total`** - Counter with labels -```go -// result: "allow", "deny", "warn" -// resource_kind: "Pod", "Deployment", etc. -``` -- Instrument at the ValidatePodSpecable/ValidatePod/etc. level +#### Webhook admission metrics +- `admission.requests` (Counter) with attributes `result`, `resource_kind` +- `admission.duration` (Histogram, unit `s`) -**e) `policy_controller_admission_duration_seconds`** - Histogram -- End-to-end webhook response time per admission request +#### Signature verification metrics +- `signature.verifications` (Counter) with attributes `type`, `method`, `result` -#### Signature/Attestation Verification Metrics +### 6. Implementation Location -**f) `policy_controller_signature_verifications_total`** - Counter with labels -```go -// type: "signature", "attestation" -// method: "key", "keyless", "static", "rfc3161" -// result: "success", "failure" -``` -- Instrument in `ValidatePolicySignaturesForAuthority()` and `ValidatePolicyAttestationsForAuthority()` - -### 5. Implementation Location - -The natural place for the metrics definitions is a new file: +Metrics definitions in a new file: ``` pkg/webhook/metrics.go ``` -The instrumentation points are: +Instrumentation points: - `pkg/webhook/lrucache.go:45-53` (Get - hit/miss) - `pkg/webhook/lrucache.go:55-64` (Set - stored/skipped) - `pkg/webhook/lrucache.go:37` (onEvict callback in NewLRUCache) For the `NoCache` implementation (`pkg/webhook/nocache.go`), no metrics should be emitted since the cache is disabled. -### 6. Metric Naming Conventions +### 7. ConfigMap Update Required + +The existing `config/config-observability.yaml` uses old OpenCensus-era keys that the new knative/pkg ignores. It needs to be updated: + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: config-observability + namespace: cosign-system +data: + metrics-protocol: "prometheus" +``` -Following Prometheus naming best practices and the label-based style: -- Prefix: `policy_controller_` (matches the component name from `sharedmain.MainWithContext(ctx, "policy-controller", ...)`) -- Units in suffix: `_seconds`, `_bytes`, `_total` for counters -- Use labels for dimensions rather than separate metric names -- Example: `policy_controller_cache_operations_total{result="hit"}` not `policy_controller_cache_hits_total` +Without this, `DefaultConfig()` returns `Protocol: "none"` and the MeterProvider is a no-op (no metrics collected or served). ## Code References -- `cmd/webhook/main.go:157` - sharedmain.MainWithContext bootstrap (metrics server) +- `cmd/webhook/main.go:157` - sharedmain.MainWithContext bootstrap - `cmd/webhook/main.go:107-111` - Cache CLI flags (enable-cache, cache-size, cache-ttl) - `cmd/webhook/main.go:247-251` - Cache initialization - `pkg/webhook/cache.go:47-56` - ResultCache interface - `pkg/webhook/lrucache.go:35-38` - NewLRUCache constructor (onEvict callback is nil) - `pkg/webhook/lrucache.go:45-53` - Get() method (hit/miss instrumentation point) - `pkg/webhook/lrucache.go:55-64` - Set() method (store/skip instrumentation point) -- `pkg/webhook/nocache.go:23-31` - NoCache implementation (no metrics should be emitted) +- `pkg/webhook/nocache.go:23-31` - NoCache implementation - `pkg/webhook/validator.go:474-479` - Cache lookup in ValidatePolicy - `pkg/webhook/validator.go:637-640` - Cache write in ValidatePolicy -- `config/config-observability.yaml` - Observability config template -- `go.mod:223` - prometheus/client_golang v1.21.1 (indirect) +- `config/config-observability.yaml` - Observability config (needs update) +- `config/webhook.yaml:70-71` - METRICS_DOMAIN env var ## Architecture Documentation @@ -277,13 +243,13 @@ Following Prometheus naming best practices and the label-based style: 5. Failed validations (PolicyResult nil) are NOT cached, allowing retries ### Metrics Endpoint -- knative's `sharedmain` starts metrics server on port 9090 -- Current knative.dev/pkg version (June 2023): OpenCensus Prometheus exporter bridges to `prometheus.DefaultRegistry` -- Latest knative.dev/pkg (2025+): OTel Prometheus exporter via `go.opentelemetry.io/otel/exporters/prometheus`, served via `promhttp.Handler()` on same port -- In both cases, metrics registered with `promauto` on `prometheus.DefaultRegistry` appear on `/metrics` +- knative's `sharedmain` reads `config-observability` ConfigMap +- When `metrics-protocol: prometheus`, starts OTEL Prometheus exporter on `:9090/metrics` +- All instruments created via `otel.Meter()` are automatically exported +- Pull-based: standard Prometheus scraping, no push infrastructure needed ## Open Questions -1. **Should `NoCache` emit a "disabled" metric?** - Could add a gauge `policy_controller_cache_enabled{} 0/1` to indicate cache state -2. **Cardinality concerns** - Should any labels include image name or policy name? Probably not for cache metrics (high cardinality), but worth considering for future validation metrics +1. **Should `NoCache` emit a "disabled" metric?** - Could add a gauge `cache.enabled` (0/1) to indicate cache state +2. **Cardinality concerns** - Should any attributes include image name or policy name? Probably not for cache metrics (high cardinality), but worth considering for future validation metrics 3. **Histogram buckets for validation duration** - What are typical validation times? This affects bucket configuration for future duration histograms From 6349c2e48003e951470d86415e635c8411ff381d Mon Sep 17 00:00:00 2001 From: Edvin Norling Date: Mon, 16 Feb 2026 23:20:08 +0100 Subject: [PATCH 09/13] plan --- thoughts/plans/2026-02-16-cache-metrics.md | 343 +++++++++++++++++++++ 1 file changed, 343 insertions(+) create mode 100644 thoughts/plans/2026-02-16-cache-metrics.md diff --git a/thoughts/plans/2026-02-16-cache-metrics.md b/thoughts/plans/2026-02-16-cache-metrics.md new file mode 100644 index 000000000..2d7dcfe28 --- /dev/null +++ b/thoughts/plans/2026-02-16-cache-metrics.md @@ -0,0 +1,343 @@ +# Cache Metrics Implementation Plan + +## Overview + +Add OpenTelemetry metrics to the LRU cache in `pkg/webhook/` to track cache operations, writes, entries, and evictions. Uses the global OTEL MeterProvider set up by knative's `sharedmain.MainWithContext()`. + +## Current State Analysis + +- **Zero custom metrics** in the codebase +- LRU cache in `pkg/webhook/lrucache.go` has clear instrumentation points (Get hit/miss, Set stored/skipped) +- The `onEvict` callback in `NewLRUCache` is currently `nil` (lrucache.go:37) +- OTEL v1.39.0 dependencies available transitively via `knative.dev/pkg` +- `NoCache` implementation (nocache.go) is a no-op and should not emit metrics +- Existing tests in `lrucache_test.go` use stdlib `testing` (no testify, no external assertion libs) + +### Key Discoveries: +- `Set` method uses `_ context.Context` (lrucache.go:55) - needs to be changed to `ctx` for metric recording +- `expirable.LRU`'s onEvict callback fires for both LRU eviction and TTL expiration - so `cache.entries` tracking will be accurate +- The onEvict callback signature is `func(key string, value *CacheResult)` - no context, so eviction metrics use `context.Background()` +- OTEL's global meter delegation means package-level instrument vars work with later MeterProvider setup (both knative's sharedmain and test providers) + +## Desired End State + +Four cache metrics emitted by `LRUCache` (not `NoCache`): + +| OTEL Instrument | Name | Type | Attributes | Prometheus Output | +|-----------------|------|------|------------|-------------------| +| Int64Counter | `cache.operations` | Counter | `result=hit\|miss` | `cache_operations_total{result="hit"}` | +| Int64Counter | `cache.writes` | Counter | `result=stored\|skipped` | `cache_writes_total{result="stored"}` | +| Int64UpDownCounter | `cache.entries` | UpDownCounter | (none) | `cache_entries` | +| Int64Counter | `cache.evictions` | Counter | (none) | `cache_evictions_total` | + +### Verification: +- `go test ./pkg/webhook/ -run TestCacheMetrics -v` passes with metric value assertions +- `go test ./pkg/webhook/` passes (existing tests still pass) +- `make test` passes +- `golangci-lint run ./pkg/webhook/...` passes +- `go mod tidy` produces no diff + +## What We're NOT Doing + +- Updating `config/config-observability.yaml` (separate PR) +- Adding validation/admission/signature metrics (future work) +- Adding a `cache.enabled` gauge +- Metrics for `NoCache` implementation +- Histogram metrics (no duration tracking in cache operations) + +## Implementation Approach + +Single phase - the change is small and self-contained. Create a `metrics.go` file with package-level OTEL instruments, wire them into `LRUCache.Get()`, `LRUCache.Set()`, and the onEvict callback, then add tests. + +## Phase 1: Cache Metrics + +### Overview +Define 4 OTEL instruments, add recording calls to LRUCache, add tests with OTEL SDK test utilities. + +### Changes Required: + +#### 1. New file: `pkg/webhook/metrics.go` +**File**: `pkg/webhook/metrics.go` +**Purpose**: Define OTEL meter and cache instruments as package-level vars + +```go +// +// Copyright 2026 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package webhook + +import ( + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/metric" +) + +var meter = otel.Meter("policy-controller") + +var ( + cacheOps metric.Int64Counter + cacheWrites metric.Int64Counter + cacheEntries metric.Int64UpDownCounter + cacheEvictions metric.Int64Counter +) + +func init() { + registerCacheMetrics(meter) +} + +// registerCacheMetrics initializes cache metric instruments from the given meter. +// Called from init() for production use. Tests call this with a test meter +// to capture metric values. +func registerCacheMetrics(m metric.Meter) { + cacheOps, _ = m.Int64Counter( + "cache.operations", + metric.WithDescription("Number of cache lookup operations"), + metric.WithUnit("{operation}"), + ) + cacheWrites, _ = m.Int64Counter( + "cache.writes", + metric.WithDescription("Number of cache write operations"), + metric.WithUnit("{operation}"), + ) + cacheEntries, _ = m.Int64UpDownCounter( + "cache.entries", + metric.WithDescription("Current number of entries in the validation result cache"), + metric.WithUnit("{entry}"), + ) + cacheEvictions, _ = m.Int64Counter( + "cache.evictions", + metric.WithDescription("Number of cache entries evicted"), + metric.WithUnit("{eviction}"), + ) +} +``` + +**Design decisions**: +- `registerCacheMetrics(m metric.Meter)` is separated from `init()` so tests can reinitialize instruments with a test meter. This avoids global MeterProvider issues in tests. +- Errors from instrument creation are intentionally ignored (the `_` pattern). These only fail for invalid metric names, which is a developer error caught during development. +- The meter scope name `"policy-controller"` appears as `otel_scope_name` label in Prometheus output. +- No `_total` suffix or component prefix in names - the OTEL Prometheus exporter adds `_total` for counters automatically. + +#### 2. Modify: `pkg/webhook/lrucache.go` +**File**: `pkg/webhook/lrucache.go` +**Changes**: Add metric recording to Get, Set, and onEvict callback + +The full file after changes: + +```go +package webhook + +import ( + "context" + "fmt" + "time" + + expirable "github.com/hashicorp/golang-lru/v2/expirable" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric" + "knative.dev/pkg/logging" +) + +type LRUCache struct { + cache *expirable.LRU[string, *CacheResult] +} + +func NewLRUCache(size int, ttl time.Duration) *LRUCache { + return &LRUCache{ + cache: expirable.NewLRU[string, *CacheResult](size, func(_ string, _ *CacheResult) { + cacheEvictions.Add(context.Background(), 1) + cacheEntries.Add(context.Background(), -1) + }, ttl), + } +} + +func cacheKeyFor(image, uid, resourceVersion string) string { + return fmt.Sprintf("%s/%s/%s", image, uid, resourceVersion) +} + +func (c *LRUCache) Get(ctx context.Context, image, uid, resourceVersion string) *CacheResult { + result, ok := c.cache.Get(cacheKeyFor(image, uid, resourceVersion)) + if !ok { + cacheOps.Add(ctx, 1, metric.WithAttributes(attribute.String("result", "miss"))) + logging.FromContext(ctx).Debugf("cache miss for image %s, policy UID %s", image, uid) + return nil + } + cacheOps.Add(ctx, 1, metric.WithAttributes(attribute.String("result", "hit"))) + logging.FromContext(ctx).Debugf("cache hit for image %s, policy UID %s", image, uid) + return result +} + +func (c *LRUCache) Set(ctx context.Context, image, name, uid, resourceVersion string, cacheResult *CacheResult) { //nolint: revive + if cacheResult.PolicyResult == nil { + cacheWrites.Add(ctx, 1, metric.WithAttributes(attribute.String("result", "skipped"))) + return + } + copied := &CacheResult{ + PolicyResult: cacheResult.PolicyResult, + Errors: append([]error(nil), cacheResult.Errors...), + } + c.cache.Add(cacheKeyFor(image, uid, resourceVersion), copied) + cacheWrites.Add(ctx, 1, metric.WithAttributes(attribute.String("result", "stored"))) + cacheEntries.Add(ctx, 1) +} +``` + +**Key changes from current code**: +- Added imports: `go.opentelemetry.io/otel/attribute`, `go.opentelemetry.io/otel/metric` +- `NewLRUCache`: Changed `nil` onEvict callback to a function that records eviction and decrements entries +- `Get`: Added `cacheOps.Add()` calls with `result=hit` and `result=miss` attributes +- `Set`: Changed `_ context.Context` to `ctx context.Context`, added `cacheWrites.Add()` calls with `result=stored` and `result=skipped`, added `cacheEntries.Add(ctx, 1)` on store +- Kept existing debug logging unchanged +- Kept `//nolint: revive` for the unused `name` parameter + +#### 3. New file: `pkg/webhook/metrics_test.go` +**File**: `pkg/webhook/metrics_test.go` +**Purpose**: Test that cache operations record correct metric values + +Tests use OTEL SDK's `ManualReader` to capture and assert metric values. The test structure: + +```go +package webhook + +import ( + "context" + "testing" + "time" + + "go.opentelemetry.io/otel/attribute" + sdkmetric "go.opentelemetry.io/otel/sdk/metric" + "go.opentelemetry.io/otel/sdk/metric/metricdata" +) + +// setupTestMetrics creates a test MeterProvider and reinitializes the +// package-level cache instruments. Returns a ManualReader for asserting +// metric values. Restores the original instruments on test cleanup. +func setupTestMetrics(t *testing.T) *sdkmetric.ManualReader { + t.Helper() + reader := sdkmetric.NewManualReader() + provider := sdkmetric.NewMeterProvider(sdkmetric.WithReader(reader)) + m := provider.Meter("policy-controller") + registerCacheMetrics(m) + t.Cleanup(func() { + registerCacheMetrics(meter) // restore package-level instruments + provider.Shutdown(context.Background()) + }) + return reader +} + +// getCounterValue extracts an Int64Counter value by metric name and attributes. +func getCounterValue(t *testing.T, reader *sdkmetric.ManualReader, name string, attrs ...attribute.KeyValue) int64 { + t.Helper() + var rm metricdata.ResourceMetrics + if err := reader.Collect(context.Background(), &rm); err != nil { + t.Fatalf("failed to collect metrics: %v", err) + } + set := attribute.NewSet(attrs...) + for _, sm := range rm.ScopeMetrics { + for _, m := range sm.Metrics { + if m.Name != name { + continue + } + if sum, ok := m.Data.(metricdata.Sum[int64]); ok { + for _, dp := range sum.DataPoints { + if dp.Attributes.Equals(&set) { + return dp.Value + } + } + } + } + } + return 0 +} + +// getUpDownCounterValue extracts an Int64UpDownCounter value by metric name. +func getUpDownCounterValue(t *testing.T, reader *sdkmetric.ManualReader, name string) int64 { + t.Helper() + var rm metricdata.ResourceMetrics + if err := reader.Collect(context.Background(), &rm); err != nil { + t.Fatalf("failed to collect metrics: %v", err) + } + for _, sm := range rm.ScopeMetrics { + for _, m := range sm.Metrics { + if m.Name != name { + continue + } + if sum, ok := m.Data.(metricdata.Sum[int64]); ok { + for _, dp := range sum.DataPoints { + return dp.Value + } + } + } + } + return 0 +} +``` + +**Test cases** (specific functions to implement): + +1. **`TestCacheMetricsHitMiss`** - Get on empty cache records `cache.operations{result=miss}`, Set + Get records `cache.operations{result=hit}`. Assert exact counter values. + +2. **`TestCacheMetricsWrites`** - Set with non-nil PolicyResult records `cache.writes{result=stored}`. Set with nil PolicyResult records `cache.writes{result=skipped}`. + +3. **`TestCacheMetricsEntries`** - After Set (stored), `cache.entries` is 1. After another Set, `cache.entries` is 2. After eviction (size-limited cache), `cache.entries` goes back down. + +4. **`TestCacheMetricsEviction`** - Use a size-2 cache. After 3 Sets, `cache.evictions` is 1 and `cache.entries` is 2. + +#### 4. Update go.mod +**File**: `go.mod` +**Changes**: Run `go mod tidy` to promote OTEL imports from `indirect` to direct dependencies. + +The following will move from `// indirect` to direct: +- `go.opentelemetry.io/otel v1.39.0` +- `go.opentelemetry.io/otel/metric v1.39.0` + +The following will be added to direct dependencies for tests: +- `go.opentelemetry.io/otel/sdk/metric v1.39.0` + +This is handled automatically by `go mod tidy`. + +### Success Criteria: + +#### Automated Verification: +- [ ] Unit tests pass: `go test ./pkg/webhook/ -run TestCacheMetrics -v` +- [ ] All existing tests still pass: `go test ./pkg/webhook/` +- [ ] Full test suite passes: `make test` +- [ ] Linting passes: `golangci-lint run ./pkg/webhook/...` +- [ ] Module is tidy: `go mod tidy` produces no diff + +#### Manual Verification: +- [ ] Deploy with `config-observability` set to `metrics-protocol: prometheus` and confirm `/metrics` on `:9090` includes `cache_operations_total`, `cache_writes_total`, `cache_entries`, `cache_evictions_total` +- [ ] Trigger admission requests with cache enabled and verify counters increment + +## Testing Strategy + +### Unit Tests (in this PR): +- Test hit/miss counter values after Get operations +- Test stored/skipped counter values after Set operations +- Test entries UpDownCounter tracks current cache size accurately +- Test eviction counter fires on LRU eviction +- Use OTEL SDK `ManualReader` + `metricdata` for precise value assertions + +### Integration/Manual Testing (optional, for confidence): +- Deploy with `--enable-cache=true` and `metrics-protocol: prometheus` +- Curl `:9090/metrics` and verify metric lines appear +- Submit admission requests and verify counters change + +## References + +- Research: `thoughts/research/2026-02-16-cache-metrics-implementation.md` +- Cache interface: `pkg/webhook/cache.go:47-56` +- LRU implementation: `pkg/webhook/lrucache.go` +- Existing cache tests: `pkg/webhook/lrucache_test.go` +- NoCache (no changes): `pkg/webhook/nocache.go` From 8fbc75dfb40e8f0c38dcdfb4cea45b65d1bbbbec Mon Sep 17 00:00:00 2001 From: Edvin Norling Date: Mon, 16 Feb 2026 23:37:05 +0100 Subject: [PATCH 10/13] update plan --- thoughts/plans/2026-02-16-cache-metrics.md | 90 +++++++++++++++------- 1 file changed, 62 insertions(+), 28 deletions(-) diff --git a/thoughts/plans/2026-02-16-cache-metrics.md b/thoughts/plans/2026-02-16-cache-metrics.md index 2d7dcfe28..8e4ac01c9 100644 --- a/thoughts/plans/2026-02-16-cache-metrics.md +++ b/thoughts/plans/2026-02-16-cache-metrics.md @@ -15,9 +15,11 @@ Add OpenTelemetry metrics to the LRU cache in `pkg/webhook/` to track cache oper ### Key Discoveries: - `Set` method uses `_ context.Context` (lrucache.go:55) - needs to be changed to `ctx` for metric recording -- `expirable.LRU`'s onEvict callback fires for both LRU eviction and TTL expiration - so `cache.entries` tracking will be accurate +- `expirable.LRU`'s onEvict callback fires for both LRU eviction and TTL expiration +- `expirable.LRU.Add()` updates existing keys in-place WITHOUT firing onEvict - so manual increment/decrement of an entries counter would drift on concurrent Sets for the same key - The onEvict callback signature is `func(key string, value *CacheResult)` - no context, so eviction metrics use `context.Background()` - OTEL's global meter delegation means package-level instrument vars work with later MeterProvider setup (both knative's sharedmain and test providers) +- `cache.Len()` returns the accurate current size - using an Observable Gauge with this avoids race conditions entirely ## Desired End State @@ -27,7 +29,7 @@ Four cache metrics emitted by `LRUCache` (not `NoCache`): |-----------------|------|------|------------|-------------------| | Int64Counter | `cache.operations` | Counter | `result=hit\|miss` | `cache_operations_total{result="hit"}` | | Int64Counter | `cache.writes` | Counter | `result=stored\|skipped` | `cache_writes_total{result="stored"}` | -| Int64UpDownCounter | `cache.entries` | UpDownCounter | (none) | `cache_entries` | +| Int64ObservableGauge | `cache.entries` | Gauge (callback) | (none) | `cache_entries` | | Int64Counter | `cache.evictions` | Counter | (none) | `cache_evictions_total` | ### Verification: @@ -47,12 +49,12 @@ Four cache metrics emitted by `LRUCache` (not `NoCache`): ## Implementation Approach -Single phase - the change is small and self-contained. Create a `metrics.go` file with package-level OTEL instruments, wire them into `LRUCache.Get()`, `LRUCache.Set()`, and the onEvict callback, then add tests. +Single phase - the change is small and self-contained. Create a `metrics.go` file with package-level OTEL instruments, wire them into `LRUCache.Get()`, `LRUCache.Set()`, and the onEvict callback, then add tests. The `cache.entries` metric uses an Observable Gauge that reads `cache.Len()` at collection time, avoiding race conditions from manual increment/decrement bookkeeping. ## Phase 1: Cache Metrics ### Overview -Define 4 OTEL instruments, add recording calls to LRUCache, add tests with OTEL SDK test utilities. +Define 4 OTEL instruments (3 counters + 1 observable gauge), add recording calls to LRUCache, add tests with OTEL SDK test utilities. ### Changes Required: @@ -79,6 +81,8 @@ Define 4 OTEL instruments, add recording calls to LRUCache, add tests with OTEL package webhook import ( + "context" + "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/metric" ) @@ -86,10 +90,9 @@ import ( var meter = otel.Meter("policy-controller") var ( - cacheOps metric.Int64Counter - cacheWrites metric.Int64Counter - cacheEntries metric.Int64UpDownCounter - cacheEvictions metric.Int64Counter + cacheOps metric.Int64Counter + cacheWrites metric.Int64Counter + cacheEvictions metric.Int64Counter ) func init() { @@ -110,21 +113,41 @@ func registerCacheMetrics(m metric.Meter) { metric.WithDescription("Number of cache write operations"), metric.WithUnit("{operation}"), ) - cacheEntries, _ = m.Int64UpDownCounter( - "cache.entries", - metric.WithDescription("Current number of entries in the validation result cache"), - metric.WithUnit("{entry}"), - ) cacheEvictions, _ = m.Int64Counter( "cache.evictions", metric.WithDescription("Number of cache entries evicted"), metric.WithUnit("{eviction}"), ) } + +// cacheEntriesLenFunc is the function called by the Observable Gauge to report +// the current number of cache entries. Nil when no cache is registered. +var cacheEntriesLenFunc func() int + +// registerCacheEntriesGauge registers an Observable Gauge that reads the +// current cache size via lenFunc at collection time. This is race-free +// because it reads the source of truth (cache.Len()) rather than maintaining +// a running counter. +func registerCacheEntriesGauge(m metric.Meter, lenFunc func() int) { + cacheEntriesLenFunc = lenFunc + gauge, _ := m.Int64ObservableGauge( + "cache.entries", + metric.WithDescription("Current number of entries in the validation result cache"), + metric.WithUnit("{entry}"), + ) + m.RegisterCallback(func(_ context.Context, o metric.Observer) error { + if cacheEntriesLenFunc != nil { + o.ObserveInt64(gauge, int64(cacheEntriesLenFunc())) + } + return nil + }, gauge) +} ``` **Design decisions**: - `registerCacheMetrics(m metric.Meter)` is separated from `init()` so tests can reinitialize instruments with a test meter. This avoids global MeterProvider issues in tests. +- `cache.entries` uses an `Int64ObservableGauge` with a callback that reads `cache.Len()` at collection time. This is race-free — no manual increment/decrement bookkeeping that could drift when `expirable.LRU.Add()` updates existing keys in-place without firing onEvict. +- `registerCacheEntriesGauge` is called separately from `registerCacheMetrics` because it needs a reference to the cache instance. Called from `NewLRUCache`. - Errors from instrument creation are intentionally ignored (the `_` pattern). These only fail for invalid metric names, which is a developer error caught during development. - The meter scope name `"policy-controller"` appears as `otel_scope_name` label in Prometheus output. - No `_total` suffix or component prefix in names - the OTEL Prometheus exporter adds `_total` for counters automatically. @@ -154,12 +177,13 @@ type LRUCache struct { } func NewLRUCache(size int, ttl time.Duration) *LRUCache { - return &LRUCache{ + lc := &LRUCache{ cache: expirable.NewLRU[string, *CacheResult](size, func(_ string, _ *CacheResult) { cacheEvictions.Add(context.Background(), 1) - cacheEntries.Add(context.Background(), -1) }, ttl), } + registerCacheEntriesGauge(meter, lc.cache.Len) + return lc } func cacheKeyFor(image, uid, resourceVersion string) string { @@ -189,15 +213,15 @@ func (c *LRUCache) Set(ctx context.Context, image, name, uid, resourceVersion st } c.cache.Add(cacheKeyFor(image, uid, resourceVersion), copied) cacheWrites.Add(ctx, 1, metric.WithAttributes(attribute.String("result", "stored"))) - cacheEntries.Add(ctx, 1) } ``` **Key changes from current code**: - Added imports: `go.opentelemetry.io/otel/attribute`, `go.opentelemetry.io/otel/metric` -- `NewLRUCache`: Changed `nil` onEvict callback to a function that records eviction and decrements entries +- `NewLRUCache`: Changed `nil` onEvict callback to a function that records eviction counter. Calls `registerCacheEntriesGauge` with `cache.Len` method reference for the Observable Gauge. - `Get`: Added `cacheOps.Add()` calls with `result=hit` and `result=miss` attributes -- `Set`: Changed `_ context.Context` to `ctx context.Context`, added `cacheWrites.Add()` calls with `result=stored` and `result=skipped`, added `cacheEntries.Add(ctx, 1)` on store +- `Set`: Changed `_ context.Context` to `ctx context.Context`, added `cacheWrites.Add()` calls with `result=stored` and `result=skipped` +- No manual `cacheEntries` increment/decrement — the Observable Gauge reads `cache.Len()` at collection time, which is always accurate - Kept existing debug logging unchanged - Kept `//nolint: revive` for the unused `name` parameter @@ -221,14 +245,16 @@ import ( ) // setupTestMetrics creates a test MeterProvider and reinitializes the -// package-level cache instruments. Returns a ManualReader for asserting -// metric values. Restores the original instruments on test cleanup. -func setupTestMetrics(t *testing.T) *sdkmetric.ManualReader { +// package-level cache instruments including the Observable Gauge for +// cache.entries. Returns a ManualReader for asserting metric values. +// Restores the original instruments on test cleanup. +func setupTestMetrics(t *testing.T, cache *LRUCache) *sdkmetric.ManualReader { t.Helper() reader := sdkmetric.NewManualReader() provider := sdkmetric.NewMeterProvider(sdkmetric.WithReader(reader)) m := provider.Meter("policy-controller") registerCacheMetrics(m) + registerCacheEntriesGauge(m, cache.cache.Len) t.Cleanup(func() { registerCacheMetrics(meter) // restore package-level instruments provider.Shutdown(context.Background()) @@ -237,6 +263,7 @@ func setupTestMetrics(t *testing.T) *sdkmetric.ManualReader { } // getCounterValue extracts an Int64Counter value by metric name and attributes. +// Fails the test immediately if the metric name is not found (catches typos). func getCounterValue(t *testing.T, reader *sdkmetric.ManualReader, name string, attrs ...attribute.KeyValue) int64 { t.Helper() var rm metricdata.ResourceMetrics @@ -256,13 +283,16 @@ func getCounterValue(t *testing.T, reader *sdkmetric.ManualReader, name string, } } } + t.Fatalf("metric %q found but no data point matching attributes %v", name, attrs) } } + t.Fatalf("metric %q not found in collected metrics", name) return 0 } -// getUpDownCounterValue extracts an Int64UpDownCounter value by metric name. -func getUpDownCounterValue(t *testing.T, reader *sdkmetric.ManualReader, name string) int64 { +// getGaugeValue extracts an Int64ObservableGauge value by metric name. +// Fails the test immediately if the metric name is not found (catches typos). +func getGaugeValue(t *testing.T, reader *sdkmetric.ManualReader, name string) int64 { t.Helper() var rm metricdata.ResourceMetrics if err := reader.Collect(context.Background(), &rm); err != nil { @@ -273,13 +303,15 @@ func getUpDownCounterValue(t *testing.T, reader *sdkmetric.ManualReader, name st if m.Name != name { continue } - if sum, ok := m.Data.(metricdata.Sum[int64]); ok { - for _, dp := range sum.DataPoints { + if gauge, ok := m.Data.(metricdata.Gauge[int64]); ok { + for _, dp := range gauge.DataPoints { return dp.Value } } + t.Fatalf("metric %q found but no gauge data points", name) } } + t.Fatalf("metric %q not found in collected metrics", name) return 0 } ``` @@ -290,9 +322,9 @@ func getUpDownCounterValue(t *testing.T, reader *sdkmetric.ManualReader, name st 2. **`TestCacheMetricsWrites`** - Set with non-nil PolicyResult records `cache.writes{result=stored}`. Set with nil PolicyResult records `cache.writes{result=skipped}`. -3. **`TestCacheMetricsEntries`** - After Set (stored), `cache.entries` is 1. After another Set, `cache.entries` is 2. After eviction (size-limited cache), `cache.entries` goes back down. +3. **`TestCacheMetricsEntries`** - After Set (stored), `cache.entries` gauge reads 1. After another Set (different key), `cache.entries` reads 2. After eviction (size-limited cache), `cache.entries` reads the actual cache size. This is race-free since the Observable Gauge reads `cache.Len()` directly. -4. **`TestCacheMetricsEviction`** - Use a size-2 cache. After 3 Sets, `cache.evictions` is 1 and `cache.entries` is 2. +4. **`TestCacheMetricsEviction`** - Use a size-2 cache. After 3 Sets, `cache.evictions` is 1 and `cache.entries` gauge is 2. #### 4. Update go.mod **File**: `go.mod` @@ -303,6 +335,7 @@ The following will move from `// indirect` to direct: - `go.opentelemetry.io/otel/metric v1.39.0` The following will be added to direct dependencies for tests: +- `go.opentelemetry.io/otel/sdk v1.39.0` - `go.opentelemetry.io/otel/sdk/metric v1.39.0` This is handled automatically by `go mod tidy`. @@ -325,9 +358,10 @@ This is handled automatically by `go mod tidy`. ### Unit Tests (in this PR): - Test hit/miss counter values after Get operations - Test stored/skipped counter values after Set operations -- Test entries UpDownCounter tracks current cache size accurately +- Test entries Observable Gauge reports current cache size accurately (race-free via `cache.Len()`) - Test eviction counter fires on LRU eviction - Use OTEL SDK `ManualReader` + `metricdata` for precise value assertions +- Test helpers fail-fast with `t.Fatalf` when metric names are not found (catches typos) ### Integration/Manual Testing (optional, for confidence): - Deploy with `--enable-cache=true` and `metrics-protocol: prometheus` From 6a3a62974b969e1659936959f3eb86b4abfb7bd4 Mon Sep 17 00:00:00 2001 From: Edvin Norling Date: Mon, 16 Feb 2026 23:43:23 +0100 Subject: [PATCH 11/13] feat: add OpenTelemetry cache metrics for LRU validation cache Add four OTEL metrics to track cache behavior: cache.operations (hit/miss counter), cache.writes (stored/skipped counter), cache.entries (observable gauge via cache.Len()), and cache.evictions (counter). Uses the global OTEL MeterProvider set up by knative's sharedmain, with race-free entries tracking via callback gauge. Co-Authored-By: Claude Opus 4.6 --- go.mod | 6 +- pkg/webhook/lrucache.go | 16 +- pkg/webhook/metrics.go | 79 +++++++++ pkg/webhook/metrics_test.go | 186 +++++++++++++++++++++ thoughts/plans/2026-02-16-cache-metrics.md | 10 +- 5 files changed, 286 insertions(+), 11 deletions(-) create mode 100644 pkg/webhook/metrics.go create mode 100644 pkg/webhook/metrics_test.go diff --git a/go.mod b/go.mod index 5b2fd7658..67f43a928 100644 --- a/go.mod +++ b/go.mod @@ -70,6 +70,9 @@ require ( github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.9.4 github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.9.4 github.com/spf13/viper v1.20.1 + go.opentelemetry.io/otel v1.39.0 + go.opentelemetry.io/otel/metric v1.39.0 + go.opentelemetry.io/otel/sdk/metric v1.39.0 knative.dev/hack/schema v0.0.0-20240607132042-09143140a254 knative.dev/pkg v0.0.0-20260213150858-6758a9ff4767 ) @@ -247,7 +250,6 @@ require ( go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0 // indirect go.opentelemetry.io/contrib/instrumentation/runtime v0.64.0 // indirect - go.opentelemetry.io/otel v1.39.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.39.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.39.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0 // indirect @@ -255,9 +257,7 @@ require ( go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.39.0 // indirect go.opentelemetry.io/otel/exporters/prometheus v0.61.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.39.0 // indirect - go.opentelemetry.io/otel/metric v1.39.0 // indirect go.opentelemetry.io/otel/sdk v1.39.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.39.0 // indirect go.opentelemetry.io/otel/trace v1.39.0 // indirect go.opentelemetry.io/proto/otlp v1.9.0 // indirect go.uber.org/automaxprocs v1.6.0 // indirect diff --git a/pkg/webhook/lrucache.go b/pkg/webhook/lrucache.go index fedb969a7..8cac0eda5 100644 --- a/pkg/webhook/lrucache.go +++ b/pkg/webhook/lrucache.go @@ -21,6 +21,8 @@ import ( "time" expirable "github.com/hashicorp/golang-lru/v2/expirable" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric" "knative.dev/pkg/logging" ) @@ -33,9 +35,13 @@ type LRUCache struct { // NewLRUCache creates a new LRU cache with the given size and TTL. func NewLRUCache(size int, ttl time.Duration) *LRUCache { - return &LRUCache{ - cache: expirable.NewLRU[string, *CacheResult](size, nil, ttl), + lc := &LRUCache{ + cache: expirable.NewLRU[string, *CacheResult](size, func(_ string, _ *CacheResult) { + cacheEvictions.Add(context.Background(), 1) + }, ttl), } + registerCacheEntriesGauge(meter, lc.cache.Len) + return lc } func cacheKeyFor(image, uid, resourceVersion string) string { @@ -45,15 +51,18 @@ func cacheKeyFor(image, uid, resourceVersion string) string { func (c *LRUCache) Get(ctx context.Context, image, uid, resourceVersion string) *CacheResult { result, ok := c.cache.Get(cacheKeyFor(image, uid, resourceVersion)) if !ok { + cacheOps.Add(ctx, 1, metric.WithAttributes(attribute.String("result", "miss"))) logging.FromContext(ctx).Debugf("cache miss for image %s, policy UID %s", image, uid) return nil } + cacheOps.Add(ctx, 1, metric.WithAttributes(attribute.String("result", "hit"))) logging.FromContext(ctx).Debugf("cache hit for image %s, policy UID %s", image, uid) return result } -func (c *LRUCache) Set(_ context.Context, image, name, uid, resourceVersion string, cacheResult *CacheResult) { //nolint: revive +func (c *LRUCache) Set(ctx context.Context, image, name, uid, resourceVersion string, cacheResult *CacheResult) { //nolint: revive if cacheResult.PolicyResult == nil { + cacheWrites.Add(ctx, 1, metric.WithAttributes(attribute.String("result", "skipped"))) return } copied := &CacheResult{ @@ -61,4 +70,5 @@ func (c *LRUCache) Set(_ context.Context, image, name, uid, resourceVersion stri Errors: append([]error(nil), cacheResult.Errors...), } c.cache.Add(cacheKeyFor(image, uid, resourceVersion), copied) + cacheWrites.Add(ctx, 1, metric.WithAttributes(attribute.String("result", "stored"))) } diff --git a/pkg/webhook/metrics.go b/pkg/webhook/metrics.go new file mode 100644 index 000000000..13a9494e3 --- /dev/null +++ b/pkg/webhook/metrics.go @@ -0,0 +1,79 @@ +// +// Copyright 2026 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package webhook + +import ( + "context" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/metric" +) + +var meter = otel.Meter("policy-controller") + +var ( + cacheOps metric.Int64Counter + cacheWrites metric.Int64Counter + cacheEvictions metric.Int64Counter +) + +func init() { + registerCacheMetrics(meter) +} + +// registerCacheMetrics initializes cache metric instruments from the given meter. +// Called from init() for production use. Tests call this with a test meter +// to capture metric values. +func registerCacheMetrics(m metric.Meter) { + cacheOps, _ = m.Int64Counter( + "cache.operations", + metric.WithDescription("Number of cache lookup operations"), + metric.WithUnit("{operation}"), + ) + cacheWrites, _ = m.Int64Counter( + "cache.writes", + metric.WithDescription("Number of cache write operations"), + metric.WithUnit("{operation}"), + ) + cacheEvictions, _ = m.Int64Counter( + "cache.evictions", + metric.WithDescription("Number of cache entries evicted"), + metric.WithUnit("{eviction}"), + ) +} + +// cacheEntriesLenFunc is the function called by the Observable Gauge to report +// the current number of cache entries. Nil when no cache is registered. +var cacheEntriesLenFunc func() int + +// registerCacheEntriesGauge registers an Observable Gauge that reads the +// current cache size via lenFunc at collection time. This is race-free +// because it reads the source of truth (cache.Len()) rather than maintaining +// a running counter. +func registerCacheEntriesGauge(m metric.Meter, lenFunc func() int) { + cacheEntriesLenFunc = lenFunc + gauge, _ := m.Int64ObservableGauge( + "cache.entries", + metric.WithDescription("Current number of entries in the validation result cache"), + metric.WithUnit("{entry}"), + ) + _, _ = m.RegisterCallback(func(_ context.Context, o metric.Observer) error { + if cacheEntriesLenFunc != nil { + o.ObserveInt64(gauge, int64(cacheEntriesLenFunc())) + } + return nil + }, gauge) +} diff --git a/pkg/webhook/metrics_test.go b/pkg/webhook/metrics_test.go new file mode 100644 index 000000000..396af0a84 --- /dev/null +++ b/pkg/webhook/metrics_test.go @@ -0,0 +1,186 @@ +// +// Copyright 2026 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package webhook + +import ( + "context" + "testing" + "time" + + "go.opentelemetry.io/otel/attribute" + sdkmetric "go.opentelemetry.io/otel/sdk/metric" + "go.opentelemetry.io/otel/sdk/metric/metricdata" +) + +// setupTestMetrics creates a test MeterProvider and reinitializes the +// package-level cache instruments including the Observable Gauge for +// cache.entries. Returns a ManualReader for asserting metric values. +// Restores the original instruments on test cleanup. +func setupTestMetrics(t *testing.T, cache *LRUCache) *sdkmetric.ManualReader { + t.Helper() + reader := sdkmetric.NewManualReader() + provider := sdkmetric.NewMeterProvider(sdkmetric.WithReader(reader)) + m := provider.Meter("policy-controller") + registerCacheMetrics(m) + registerCacheEntriesGauge(m, cache.cache.Len) + t.Cleanup(func() { + registerCacheMetrics(meter) // restore package-level instruments + provider.Shutdown(context.Background()) + }) + return reader +} + +// getCounterValue extracts an Int64Counter value by metric name and attributes. +// Fails the test immediately if the metric name is not found (catches typos). +func getCounterValue(t *testing.T, reader *sdkmetric.ManualReader, name string, attrs ...attribute.KeyValue) int64 { + t.Helper() + var rm metricdata.ResourceMetrics + if err := reader.Collect(context.Background(), &rm); err != nil { + t.Fatalf("failed to collect metrics: %v", err) + } + set := attribute.NewSet(attrs...) + for _, sm := range rm.ScopeMetrics { + for _, m := range sm.Metrics { + if m.Name != name { + continue + } + if sum, ok := m.Data.(metricdata.Sum[int64]); ok { + for _, dp := range sum.DataPoints { + if dp.Attributes.Equals(&set) { + return dp.Value + } + } + } + t.Fatalf("metric %q found but no data point matching attributes %v", name, attrs) + } + } + t.Fatalf("metric %q not found in collected metrics", name) + return 0 +} + +// getGaugeValue extracts an Int64ObservableGauge value by metric name. +// Fails the test immediately if the metric name is not found (catches typos). +func getGaugeValue(t *testing.T, reader *sdkmetric.ManualReader, name string) int64 { + t.Helper() + var rm metricdata.ResourceMetrics + if err := reader.Collect(context.Background(), &rm); err != nil { + t.Fatalf("failed to collect metrics: %v", err) + } + for _, sm := range rm.ScopeMetrics { + for _, m := range sm.Metrics { + if m.Name != name { + continue + } + if gauge, ok := m.Data.(metricdata.Gauge[int64]); ok { + for _, dp := range gauge.DataPoints { + return dp.Value + } + } + t.Fatalf("metric %q found but no gauge data points", name) + } + } + t.Fatalf("metric %q not found in collected metrics", name) + return 0 +} + +func TestCacheMetricsHitMiss(t *testing.T) { + cache := NewLRUCache(10, 1*time.Hour) + reader := setupTestMetrics(t, cache) + ctx := context.Background() + + // Miss on empty cache + cache.Get(ctx, "img", "uid-1", "v1") + + if got := getCounterValue(t, reader, "cache.operations", attribute.String("result", "miss")); got != 1 { + t.Fatalf("expected 1 miss, got %d", got) + } + + // Store an entry, then hit + cache.Set(ctx, "img", "p", "uid-1", "v1", &CacheResult{ + PolicyResult: &PolicyResult{AuthorityMatches: map[string]AuthorityMatch{}}, + }) + cache.Get(ctx, "img", "uid-1", "v1") + + if got := getCounterValue(t, reader, "cache.operations", attribute.String("result", "hit")); got != 1 { + t.Fatalf("expected 1 hit, got %d", got) + } + if got := getCounterValue(t, reader, "cache.operations", attribute.String("result", "miss")); got != 1 { + t.Fatalf("expected miss count still 1, got %d", got) + } +} + +func TestCacheMetricsWrites(t *testing.T) { + cache := NewLRUCache(10, 1*time.Hour) + reader := setupTestMetrics(t, cache) + ctx := context.Background() + + // Stored write (non-nil PolicyResult) + cache.Set(ctx, "img", "p", "uid-1", "v1", &CacheResult{ + PolicyResult: &PolicyResult{AuthorityMatches: map[string]AuthorityMatch{}}, + }) + + if got := getCounterValue(t, reader, "cache.writes", attribute.String("result", "stored")); got != 1 { + t.Fatalf("expected 1 stored write, got %d", got) + } + + // Skipped write (nil PolicyResult) + cache.Set(ctx, "img2", "p", "uid-1", "v1", &CacheResult{ + Errors: []error{nil}, + }) + + if got := getCounterValue(t, reader, "cache.writes", attribute.String("result", "skipped")); got != 1 { + t.Fatalf("expected 1 skipped write, got %d", got) + } +} + +func TestCacheMetricsEntries(t *testing.T) { + cache := NewLRUCache(10, 1*time.Hour) + reader := setupTestMetrics(t, cache) + ctx := context.Background() + result := &CacheResult{ + PolicyResult: &PolicyResult{AuthorityMatches: map[string]AuthorityMatch{}}, + } + + cache.Set(ctx, "img-1", "p", "uid-1", "v1", result) + if got := getGaugeValue(t, reader, "cache.entries"); got != 1 { + t.Fatalf("expected 1 entry, got %d", got) + } + + cache.Set(ctx, "img-2", "p", "uid-1", "v1", result) + if got := getGaugeValue(t, reader, "cache.entries"); got != 2 { + t.Fatalf("expected 2 entries, got %d", got) + } +} + +func TestCacheMetricsEviction(t *testing.T) { + cache := NewLRUCache(2, 1*time.Hour) + reader := setupTestMetrics(t, cache) + ctx := context.Background() + result := &CacheResult{ + PolicyResult: &PolicyResult{AuthorityMatches: map[string]AuthorityMatch{}}, + } + + cache.Set(ctx, "img-1", "p", "uid-1", "v1", result) + cache.Set(ctx, "img-2", "p", "uid-1", "v1", result) + cache.Set(ctx, "img-3", "p", "uid-1", "v1", result) // evicts img-1 + + if got := getCounterValue(t, reader, "cache.evictions"); got != 1 { + t.Fatalf("expected 1 eviction, got %d", got) + } + if got := getGaugeValue(t, reader, "cache.entries"); got != 2 { + t.Fatalf("expected 2 entries after eviction, got %d", got) + } +} diff --git a/thoughts/plans/2026-02-16-cache-metrics.md b/thoughts/plans/2026-02-16-cache-metrics.md index 8e4ac01c9..0caab3707 100644 --- a/thoughts/plans/2026-02-16-cache-metrics.md +++ b/thoughts/plans/2026-02-16-cache-metrics.md @@ -343,11 +343,11 @@ This is handled automatically by `go mod tidy`. ### Success Criteria: #### Automated Verification: -- [ ] Unit tests pass: `go test ./pkg/webhook/ -run TestCacheMetrics -v` -- [ ] All existing tests still pass: `go test ./pkg/webhook/` -- [ ] Full test suite passes: `make test` -- [ ] Linting passes: `golangci-lint run ./pkg/webhook/...` -- [ ] Module is tidy: `go mod tidy` produces no diff +- [x] Unit tests pass: `go test ./pkg/webhook/ -run TestCacheMetrics -v` +- [x] All existing tests still pass: `go test ./pkg/webhook/` +- [x] Full test suite passes: `make test` +- [x] Linting passes: `golangci-lint run ./pkg/webhook/...` +- [x] Module is tidy: `go mod tidy` produces no diff #### Manual Verification: - [ ] Deploy with `config-observability` set to `metrics-protocol: prometheus` and confirm `/metrics` on `:9090` includes `cache_operations_total`, `cache_writes_total`, `cache_entries`, `cache_evictions_total` From 34f5e68a52135975bc7e62f328e4f4f35ede98eb Mon Sep 17 00:00:00 2001 From: Edvin Norling Date: Mon, 16 Feb 2026 23:56:40 +0100 Subject: [PATCH 12/13] refactor: move gauge registration to LRUCache instance to prevent callback accumulation Each NewLRUCache call was registering a new observable gauge callback on the global meter with no way to unregister, causing callbacks to accumulate across test runs. Move gauge ownership into the LRUCache struct with a Registration field and Close() method, eliminating the global cacheEntriesLenFunc state entirely. Co-Authored-By: Claude Opus 4.6 --- pkg/webhook/lrucache.go | 30 ++++++++++++++++++++++++++++-- pkg/webhook/metrics.go | 24 ------------------------ pkg/webhook/metrics_test.go | 3 ++- 3 files changed, 30 insertions(+), 27 deletions(-) diff --git a/pkg/webhook/lrucache.go b/pkg/webhook/lrucache.go index 8cac0eda5..d3ee87d35 100644 --- a/pkg/webhook/lrucache.go +++ b/pkg/webhook/lrucache.go @@ -30,7 +30,8 @@ import ( // Only successful validations (PolicyResult non-nil) are cached. // Failed validations (PolicyResult nil) are not cached to allow retries. type LRUCache struct { - cache *expirable.LRU[string, *CacheResult] + cache *expirable.LRU[string, *CacheResult] + gaugeRegistration metric.Registration } // NewLRUCache creates a new LRU cache with the given size and TTL. @@ -40,10 +41,35 @@ func NewLRUCache(size int, ttl time.Duration) *LRUCache { cacheEvictions.Add(context.Background(), 1) }, ttl), } - registerCacheEntriesGauge(meter, lc.cache.Len) + lc.registerEntriesGauge(meter) return lc } +// registerEntriesGauge registers an Observable Gauge that reads cache.Len() +// at collection time. Unregisters any previous gauge callback first. +func (c *LRUCache) registerEntriesGauge(m metric.Meter) { + if c.gaugeRegistration != nil { + c.gaugeRegistration.Unregister() + } + gauge, _ := m.Int64ObservableGauge( + "cache.entries", + metric.WithDescription("Current number of entries in the validation result cache"), + metric.WithUnit("{entry}"), + ) + c.gaugeRegistration, _ = m.RegisterCallback(func(_ context.Context, o metric.Observer) error { + o.ObserveInt64(gauge, int64(c.cache.Len())) + return nil + }, gauge) +} + +// Close unregisters the gauge callback. Call when discarding a cache instance. +func (c *LRUCache) Close() error { + if c.gaugeRegistration != nil { + return c.gaugeRegistration.Unregister() + } + return nil +} + func cacheKeyFor(image, uid, resourceVersion string) string { return fmt.Sprintf("%s/%s/%s", image, uid, resourceVersion) } diff --git a/pkg/webhook/metrics.go b/pkg/webhook/metrics.go index 13a9494e3..eb3e7ac28 100644 --- a/pkg/webhook/metrics.go +++ b/pkg/webhook/metrics.go @@ -16,8 +16,6 @@ package webhook import ( - "context" - "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/metric" ) @@ -55,25 +53,3 @@ func registerCacheMetrics(m metric.Meter) { ) } -// cacheEntriesLenFunc is the function called by the Observable Gauge to report -// the current number of cache entries. Nil when no cache is registered. -var cacheEntriesLenFunc func() int - -// registerCacheEntriesGauge registers an Observable Gauge that reads the -// current cache size via lenFunc at collection time. This is race-free -// because it reads the source of truth (cache.Len()) rather than maintaining -// a running counter. -func registerCacheEntriesGauge(m metric.Meter, lenFunc func() int) { - cacheEntriesLenFunc = lenFunc - gauge, _ := m.Int64ObservableGauge( - "cache.entries", - metric.WithDescription("Current number of entries in the validation result cache"), - metric.WithUnit("{entry}"), - ) - _, _ = m.RegisterCallback(func(_ context.Context, o metric.Observer) error { - if cacheEntriesLenFunc != nil { - o.ObserveInt64(gauge, int64(cacheEntriesLenFunc())) - } - return nil - }, gauge) -} diff --git a/pkg/webhook/metrics_test.go b/pkg/webhook/metrics_test.go index 396af0a84..c572da123 100644 --- a/pkg/webhook/metrics_test.go +++ b/pkg/webhook/metrics_test.go @@ -35,9 +35,10 @@ func setupTestMetrics(t *testing.T, cache *LRUCache) *sdkmetric.ManualReader { provider := sdkmetric.NewMeterProvider(sdkmetric.WithReader(reader)) m := provider.Meter("policy-controller") registerCacheMetrics(m) - registerCacheEntriesGauge(m, cache.cache.Len) + cache.registerEntriesGauge(m) t.Cleanup(func() { registerCacheMetrics(meter) // restore package-level instruments + cache.Close() provider.Shutdown(context.Background()) }) return reader From 08e88b61eaf333a4c4a7182d79196333f67218e6 Mon Sep 17 00:00:00 2001 From: Edvin Norling Date: Tue, 17 Feb 2026 20:55:36 +0100 Subject: [PATCH 13/13] refactor: move registerEntriesGauge to metrics.go for cohesion All metric instrument definitions now live together in metrics.go. registerEntriesGauge is a plain function that returns metric.Registration, and LRUCache still owns the lifecycle via its gaugeRegistration field and Close() method. Co-Authored-By: Claude Opus 4.6 --- pkg/webhook/lrucache.go | 19 +------------------ pkg/webhook/metrics.go | 17 +++++++++++++++++ pkg/webhook/metrics_test.go | 3 ++- 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/pkg/webhook/lrucache.go b/pkg/webhook/lrucache.go index d3ee87d35..5ae2159d5 100644 --- a/pkg/webhook/lrucache.go +++ b/pkg/webhook/lrucache.go @@ -41,27 +41,10 @@ func NewLRUCache(size int, ttl time.Duration) *LRUCache { cacheEvictions.Add(context.Background(), 1) }, ttl), } - lc.registerEntriesGauge(meter) + lc.gaugeRegistration = registerEntriesGauge(meter, lc.cache.Len) return lc } -// registerEntriesGauge registers an Observable Gauge that reads cache.Len() -// at collection time. Unregisters any previous gauge callback first. -func (c *LRUCache) registerEntriesGauge(m metric.Meter) { - if c.gaugeRegistration != nil { - c.gaugeRegistration.Unregister() - } - gauge, _ := m.Int64ObservableGauge( - "cache.entries", - metric.WithDescription("Current number of entries in the validation result cache"), - metric.WithUnit("{entry}"), - ) - c.gaugeRegistration, _ = m.RegisterCallback(func(_ context.Context, o metric.Observer) error { - o.ObserveInt64(gauge, int64(c.cache.Len())) - return nil - }, gauge) -} - // Close unregisters the gauge callback. Call when discarding a cache instance. func (c *LRUCache) Close() error { if c.gaugeRegistration != nil { diff --git a/pkg/webhook/metrics.go b/pkg/webhook/metrics.go index eb3e7ac28..a4c68501c 100644 --- a/pkg/webhook/metrics.go +++ b/pkg/webhook/metrics.go @@ -16,6 +16,8 @@ package webhook import ( + "context" + "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/metric" ) @@ -53,3 +55,18 @@ func registerCacheMetrics(m metric.Meter) { ) } +// registerEntriesGauge registers an Observable Gauge that reads lenFunc +// at collection time and returns the Registration for lifecycle management. +func registerEntriesGauge(m metric.Meter, lenFunc func() int) metric.Registration { + gauge, _ := m.Int64ObservableGauge( + "cache.entries", + metric.WithDescription("Current number of entries in the validation result cache"), + metric.WithUnit("{entry}"), + ) + reg, _ := m.RegisterCallback(func(_ context.Context, o metric.Observer) error { + o.ObserveInt64(gauge, int64(lenFunc())) + return nil + }, gauge) + return reg +} + diff --git a/pkg/webhook/metrics_test.go b/pkg/webhook/metrics_test.go index c572da123..0f174e866 100644 --- a/pkg/webhook/metrics_test.go +++ b/pkg/webhook/metrics_test.go @@ -35,7 +35,8 @@ func setupTestMetrics(t *testing.T, cache *LRUCache) *sdkmetric.ManualReader { provider := sdkmetric.NewMeterProvider(sdkmetric.WithReader(reader)) m := provider.Meter("policy-controller") registerCacheMetrics(m) - cache.registerEntriesGauge(m) + cache.Close() + cache.gaugeRegistration = registerEntriesGauge(m, cache.cache.Len) t.Cleanup(func() { registerCacheMetrics(meter) // restore package-level instruments cache.Close()