From 443c10cfd1ad191d54ab0dcf02c388d7f9dd5547 Mon Sep 17 00:00:00 2001 From: Cristian Magherusan-Stanciu Date: Sat, 3 Oct 2026 15:22:10 +0200 Subject: [PATCH 1/2] fix(recommendations): retain incomplete AWS survivors Report typed AWS completeness diagnostics while preserving valid rows during discovery and regional collection. Verify the real command and CSV path against synthetic SDK responses. Refs LeanerCloud/cloud-commitments-go#54 --- cmd/multi_service_helpers.go | 11 +- cmd/recommendation_completeness_proxy_test.go | 198 ++++++++++++++++++ cmd/recommendation_completeness_test.go | 179 ++++++++++++++++ go.mod | 4 +- go.sum | 8 +- 5 files changed, 393 insertions(+), 7 deletions(-) create mode 100644 cmd/recommendation_completeness_proxy_test.go create mode 100644 cmd/recommendation_completeness_test.go diff --git a/cmd/multi_service_helpers.go b/cmd/multi_service_helpers.go index 5770ed59e..387508d00 100644 --- a/cmd/multi_service_helpers.go +++ b/cmd/multi_service_helpers.go @@ -2,6 +2,7 @@ package main import ( "context" + "errors" "fmt" "log" "sort" @@ -108,7 +109,10 @@ func getAllAWSRegionsWithClient(ctx context.Context, ec2Client EC2ClientInterfac // discoverRegionsForService discovers regions that have recommendations for a specific service. func discoverRegionsForService(ctx context.Context, client provider.RecommendationsClient, service common.ServiceType) ([]string, error) { recs, err := client.GetRecommendationsForService(ctx, service) - if partial := azureprovider.AsPartialSubscriptionFailure(err); partial != nil { + var incomplete *recommendations.IncompleteRecommendationsError + if errors.As(err, &incomplete) { + AppLogger.Printf(" ⚠️ Region discovery incomplete: %v\n", incomplete) + } else if partial := azureprovider.AsPartialSubscriptionFailure(err); partial != nil { // Region discovery is best-effort: the subscriptions that answered // still tell us where to look. Report the gap rather than dropping // the discovered regions or failing outright. @@ -463,6 +467,11 @@ func fetchRecommendationsForRegion( } recs, err := recClient.GetRecommendations(ctx, ¶ms) + var incomplete *recommendations.IncompleteRecommendationsError + if errors.As(err, &incomplete) { + AppLogger.Printf(" ⚠️ %v\n", incomplete) + return recs + } if partial := azureprovider.AsPartialSubscriptionFailure(err); partial != nil { // Keep the subscriptions that did answer, but say plainly that the // sweep was incomplete: without this the operator would read a short diff --git a/cmd/recommendation_completeness_proxy_test.go b/cmd/recommendation_completeness_proxy_test.go new file mode 100644 index 000000000..ec8a71b5e --- /dev/null +++ b/cmd/recommendation_completeness_proxy_test.go @@ -0,0 +1,198 @@ +package main + +import ( + "bufio" + "crypto/rand" + "crypto/rsa" + "crypto/tls" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "errors" + "fmt" + "io" + "math/big" + "net/http" + "net/http/httptest" + "net/url" + "os" + "path/filepath" + "strings" + "sync" + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +type completenessProxy struct { + t *testing.T + mu sync.Mutex + handlers sync.WaitGroup + regions string + details string + requests map[string]int + cert tls.Certificate +} + +func newCompletenessProxy(t *testing.T, dir, regions, details string) (*completenessProxy, string, string) { + t.Helper() + key, err := rsa.GenerateKey(rand.Reader, 2048) + require.NoError(t, err) + template := &x509.Certificate{ + SerialNumber: big.NewInt(54), Subject: pkix.Name{CommonName: "recommendation fixture"}, + DNSNames: []string{"ce.us-east-1.amazonaws.com", "ec2.us-east-1.amazonaws.com", "rds.us-east-1.amazonaws.com"}, + NotBefore: time.Now().Add(-time.Hour), NotAfter: time.Now().Add(time.Hour), + IsCA: true, BasicConstraintsValid: true, + KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + } + der, err := x509.CreateCertificate(rand.Reader, template, template, &key.PublicKey, key) + require.NoError(t, err) + certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: der}) + caPath := filepath.Join(dir, "ca.pem") + require.NoError(t, os.WriteFile(caPath, certPEM, 0600)) + fixture := &completenessProxy{t: t, regions: regions, details: details, requests: make(map[string]int), + cert: tls.Certificate{Certificate: [][]byte{der}, PrivateKey: key}} + server := httptest.NewServer(http.HandlerFunc(fixture.serveConnect)) + t.Cleanup(func() { + server.Close() + fixture.handlers.Wait() + }) + return fixture, server.URL, caPath +} + +func (p *completenessProxy) serveConnect(w http.ResponseWriter, r *http.Request) { + p.handlers.Add(1) + defer p.handlers.Done() + allowed := r.Host == "ce.us-east-1.amazonaws.com:443" || r.Host == "ec2.us-east-1.amazonaws.com:443" || r.Host == "rds.us-east-1.amazonaws.com:443" + if r.Method != http.MethodConnect || !allowed { + p.t.Errorf("unexpected CONNECT %s %s", r.Method, r.Host) + http.Error(w, "forbidden", http.StatusForbidden) + return + } + conn, _, err := w.(http.Hijacker).Hijack() + if err != nil { + p.t.Error(err) + return + } + defer conn.Close() + if err := conn.SetDeadline(time.Now().Add(40 * time.Second)); err != nil { + p.t.Error(err) + return + } + if _, err := fmt.Fprint(conn, "HTTP/1.1 200 Connection Established\r\n\r\n"); err != nil { + p.t.Error(err) + return + } + secure := tls.Server(conn, &tls.Config{Certificates: []tls.Certificate{p.cert}, MinVersion: tls.VersionTLS12}) + if err := secure.Handshake(); err != nil { + p.t.Errorf("TLS handshake: %v", err) + return + } + reader := bufio.NewReader(secure) + for { + req, err := http.ReadRequest(reader) + if errors.Is(err, io.EOF) { + return + } + if err != nil { + p.t.Errorf("read request: %v", err) + return + } + if req.Host != strings.TrimSuffix(r.Host, ":443") { + p.t.Errorf("tunnel host mismatch: %s", req.Host) + return + } + response := p.respond(req) + err = response.Write(secure) + _ = response.Body.Close() + if err != nil { + p.t.Errorf("write response: %v", err) + return + } + } +} + +func (p *completenessProxy) respond(req *http.Request) *http.Response { + body, err := io.ReadAll(req.Body) + _ = req.Body.Close() + if err != nil { + p.t.Error(err) + } + op := req.Header.Get("X-Amz-Target") + if op == "" { + values, parseErr := url.ParseQuery(string(body)) + if parseErr != nil { + p.t.Error(parseErr) + } + op = values.Get("Action") + } + p.mu.Lock() + p.requests[op]++ + call := p.requests[op] + p.mu.Unlock() + status, contentType, payload := p.operation(req.Host, op, call) + return &http.Response{StatusCode: status, ProtoMajor: 1, ProtoMinor: 1, + Header: http.Header{"Content-Type": []string{contentType}}, + Body: io.NopCloser(strings.NewReader(payload)), ContentLength: int64(len(payload))} +} + +func (p *completenessProxy) operation(host, op string, call int) (int, string, string) { + if host == "ce.us-east-1.amazonaws.com" && op == "AWSInsightsIndexService.GetReservationCoverage" { + return 200, "application/x-amz-json-1.1", `{"CoveragesByTime":[]}` + } + if host == "ce.us-east-1.amazonaws.com" && op == "AWSInsightsIndexService.GetReservationPurchaseRecommendation" { + if p.details == "api-error" { + return 400, "application/x-amz-json-1.1", `{"__type":"AccessDeniedException","message":"fixture denied recommendations"}` + } + const valid = `{"RecommendedNumberOfInstancesToPurchase":"2","EstimatedMonthlySavingsAmount":"10", + "EstimatedMonthlyOnDemandCost":"30","InstanceDetails":{"RDSInstanceDetails":{ + "InstanceType":"db.t3.medium","Region":"us-east-1","DeploymentOption":"Single-AZ"}}}` + const invalid = `{"RecommendedNumberOfInstancesToPurchase":"not-a-number"}` + details := map[string]string{ + "valid": valid, + "mixed": valid + "," + invalid, + "all-invalid": invalid, + "empty": "", + } + return 200, "application/x-amz-json-1.1", `{"Recommendations":[{"RecommendationDetails":[` + details[p.details] + `]}]}` + } + if host == "ec2.us-east-1.amazonaws.com" && op == "DescribeRegions" { + if p.regions == "fallback" && call == 2 { + return 400, "text/xml", `UnauthorizedOperationfixture denied region discovery` + } + return 200, "text/xml", `us-east-1` + } + responses := map[string]string{ + "DescribeDBInstances": "DBInstances", + "DescribeDBMajorEngineVersions": "DBMajorEngineVersions", + "DescribeReservedDBInstances": "ReservedDBInstances", + } + if element, ok := responses[op]; ok && host == "rds.us-east-1.amazonaws.com" { + return 200, "text/xml", fmt.Sprintf(`<%sResponse xmlns="http://rds.amazonaws.com/doc/2014-10-31/"><%sResult><%s/>`, op, op, element, op, op) + } + p.t.Errorf("unexpected operation %s %s", host, op) + return 403, "text/plain", "fixture rejected operation" +} + +func (p *completenessProxy) assertRequests(t *testing.T) { + t.Helper() + p.handlers.Wait() + p.mu.Lock() + defer p.mu.Unlock() + ceCalls := 1 + if p.regions == "fallback" { + ceCalls = 6 + if p.details == "valid" || p.details == "mixed" { + ceCalls++ + } + } + require.Equal(t, ceCalls, p.requests["AWSInsightsIndexService.GetReservationPurchaseRecommendation"], "operations=%v", p.requests) + wantRegions := 2 + if p.regions == "explicit" { + wantRegions = 1 + } + require.Equal(t, wantRegions, p.requests["DescribeRegions"], "region calls") + t.Logf("actual root command, config loader, SDK and CSV; synthetic operations=%v", p.requests) +} diff --git a/cmd/recommendation_completeness_test.go b/cmd/recommendation_completeness_test.go new file mode 100644 index 000000000..1292cd3fe --- /dev/null +++ b/cmd/recommendation_completeness_test.go @@ -0,0 +1,179 @@ +package main + +import ( + "bytes" + "context" + "crypto/tls" + "crypto/x509" + "encoding/csv" + "errors" + "fmt" + "net/http" + "os" + "os/exec" + "path/filepath" + "strings" + "testing" + "time" + + "github.com/LeanerCloud/cloud-commitments-go/pkg/common" + "github.com/LeanerCloud/cloud-commitments-go/providers/aws/recommendations" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" +) + +func TestRecommendationCompletenessCommand(t *testing.T) { + if os.Getenv("CUDLY_COMPLETENESS_CHILD") == "1" { + runCompletenessCommandChild(t) + return + } + for _, regionMode := range []string{"explicit", "default", "fallback"} { + for _, details := range []string{"valid", "mixed", "all-invalid", "empty", "api-error"} { + t.Run(regionMode+"/"+details, func(t *testing.T) { + dir := t.TempDir() + fixture, proxyURL, caPath := newCompletenessProxy(t, dir, regionMode, details) + output := filepath.Join(dir, "recommendations.csv") + ctx, cancel := context.WithTimeout(context.Background(), 45*time.Second) + defer cancel() + child := exec.CommandContext(ctx, os.Args[0], "-test.run=^TestRecommendationCompletenessCommand$", "-test.v") + child.Env = []string{ + "PATH=/usr/bin:/bin", "HOME=" + dir, "TMPDIR=" + dir, + "CUDLY_COMPLETENESS_CHILD=1", "CUDLY_COMPLETENESS_OUTPUT=" + output, + "CUDLY_COMPLETENESS_AUDIT=" + filepath.Join(dir, "audit.jsonl"), + "CUDLY_COMPLETENESS_REGIONS=" + regionMode, + "AWS_ACCESS_KEY_ID=synthetic", "AWS_SECRET_ACCESS_KEY=synthetic", "AWS_REGION=us-east-1", + "AWS_EC2_METADATA_DISABLED=true", "AWS_MAX_ATTEMPTS=1", + "AWS_CONFIG_FILE=" + filepath.Join(dir, "absent-config"), + "AWS_SHARED_CREDENTIALS_FILE=" + filepath.Join(dir, "absent-credentials"), + "HTTPS_PROXY=" + proxyURL, "HTTP_PROXY=" + proxyURL, "AWS_CA_BUNDLE=" + caPath, + } + var stdout, stderr bytes.Buffer + child.Stdout, child.Stderr = &stdout, &stderr + require.NoError(t, child.Run(), "stdout: %s\nstderr: %s", &stdout, &stderr) + fixture.assertRequests(t) + logs := stdout.String() + stderr.String() + for _, failure := range []string{"Could not check", "Failed to query", "request send failed", "certificate", "TLS handshake"} { + require.NotContains(t, logs, failure, "unexpected ancillary error") + } + assertCompletenessDiagnostics(t, regionMode, details, stdout.String(), stderr.String()) + assertCompletenessCSV(t, output, details, logs) + }) + } + } +} + +func runCompletenessCommandChild(t *testing.T) { + t.Helper() + ca, err := os.ReadFile(os.Getenv("AWS_CA_BUNDLE")) + require.NoError(t, err) + roots := x509.NewCertPool() + require.True(t, roots.AppendCertsFromPEM(ca), "invalid fixture CA") + transport := http.DefaultTransport.(*http.Transport).Clone() + transport.TLSClientConfig = &tls.Config{RootCAs: roots, MinVersion: tls.VersionTLS12} + http.DefaultTransport = transport + t.Cleanup(transport.CloseIdleConnections) + args := []string{"--services", "rds", "--term", "1", "--include-extended-support", "--coverage", "100", + "--output", os.Getenv("CUDLY_COMPLETENESS_OUTPUT"), "--audit-log", os.Getenv("CUDLY_COMPLETENESS_AUDIT")} + if os.Getenv("CUDLY_COMPLETENESS_REGIONS") == "explicit" { + args = append(args, "--regions", "us-east-1") + } + rootCmd.SetArgs(args) + require.NoError(t, rootCmd.Execute()) +} + +func assertCompletenessDiagnostics(t *testing.T, regionMode, details, stdout, stderr string) { + t.Helper() + malformed := details == "mixed" || details == "all-invalid" + if malformed { + count := 1 + if regionMode == "fallback" { + count = 6 + } + want := fmt.Sprintf("incomplete AWS recommendations: %d failed details, 0 failed scopes:", count) + for _, diagnostic := range []string{want, "not-a-number", "detail"} { + require.Contains(t, stdout, diagnostic, "stderr: %s", stderr) + } + if regionMode == "fallback" { + require.Contains(t, stdout, "Region discovery incomplete") + } + } else { + require.NotContains(t, stdout, "incomplete AWS recommendations") + } + if details == "api-error" { + require.Contains(t, stdout+stderr, "fixture denied recommendations") + } else { + require.NotContains(t, stdout+stderr, "Failed to fetch recommendations", "partial collection treated as fatal") + require.NotContains(t, stdout+stderr, "failed to discover regions", "partial collection treated as fatal") + } +} + +func assertCompletenessCSV(t *testing.T, output, details, logs string) { + t.Helper() + data, err := os.ReadFile(output) + if details != "valid" && details != "mixed" { + require.ErrorIs(t, err, os.ErrNotExist, "unexpected CSV: %s\n%s", data, logs) + require.NotContains(t, logs, "CSV report written", "false CSV success for %s", details) + return + } + require.NoError(t, err, "CSV: %s", logs) + rows, err := csv.NewReader(bytes.NewReader(data)).ReadAll() + require.NoError(t, err) + require.Len(t, rows, 3, "want header, exact surviving RDS row and total: %s", logs) + require.Equal(t, "TOTAL", rows[2][0]) + columns := make(map[string]string) + for i, header := range rows[0] { + columns[header] = rows[1][i] + } + for field, want := range map[string]string{"Service": "rds", "Region": "us-east-1", "ResourceType": "db.t3.medium", "Count": "2"} { + require.Equal(t, want, columns[field], "CSV %s; row=%v", field, rows[1]) + } + require.Contains(t, logs, "CSV report written") +} + +func TestRecommendationCompletenessHelpers(t *testing.T) { + partial := &recommendations.IncompleteRecommendationsError{ + FailedDetails: 2, FailedScopes: 1, + Causes: []error{errors.New("rds block 0 detail 1: invalid quantity"), errors.New("rds block 1 detail 0: invalid cost"), errors.New("rds 3yr AllUpfront: denied")}, + } + survivors := []common.Recommendation{{Service: common.ServiceRDS, Region: "us-east-1", Count: 2}} + for _, tc := range []struct { + name string + err error + recs []common.Recommendation + keep bool + }{ + {"partial", fmt.Errorf("wrapped: %w", partial), survivors, true}, + {"all-invalid", partial, nil, true}, + {"ordinary", errors.New("API failed"), survivors, false}, + {"canceled", context.Canceled, survivors, false}, + {"deadline", context.DeadlineExceeded, survivors, false}, + } { + t.Run(tc.name, func(t *testing.T) { + var output bytes.Buffer + previous := AppLogger.Writer() + AppLogger.SetOutput(&output) + t.Cleanup(func() { AppLogger.SetOutput(previous) }) + ctx := context.Background() + client := &MockRecommendationsClient{} + client.On("GetRecommendationsForService", ctx, common.ServiceRDS).Return(tc.recs, tc.err) + client.On("GetRecommendations", ctx, mock.Anything).Return(tc.recs, tc.err) + regions, err := discoverRegionsForService(ctx, client, common.ServiceRDS) + got := fetchRecommendationsForRegion(ctx, client, common.ServiceRDS, "us-east-1", Config{TermYears: 1}) + if !tc.keep { + require.ErrorIs(t, err, tc.err) + require.Nil(t, regions) + require.Nil(t, got) + require.NotContains(t, output.String(), "incomplete AWS recommendations") + } else { + require.NoError(t, err) + require.Equal(t, tc.recs, got) + require.Len(t, regions, len(tc.recs)) + require.Equal(t, 2, strings.Count(output.String(), "2 failed details, 1 failed scopes")) + for _, cause := range partial.Causes { + require.Equal(t, 2, strings.Count(output.String(), cause.Error())) + } + } + client.AssertExpectations(t) + }) + } +} diff --git a/go.mod b/go.mod index ebb36821a..bc1f6d68b 100644 --- a/go.mod +++ b/go.mod @@ -82,8 +82,8 @@ require ( require ( github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v2 v2.2.0 - github.com/LeanerCloud/cloud-commitments-go/pkg v0.0.0-20260928214714-ce9513612901 - github.com/LeanerCloud/cloud-commitments-go/providers/aws v0.0.0-20260928214714-ce9513612901 + github.com/LeanerCloud/cloud-commitments-go/pkg v0.0.0-20260929105827-b3b4cb5e3d80 + github.com/LeanerCloud/cloud-commitments-go/providers/aws v0.0.0-20261002152209-006ef5c8d0a2 github.com/LeanerCloud/cloud-commitments-go/providers/azure v0.0.0-20260928214714-ce9513612901 github.com/LeanerCloud/cloud-commitments-go/providers/gcp v0.0.0-20260928214714-ce9513612901 github.com/aws/aws-sdk-go-v2/service/organizations v1.45.3 diff --git a/go.sum b/go.sum index e9ca132e1..92b158c78 100644 --- a/go.sum +++ b/go.sum @@ -78,10 +78,10 @@ github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0 github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.54.0/go.mod h1:vB2GH9GAYYJTO3mEn8oYwzEdhlayZIdQz6zdzgUIRvA= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.54.0 h1:s0WlVbf9qpvkh1c/uDAPElam0WrL7fHRIidgZJ7UqZI= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.54.0/go.mod h1:Mf6O40IAyB9zR/1J8nGDDPirZQQPbYJni8Yisy7NTMc= -github.com/LeanerCloud/cloud-commitments-go/pkg v0.0.0-20260928214714-ce9513612901 h1:JWQwkwshjBoEuoIuFK1KHh70yZDYQTrm5GRloD6ojjU= -github.com/LeanerCloud/cloud-commitments-go/pkg v0.0.0-20260928214714-ce9513612901/go.mod h1:ApWBliDXe099f3oDXBz41K/I9v4bHvn1dG/BGoRmHlw= -github.com/LeanerCloud/cloud-commitments-go/providers/aws v0.0.0-20260928214714-ce9513612901 h1:18MxQd7+ZQ+Z2uNbZfpIJQ00JWdSdTVSmVWBzg4R0DA= -github.com/LeanerCloud/cloud-commitments-go/providers/aws v0.0.0-20260928214714-ce9513612901/go.mod h1:tIUCLVX6utdBPiP6dAjY7GUfwIQngeSMK5A9aRqe6gA= +github.com/LeanerCloud/cloud-commitments-go/pkg v0.0.0-20260929105827-b3b4cb5e3d80 h1:wVKlMokfaME/Lw525Qz3F155R3nY4VmuaIyh+6d4sCU= +github.com/LeanerCloud/cloud-commitments-go/pkg v0.0.0-20260929105827-b3b4cb5e3d80/go.mod h1:ApWBliDXe099f3oDXBz41K/I9v4bHvn1dG/BGoRmHlw= +github.com/LeanerCloud/cloud-commitments-go/providers/aws v0.0.0-20261002152209-006ef5c8d0a2 h1:rYxXq0G0O2lkf8AB1ni9m4pVhIdb6Z0y52sTxS1RKIA= +github.com/LeanerCloud/cloud-commitments-go/providers/aws v0.0.0-20261002152209-006ef5c8d0a2/go.mod h1:d4nsy61/Ptib0SxqMg0ble3yksVm3+QbtUtmGja82PA= github.com/LeanerCloud/cloud-commitments-go/providers/azure v0.0.0-20260928214714-ce9513612901 h1:iSdHYdmGUjcjtSmgGZxptBzDuLrJ9KR2mh1/x7FNvSY= github.com/LeanerCloud/cloud-commitments-go/providers/azure v0.0.0-20260928214714-ce9513612901/go.mod h1:zgCL/ozOkcZUDbEC7a2UwW+6MPcLoBlEU2WUZ0TORUs= github.com/LeanerCloud/cloud-commitments-go/providers/gcp v0.0.0-20260928214714-ce9513612901 h1:i6OwXLUheudN3GfwnYXdKuEq8vPdE9qkNJ+r71LRLKA= From 6abdb1469c04b0876bb983d22eb6af9ca92b4b20 Mon Sep 17 00:00:00 2001 From: Cristian Magherusan-Stanciu Date: Sat, 3 Oct 2026 20:39:26 +0200 Subject: [PATCH 2/2] fix(deps): update otel sdk for GO-2026-6505 Preserve the published provider pins while selecting the fixed SDK. The unchanged parent reproduces the advisory against the same frozen database used for passing source and command-binary scans. --- go.mod | 12 ++++++------ go.sum | 28 ++++++++++++++-------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/go.mod b/go.mod index bc1f6d68b..e8166a8a4 100644 --- a/go.mod +++ b/go.mod @@ -47,7 +47,7 @@ require ( github.com/aws/smithy-go v1.24.2 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/go-logr/logr v1.4.3 // indirect + github.com/go-logr/logr v1.4.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/golang-jwt/jwt/v5 v5.3.1 // indirect github.com/google/s2a-go v0.1.9 // indirect @@ -61,9 +61,9 @@ require ( github.com/stretchr/objx v0.5.3 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect - go.opentelemetry.io/otel v1.44.0 // indirect - go.opentelemetry.io/otel/metric v1.44.0 // indirect - go.opentelemetry.io/otel/trace v1.44.0 // indirect + go.opentelemetry.io/otel v1.45.0 // indirect + go.opentelemetry.io/otel/metric v1.45.0 // indirect + go.opentelemetry.io/otel/trace v1.45.0 // indirect golang.org/x/crypto v0.56.0 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/oauth2 v0.36.0 @@ -126,6 +126,6 @@ require ( github.com/std-uritemplate/std-uritemplate/go/v2 v2.0.3 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/contrib/detectors/gcp v1.44.0 // indirect - go.opentelemetry.io/otel/sdk v1.44.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.44.0 // indirect + go.opentelemetry.io/otel/sdk v1.45.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.45.0 // indirect ) diff --git a/go.sum b/go.sum index 92b158c78..b0f0bd421 100644 --- a/go.sum +++ b/go.sum @@ -152,8 +152,8 @@ github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSw github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA= github.com/go-jose/go-jose/v4 v4.1.4/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.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= -github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8= +github.com/go-logr/logr v1.4.4/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/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= @@ -229,20 +229,20 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.6 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0/go.mod h1:fvPi2qXDqFs8M4B4fmJhE92TyQs9Ydjlg3RvfUp+NbQ= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q= -go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU= -go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc= +go.opentelemetry.io/otel v1.45.0 h1:pdrWmLHofpubmArBv1LgFSv1Z0Ie/ppdZzu+kUN5EeU= +go.opentelemetry.io/otel v1.45.0/go.mod h1:XZxIqPapzEYnhNSScF5DIqXhm/rYi0FzCe2XddAwZfQ= go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.38.0 h1:wm/Q0GAAykXv83wzcKzGGqAnnfLFyFe7RslekZuv+VI= go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.38.0/go.mod h1:ra3Pa40+oKjvYh+ZD3EdxFZZB0xdMfuileHAm4nNN7w= -go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc= -go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo= -go.opentelemetry.io/otel/metric/x v0.66.0 h1:YkCrx1zLOChi9ZcZ6euupOcsgzbVlec7D/xoEU1+cTA= -go.opentelemetry.io/otel/metric/x v0.66.0/go.mod h1:d1+BDj9t96do0/1LoU1ayfCv79ZgNE41qbhBvnMOBZk= -go.opentelemetry.io/otel/sdk v1.44.0 h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58= -go.opentelemetry.io/otel/sdk v1.44.0/go.mod h1:Osuydd3Se74nqjAKxid74N5eC+jfEqfTegHRnq58oK0= -go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRks6si09iEfI= -go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA= -go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk= -go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE= +go.opentelemetry.io/otel/metric v1.45.0 h1:7Eg1uH7CJ5cXv9is6tnBe1FI6rj1nwUdbFypRm3br/M= +go.opentelemetry.io/otel/metric v1.45.0/go.mod h1:HAPbm1nd3p1PmFH7v2dR+6BjXxw+Lq4a2+pndMAm08s= +go.opentelemetry.io/otel/metric/x v0.67.0 h1:PcicCNZFkZ4bXfSooXdo3WN7RBOVOtjVdo1wD358Uns= +go.opentelemetry.io/otel/metric/x v0.67.0/go.mod h1:FBjCWZe6wgcqxcMtjdGiClDKXb2YxxXii0CXftE4QtI= +go.opentelemetry.io/otel/sdk v1.45.0 h1:4VVSMgQ83dUgW2aoX5f6JgLvHwIvzcuLnF9lUdCSpCw= +go.opentelemetry.io/otel/sdk v1.45.0/go.mod h1:Sr40LgXV7DsKMMJMKOhUWOgMWTfAaqvm2kF0g7ilwuA= +go.opentelemetry.io/otel/sdk/metric v1.45.0 h1:oVFszMfyj1Am6s24Vtc7wBb8BKLcwepJjNEYILuiE3o= +go.opentelemetry.io/otel/sdk/metric v1.45.0/go.mod h1:vUWUxDZvu1WVRj8JA8S0AdhsPrZoDpA2DdZauIh4mDA= +go.opentelemetry.io/otel/trace v1.45.0 h1:l/mP6Uv7oNO7/TblbhpbgMidxhq1uO/rPsikOyVhxag= +go.opentelemetry.io/otel/trace v1.45.0/go.mod h1:qoJJA2xNMnxRrdISU/kLtfUH2wNeQbiv+jhs/CxI8bc= golang.org/x/crypto v0.56.0 h1:GUh5Ii4J5jtcseSMiRqr1jXCNHoxjeV9Fmekc2oLy6Y= golang.org/x/crypto v0.56.0/go.mod h1:OMW5y6CY9l38uPLmxU6l6pwcXp1obtLo3e6gT7gQR2I= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To=