diff --git a/README.md b/README.md index f514a85d..d8d9ae3a 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ json_exporter ======================== [![CircleCI](https://circleci.com/gh/prometheus-community/json_exporter.svg?style=svg)](https://circleci.com/gh/prometheus-community/json_exporter) -A [prometheus](https://prometheus.io/) exporter which scrapes remote JSON by JSONPath. +A [prometheus](https://prometheus.io/) exporter which scrapes remote JSON by JSONPath or [CEL (Common Expression Language)](https://github.com/google/cel-spec). - [Supported JSONPath Syntax](https://kubernetes.io/docs/reference/kubectl/jsonpath/) - [Examples configurations](/examples) @@ -21,6 +21,24 @@ Serving HTTP on :: port 8000 (http://[::]:8000/) ... ## TEST with 'default' module $ curl "http://localhost:7979/probe?module=default&target=http://localhost:8000/examples/data.json" +# HELP example_cel_global_value Example of a top-level global value scrape in the json using cel +# TYPE example_cel_global_value gauge +example_cel_global_value{environment="beta",location="planet-mars"} 1234 +# HELP example_cel_timestamped_value_count Example of a timestamped value scrape in the json +# TYPE example_cel_timestamped_value_count untyped +example_cel_timestamped_value_count{environment="beta"} 2 +# HELP example_cel_value_active Example of sub-level value scrapes from a json +# TYPE example_cel_value_active untyped +example_cel_value_active{environment="beta",id="id-A"} 1 +example_cel_value_active{environment="beta",id="id-C"} 1 +# HELP example_cel_value_boolean Example of sub-level value scrapes from a json +# TYPE example_cel_value_boolean untyped +example_cel_value_boolean{environment="beta",id="id-A"} 1 +example_cel_value_boolean{environment="beta",id="id-C"} 0 +# HELP example_cel_value_count Example of sub-level value scrapes from a json +# TYPE example_cel_value_count untyped +example_cel_value_count{environment="beta",id="id-A"} 1 +example_cel_value_count{environment="beta",id="id-C"} 3 # HELP example_global_value Example of a top-level global value scrape in the json # TYPE example_global_value untyped example_global_value{environment="beta",location="planet-mars"} 1234 diff --git a/config/config.go b/config/config.go index 6cd0accb..6a69ed58 100644 --- a/config/config.go +++ b/config/config.go @@ -23,6 +23,7 @@ import ( // Metric contains values that define a metric type Metric struct { Name string + Engine EngineType Path string Labels map[string]string Type ScrapeType @@ -44,7 +45,14 @@ type ValueType string const ( ValueTypeGauge ValueType = "gauge" ValueTypeCounter ValueType = "counter" - ValueTypeUntyped ValueType = "untyped" + ValueTypeUntyped ValueType = "untyped" // default +) + +type EngineType string + +const ( + EngineTypeJSONPath EngineType = "jsonpath" // default + EngineTypeCEL EngineType = "cel" ) // Config contains multiple modules. @@ -89,6 +97,9 @@ func LoadConfig(configPath string) (Config, error) { if module.Metrics[i].ValueType == "" { module.Metrics[i].ValueType = ValueTypeUntyped } + if module.Metrics[i].Engine == "" { + module.Metrics[i].Engine = EngineTypeJSONPath + } } } diff --git a/examples/config.yml b/examples/config.yml index 9d0745c0..bdd9ce94 100644 --- a/examples/config.yml +++ b/examples/config.yml @@ -9,7 +9,17 @@ modules: help: Example of a top-level global value scrape in the json labels: environment: beta # static label - location: 'planet-{.location}' # dynamic label + location: 'planet-{ .location }' # dynamic label + + - name: example_cel_global_value + engine: cel + path: '.counter' + help: Example of a top-level global value scrape in the json using cel + valuetype: 'gauge' + labels: + environment: "\"beta\"" # static label. Quotes need to be escaped for CEL + location: "\"planet-\"+.location" # dynamic label. Quotes need to be escaped for CEL + - name: example_timestamped_value type: object path: '{ .values[?(@.state == "INACTIVE")] }' @@ -18,18 +28,44 @@ modules: labels: environment: beta # static label values: - count: '{.count}' # dynamic value + count: '{ .count }' # dynamic value + + - name: example_cel_timestamped_value + type: object + engine: cel + path: ".values.filter(i, i.state == \"INACTIVE\")" + epochTimestamp: '.timestamp' + help: Example of a timestamped value scrape in the json + labels: + environment: "\"beta\"" # static label + values: + count: '.count' # dynamic value + - name: example_value type: object help: Example of sub-level value scrapes from a json - path: '{.values[?(@.state == "ACTIVE")]}' + path: '{ .values[?(@.state == "ACTIVE")] }' labels: environment: beta # static label - id: '{.id}' # dynamic label + id: '{ .id }' # dynamic label values: active: 1 # static value - count: '{.count}' # dynamic value - boolean: '{.some_boolean}' + count: '{ .count }' # dynamic value + boolean: '{ .some_boolean }' + + - name: example_cel_value + type: object + engine: cel + help: Example of sub-level value scrapes from a json + path: ".values.filter(i, i.state == \"ACTIVE\")" + labels: + environment: "\"beta\"" # static label + id: '.id' # dynamic label + values: + active: 1 # static value + count: '.count' # dynamic value + boolean: '.some_boolean' + animals: metrics: @@ -43,6 +79,17 @@ modules: values: population: '{ .population }' + - name: animal_cel + type: object + engine: cel + help: Example of top-level lists in a separate module + path: '[*]' + labels: + name: '.noun' + predator: '.predator' + values: + population: '.population' + ## HTTP connection configurations can be set in 'modules..http_client_config' field. For full http client config parameters, ref: https://pkg.go.dev/github.com/prometheus/common/config?tab=doc#HTTPClientConfig # # http_client_config: @@ -59,11 +106,11 @@ modules: ## If 'modueles..body' field is set, it will be sent by the exporter as the body content in the scrape request. The HTTP method will also be set as 'POST' in this case. # body: # content: | - # {"time_diff": "1m25s", "anotherVar": "some value"} + # { "time_diff": "1m25s", "anotherVar": "some value" } ## The body content can also be a Go Template (https://golang.org/pkg/text/template), with all the functions from the Sprig library (https://masterminds.github.io/sprig/) available. All the query parameters sent by prometheus in the scrape query to the exporter, are available in the template. # body: # content: | - # {"time_diff": "{{ duration `95` }}","anotherVar": "{{ .myVal | first }}"} + # { "time_diff": "{{ duration `95` }}","anotherVar": "{{ .myVal | first }}" } # templatize: true diff --git a/exporter/collector.go b/exporter/collector.go index 0a913897..cc34d301 100644 --- a/exporter/collector.go +++ b/exporter/collector.go @@ -16,11 +16,18 @@ package exporter import ( "bytes" "encoding/json" + "fmt" "log/slog" + "reflect" "time" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/common/types/ref" "github.com/prometheus-community/json_exporter/config" "github.com/prometheus/client_golang/prometheus" + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/proto" + structpb "google.golang.org/protobuf/types/known/structpb" "k8s.io/client-go/util/jsonpath" ) @@ -33,6 +40,7 @@ type JSONMetricCollector struct { type JSONMetric struct { Desc *prometheus.Desc Type config.ScrapeType + EngineType config.EngineType KeyJSONPath string ValueJSONPath string LabelsJSONPaths []string @@ -50,7 +58,8 @@ func (mc JSONMetricCollector) Collect(ch chan<- prometheus.Metric) { for _, m := range mc.JSONMetrics { switch m.Type { case config.ValueScrape: - value, err := extractValue(mc.Logger, mc.Data, m.KeyJSONPath, false) + mc.Logger.Debug("Extracting value for metric", "path", m.KeyJSONPath, "metric", m.Desc) + value, err := extractValue(mc.Logger, m.EngineType, mc.Data, m.KeyJSONPath, false) if err != nil { mc.Logger.Error("Failed to extract value for metric", "path", m.KeyJSONPath, "err", err, "metric", m.Desc) continue @@ -61,7 +70,7 @@ func (mc JSONMetricCollector) Collect(ch chan<- prometheus.Metric) { m.Desc, m.ValueType, floatValue, - extractLabels(mc.Logger, mc.Data, m.LabelsJSONPaths)..., + extractLabels(mc.Logger, m.EngineType, mc.Data, m.LabelsJSONPaths)..., ) ch <- timestampMetric(mc.Logger, m, mc.Data, metric) } else { @@ -70,7 +79,8 @@ func (mc JSONMetricCollector) Collect(ch chan<- prometheus.Metric) { } case config.ObjectScrape: - values, err := extractValue(mc.Logger, mc.Data, m.KeyJSONPath, true) + mc.Logger.Debug("Extracting object for metric", "path", m.KeyJSONPath, "metric", m.Desc) + values, err := extractValue(mc.Logger, m.EngineType, mc.Data, m.KeyJSONPath, true) if err != nil { mc.Logger.Error("Failed to extract json objects for metric", "err", err, "metric", m.Desc) continue @@ -84,7 +94,7 @@ func (mc JSONMetricCollector) Collect(ch chan<- prometheus.Metric) { mc.Logger.Error("Failed to marshal data to json", "path", m.ValueJSONPath, "err", err, "metric", m.Desc, "data", data) continue } - value, err := extractValue(mc.Logger, jdata, m.ValueJSONPath, false) + value, err := extractValue(mc.Logger, m.EngineType, jdata, m.ValueJSONPath, false) if err != nil { mc.Logger.Error("Failed to extract value for metric", "path", m.ValueJSONPath, "err", err, "metric", m.Desc) continue @@ -95,7 +105,7 @@ func (mc JSONMetricCollector) Collect(ch chan<- prometheus.Metric) { m.Desc, m.ValueType, floatValue, - extractLabels(mc.Logger, jdata, m.LabelsJSONPaths)..., + extractLabels(mc.Logger, m.EngineType, jdata, m.LabelsJSONPaths)..., ) ch <- timestampMetric(mc.Logger, m, jdata, metric) } else { @@ -104,7 +114,7 @@ func (mc JSONMetricCollector) Collect(ch chan<- prometheus.Metric) { } } } else { - mc.Logger.Error("Failed to convert extracted objects to json", "err", err, "metric", m.Desc) + mc.Logger.Error("Failed to convert extracted objects to json", "value", values, "err", err, "metric", m.Desc) continue } default: @@ -114,8 +124,19 @@ func (mc JSONMetricCollector) Collect(ch chan<- prometheus.Metric) { } } +func extractValue(logger *slog.Logger, engine config.EngineType, data []byte, path string, enableJSONOutput bool) (string, error) { + switch engine { + case config.EngineTypeJSONPath: + return extractValueJSONPath(logger, data, path, enableJSONOutput) + case config.EngineTypeCEL: + return extractValueCEL(logger, data, path, enableJSONOutput) + default: + return "", fmt.Errorf("Unknown engine type: %s", engine) + } +} + // Returns the last matching value at the given json path -func extractValue(logger *slog.Logger, data []byte, path string, enableJSONOutput bool) (string, error) { +func extractValueJSONPath(logger *slog.Logger, data []byte, path string, enableJSONOutput bool) (string, error) { var jsonData interface{} buf := new(bytes.Buffer) @@ -147,11 +168,70 @@ func extractValue(logger *slog.Logger, data []byte, path string, enableJSONOutpu return buf.String(), nil } +// Returns the last matching value at the given json path +func extractValueCEL(logger *slog.Logger, data []byte, expression string, enableJSONOutput bool) (string, error) { + + var jsonData map[string]any + + err := json.Unmarshal(data, &jsonData) + if err != nil { + logger.Error("Failed to unmarshal data to json", "err", err, "data", data) + return "", err + } + + inputVars := make([]cel.EnvOption, 0, len(jsonData)) + for k := range jsonData { + inputVars = append(inputVars, cel.Variable(k, cel.DynType)) + } + + env, err := cel.NewEnv(inputVars...) + + if err != nil { + logger.Error("Failed to set up CEL environment", "err", err, "data", data) + return "", err + } + + ast, issues := env.Compile(expression) + if issues != nil && issues.Err() != nil { + logger.Error("CEL type-check error", "err", issues.String(), "expression", expression) + return "", err + } + prg, err := env.Program(ast) + if err != nil { + logger.Error("CEL program construction error", "err", err) + return "", err + } + + out, _, err := prg.Eval(jsonData) + if err != nil { + logger.Error("Failed to evaluate cel query", "err", err, "expression", expression, "data", jsonData) + return "", err + } + + // Since we are finally going to extract only float64, unquote if necessary + + //res, err := jsonpath.UnquoteExtend(fmt.Sprintf("%g", out)) + //if err == nil { + // level.Error(logger).Log("msg","Triggered") + // return res, nil + //} + logger.Error("Triggered later", "val", out) + if enableJSONOutput { + res, err := valueToJSON(out) + if err != nil { + return "", err + } + return res, nil + } + + return fmt.Sprintf("%v", out), nil +} + // Returns the list of labels created from the list of provided json paths -func extractLabels(logger *slog.Logger, data []byte, paths []string) []string { +func extractLabels(logger *slog.Logger, engine config.EngineType, data []byte, paths []string) []string { labels := make([]string, len(paths)) for i, path := range paths { - if result, err := extractValue(logger, data, path, false); err == nil { + if result, err := extractValue(logger, engine, data, path, false); err == nil { labels[i] = result } else { logger.Error("Failed to extract label value", "err", err, "path", path, "data", data) @@ -164,7 +244,7 @@ func timestampMetric(logger *slog.Logger, m JSONMetric, data []byte, pm promethe if m.EpochTimestampJSONPath == "" { return pm } - ts, err := extractValue(logger, data, m.EpochTimestampJSONPath, false) + ts, err := extractValue(logger, m.EngineType, data, m.EpochTimestampJSONPath, false) if err != nil { logger.Error("Failed to extract timestamp for metric", "path", m.KeyJSONPath, "err", err, "metric", m.Desc) return pm @@ -177,3 +257,18 @@ func timestampMetric(logger *slog.Logger, m JSONMetric, data []byte, pm promethe timestamp := time.UnixMilli(epochTime) return prometheus.NewMetricWithTimestamp(timestamp, pm) } + +// valueToJSON converts the CEL type to a protobuf JSON representation and +// marshals the result to a string. +func valueToJSON(val ref.Val) (string, error) { + v, err := val.ConvertToNative(reflect.TypeOf(&structpb.Value{})) + if err != nil { + return "", err + } + marshaller := protojson.MarshalOptions{Indent: " "} + bytes, err := marshaller.Marshal(v.(proto.Message)) + if err != nil { + return "", err + } + return string(bytes), err +} diff --git a/exporter/util.go b/exporter/util.go index 6fbecb3d..950620f5 100644 --- a/exporter/util.go +++ b/exporter/util.go @@ -102,6 +102,7 @@ func CreateMetricsList(c config.Module) ([]JSONMetric, error) { variableLabels, nil, ), + EngineType: metric.Engine, KeyJSONPath: metric.Path, LabelsJSONPaths: variableLabelsValues, ValueType: valueType, @@ -124,6 +125,7 @@ func CreateMetricsList(c config.Module) ([]JSONMetric, error) { variableLabels, nil, ), + EngineType: metric.Engine, KeyJSONPath: metric.Path, ValueJSONPath: valuePath, LabelsJSONPaths: variableLabelsValues, diff --git a/go.mod b/go.mod index 4a9ebaf5..1e3c762b 100644 --- a/go.mod +++ b/go.mod @@ -5,18 +5,22 @@ go 1.24.0 require ( github.com/Masterminds/sprig/v3 v3.3.0 github.com/alecthomas/kingpin/v2 v2.4.0 + github.com/google/cel-go v0.26.1 github.com/prometheus/client_golang v1.23.2 github.com/prometheus/common v0.67.3 github.com/prometheus/exporter-toolkit v0.15.0 + google.golang.org/protobuf v1.36.10 gopkg.in/yaml.v2 v2.4.0 k8s.io/client-go v0.34.2 ) require ( + cel.dev/expr v0.24.0 // indirect dario.cat/mergo v1.0.1 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver/v3 v3.3.0 // indirect github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b // indirect + github.com/antlr4-go/antlr/v4 v4.13.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/coreos/go-systemd/v22 v22.6.0 // indirect @@ -34,14 +38,17 @@ require ( github.com/prometheus/procfs v0.16.1 // indirect github.com/shopspring/decimal v1.4.0 // indirect github.com/spf13/cast v1.7.0 // indirect + github.com/stoewer/go-strcase v1.2.0 // indirect github.com/xhit/go-str2duration/v2 v2.1.0 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect - golang.org/x/crypto v0.45.0 // indirect - golang.org/x/net v0.47.0 // indirect + golang.org/x/crypto v0.43.0 // indirect + golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc // indirect + golang.org/x/net v0.46.0 // indirect golang.org/x/oauth2 v0.32.0 // indirect - golang.org/x/sync v0.18.0 // indirect - golang.org/x/sys v0.38.0 // indirect - golang.org/x/text v0.31.0 // indirect + golang.org/x/sync v0.17.0 // indirect + golang.org/x/sys v0.37.0 // indirect + golang.org/x/text v0.30.0 // indirect golang.org/x/time v0.13.0 // indirect - google.golang.org/protobuf v1.36.10 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7 // indirect ) diff --git a/go.sum b/go.sum index 888fc672..5af5defb 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY= +cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw= dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= @@ -10,6 +12,8 @@ github.com/alecthomas/kingpin/v2 v2.4.0 h1:f48lwail6p8zpO1bC4TxtqACaGqHYA22qkHjH github.com/alecthomas/kingpin/v2 v2.4.0/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE= github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b h1:mimo19zliBX/vSQ6PWWSL9lK8qwHozUj03+zLoEB8O0= github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b/go.mod h1:fvzegU4vN3H1qMT+8wDmzjAcDONcgo2/SZ/TyfdUOFs= +github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= +github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= 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/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= @@ -23,6 +27,8 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= 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/google/cel-go v0.26.1 h1:iPbVVEdkhTX++hpe3lzSk7D3G3QSYqLGoHOcEio+UXQ= +github.com/google/cel-go v0.26.1/go.mod h1:A9O8OU9rdvrK5MQyrqfIxo1a0u4g3sF8KB6PUIaryMM= 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/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= @@ -69,10 +75,13 @@ github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU= +github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 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/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= @@ -85,25 +94,32 @@ go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= 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= -golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= -golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= -golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= -golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= +golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= +golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= +golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc h1:mCRnTeVUjcrhlRmO0VK8a6k6Rrf6TF9htwo2pJVSjIU= +golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= +golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= +golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= 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.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= -golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= -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/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= -golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= +golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= +golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= golang.org/x/time v0.13.0 h1:eUlYslOIt32DgYD6utsuUeHs4d7AsEYLuIAdg7FlYgI= golang.org/x/time v0.13.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= +google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7 h1:YcyjlL1PRr2Q17/I0dPk2JmYS5CDXfcdb2Z3YRioEbw= +google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7/go.mod h1:OCdP9MfskevB/rbYvHTsXTtKC+3bHWajPdoKgjcYkfo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7 h1:2035KHhUv+EpyB+hWgJnaWKJOdX1E95w2S8Rr4uWKTs= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= 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-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v2 v2.2.2/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=