diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index de28b9b..03ac9a8 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -69,7 +69,7 @@ jobs: - name: Run metrics unit tests with coverage run: | mkdir ./tests/coverage-ci - go test -timeout 20m -v -race -cover -tags=debug -failfast -coverpkg=$(cat ./tests/pkgs.txt) -coverprofile=./tests/coverage-ci/metrics_u.out -covermode=atomic config.go config_test.go plugin.go rpc.go rpc_test.go + go test -timeout 20m -v -race -cover -tags=debug -failfast -coverpkg=$(cat ./tests/pkgs.txt) -coverprofile=./tests/coverage-ci/metrics_u.out -covermode=atomic config.go config_test.go plugin.go rpc.go - name: Run metrics e2e tests with coverage run: | diff --git a/go.mod b/go.mod index d6acda0..507623d 100644 --- a/go.mod +++ b/go.mod @@ -5,11 +5,12 @@ go 1.26 toolchain go1.26.0 require ( + connectrpc.com/connect v1.19.2 github.com/prometheus/client_golang v1.23.2 + github.com/roadrunner-server/api-go/v6 v6.0.0-beta.9 github.com/roadrunner-server/endure/v2 v2.6.2 github.com/roadrunner-server/errors v1.5.0 github.com/stretchr/testify v1.11.1 - github.com/vmihailenco/msgpack/v5 v5.4.1 golang.org/x/sys v0.44.0 ) @@ -24,8 +25,11 @@ require ( github.com/prometheus/common v0.67.5 // indirect github.com/prometheus/procfs v0.20.1 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect - github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect + golang.org/x/net v0.54.0 // indirect + golang.org/x/text v0.37.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260511170946-3700d4141b60 // indirect + google.golang.org/grpc v1.81.0 // indirect google.golang.org/protobuf v1.36.11 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 254bda4..d35f934 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +connectrpc.com/connect v1.19.2 h1:McQ83FGdzL+t60peksi0gXC7MQ/iLKgLduAnThbM0mo= +connectrpc.com/connect v1.19.2/go.mod h1:tN20fjdGlewnSFeZxLKb0xwIZ6ozc3OQs2hTXy4du9w= 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= @@ -5,8 +7,16 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= 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= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 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/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -27,6 +37,8 @@ github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTU github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw= github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc= github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= +github.com/roadrunner-server/api-go/v6 v6.0.0-beta.9 h1:iD09+1Sc6ZMxEOJsj7YIblzEN1ORZ+NLH7YYO0nqASs= +github.com/roadrunner-server/api-go/v6 v6.0.0-beta.9/go.mod h1:4BbPXAqT0sOG2EVRcymESMwxaKSpHl4hG+UDoCepWu0= github.com/roadrunner-server/endure/v2 v2.6.2 h1:sIB4kTyE7gtT3fDhuYWUYn6Vt/dcPtiA6FoNS1eS+84= github.com/roadrunner-server/endure/v2 v2.6.2/go.mod h1:t/2+xpNYgGBwhzn83y2MDhvhZ19UVq1REcvqn7j7RB8= github.com/roadrunner-server/errors v1.5.0 h1:unG7LKIZrSzkCCF3YLRLA5VyqE0KKomofXVJUXJe00g= @@ -35,16 +47,34 @@ github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0t github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= 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/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= -github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= -github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= -github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= +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/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= +go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= +go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= +go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= +go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= +go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg= +go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw= +go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= +go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= +go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= 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.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= +golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w= +golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ= golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ= golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= +golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= +gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= +gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260511170946-3700d4141b60 h1:seT2EwLWM78plQ7wcDfuWBc/4FAEAXDDiaSol4ku4qo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260511170946-3700d4141b60/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/grpc v1.81.0 h1:W3G9N3KQf3BU+YuCtGKJk0CmxQNbAISICD/9AORxLIw= +google.golang.org/grpc v1.81.0/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/go.work.sum b/go.work.sum index 4326aa1..0fefaae 100644 --- a/go.work.sum +++ b/go.work.sum @@ -1,3 +1,4 @@ +cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys= cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= cloud.google.com/go v0.110.7 h1:rJyC7nWRg2jWGZ4wSJ5nY65GTdYJkg0cd/uXb+ACI6o= @@ -7,55 +8,80 @@ cloud.google.com/go v0.112.1 h1:uJSeirPke5UNZHIb4SxfZklVSiWWVqW4oXlETwZziwM= cloud.google.com/go v0.112.1/go.mod h1:+Vbu+Y1UU+I1rjmzeMOb/8RfkKJK2Gyxi1X6jJCZLo4= cloud.google.com/go/accessapproval v1.8.6/go.mod h1:FfmTs7Emex5UvfnnpMkhuNkRCP85URnBFt5ClLxhZaQ= cloud.google.com/go/accessapproval v1.8.8/go.mod h1:RFwPY9JDKseP4gJrX1BlAVsP5O6kI8NdGlTmaeDefmk= +cloud.google.com/go/accessapproval v1.13.0/go.mod h1:7bmInw17bQX+ZPi7YmReC3xKymDrMmxXaUnaI6zQOqI= cloud.google.com/go/accesscontextmanager v1.9.6/go.mod h1:884XHwy1AQpCX5Cj2VqYse77gfLaq9f8emE2bYriilk= cloud.google.com/go/accesscontextmanager v1.9.7/go.mod h1:i6e0nd5CPcrh7+YwGq4bKvju5YB9sgoAip+mXU73aMM= +cloud.google.com/go/accesscontextmanager v1.14.0/go.mod h1:VO15iVnsM0FO9Dt8hSFPgkuHRZjq6LEYZq1szJ27U2k= cloud.google.com/go/aiplatform v1.86.0/go.mod h1:xp3wFix8imliXkVpgMRkjnreJYTaNzLF44GOrnIENto= cloud.google.com/go/aiplatform v1.114.0/go.mod h1:W5yMrpIuHG/CSK8iF7XnwIfCJu6dcLRQ0cTqGR5vwwE= +cloud.google.com/go/aiplatform v1.125.0/go.mod h1:yWTZiCunYDnyxeWWD14tDo6+BMlvAUCC5VxuxhvbrVI= cloud.google.com/go/analytics v0.28.1/go.mod h1:iPaIVr5iXPB3JzkKPW1JddswksACRFl3NSHgVHsuYC4= cloud.google.com/go/analytics v0.30.1/go.mod h1:V/FnINU5kMOsttZnKPnXfKi6clJUHTEXUKQjHxcNK8A= +cloud.google.com/go/analytics v0.35.0/go.mod h1:V9Qef2N0y8GDqQ9FTlmM2XpDEMYonZJRPSUNGZlPCcc= cloud.google.com/go/apigateway v1.7.6/go.mod h1:SiBx36VPjShaOCk8Emf63M2t2c1yF+I7mYZaId7OHiA= cloud.google.com/go/apigateway v1.7.7/go.mod h1:j1bCmrUK1BzVHpiIyTApxB7cRyhivKzltqLmp6j6i7U= +cloud.google.com/go/apigateway v1.12.0/go.mod h1:f3Sk8Tdh1Ty5HR7kgbWB6Yu1M82LM+nIr5DTMZnLZWk= cloud.google.com/go/apigeeconnect v1.7.6/go.mod h1:zqDhHY99YSn2li6OeEjFpAlhXYnXKl6DFb/fGu0ye2w= cloud.google.com/go/apigeeconnect v1.7.7/go.mod h1:ftGK3nca0JePiVLl0A6alaMjKdOc5C+sAkFMyH2RH8U= +cloud.google.com/go/apigeeconnect v1.12.0/go.mod h1:mYJekCKZHc2ia5yZX5lwtexTn9CzsOfb6+sh/2hi42Q= cloud.google.com/go/apigeeregistry v0.9.6/go.mod h1:AFEepJBKPtGDfgabG2HWaLH453VVWWFFs3P4W00jbPs= cloud.google.com/go/apigeeregistry v0.10.0/go.mod h1:SAlF5OhKvyLDuwWAaFAIVJjrEqKRrGTPkJs+TWNnSqg= +cloud.google.com/go/apigeeregistry v0.15.0/go.mod h1:o+j6eA8hYhTWX5gEqMMBVDWY+/QQFrYe/YJBsO19pn0= cloud.google.com/go/appengine v1.9.6/go.mod h1:jPp9T7Opvzl97qytaRGPwoH7pFI3GAcLDaui1K8PNjY= cloud.google.com/go/appengine v1.9.7/go.mod h1:y1XpGVeAhbsNzHida79cHbr3pFRsym0ob8xnC8yphbo= +cloud.google.com/go/appengine v1.14.0/go.mod h1:JMjrVFg+YgfksZCWbtA3TgbKbPfZZtapB9cGL/5WVnM= cloud.google.com/go/area120 v0.9.6/go.mod h1:qKSokqe0iTmwBDA3tbLWonMEnh0pMAH4YxiceiHUed4= cloud.google.com/go/area120 v0.9.7/go.mod h1:5nJ0yksmjOMfc4Zpk+okWfJ3A1004FvB82rfia+ZLaY= +cloud.google.com/go/area120 v0.15.0/go.mod h1:jD1fw9W4xxIZMY68g7PpbCPleoeGddFs5jPcdhfg3+Y= cloud.google.com/go/artifactregistry v1.17.1/go.mod h1:06gLv5QwQPWtaudI2fWO37gfwwRUHwxm3gA8Fe568Hc= cloud.google.com/go/artifactregistry v1.19.0/go.mod h1:UEAPCgHDFC1q+A8nnVxXHPEy9KCVOeavFBF1fEChQvU= +cloud.google.com/go/artifactregistry v1.25.0/go.mod h1:aMmdtqKVmbuxCCb/NGDJYZHsK6AtqlcyvD05ACzs1n8= cloud.google.com/go/asset v1.21.0/go.mod h1:0lMJ0STdyImZDSCB8B3i/+lzIquLBpJ9KZ4pyRvzccM= cloud.google.com/go/asset v1.22.0/go.mod h1:q80JP2TeWWzMCazYnrAfDf36aQKf1QiKzzpNLflJwf8= +cloud.google.com/go/asset v1.27.0/go.mod h1:+HaDReZQAh/0syAf0uTMeUrMfXikr+KKyDtCdvf7j4M= cloud.google.com/go/assuredworkloads v1.12.6/go.mod h1:QyZHd7nH08fmZ+G4ElihV1zoZ7H0FQCpgS0YWtwjCKo= cloud.google.com/go/assuredworkloads v1.13.0/go.mod h1:o/oHEOnUlribR+uJWTKQo8A5RhSl9K9FNeMOew4TJ3M= +cloud.google.com/go/assuredworkloads v1.18.0/go.mod h1:zBnVYn0E+sDW/mhEmcg1R8+8tguXrtBgmfGY0q34kss= cloud.google.com/go/automl v1.14.7/go.mod h1:8a4XbIH5pdvrReOU72oB+H3pOw2JBxo9XTk39oljObE= cloud.google.com/go/automl v1.15.0/go.mod h1:U9zOtQb8zVrFNGTuW3BfxeqmLyeleLgT9B12EaXfODg= +cloud.google.com/go/automl v1.20.0/go.mod h1:OkHxjbVDblDafhwuP8yEkz1xcUJhgcbhbsieCW7GaiI= cloud.google.com/go/baremetalsolution v1.3.6/go.mod h1:7/CS0LzpLccRGO0HL3q2Rofxas2JwjREKut414sE9iM= cloud.google.com/go/baremetalsolution v1.4.0/go.mod h1:K6C6g4aS8LW95I0fEHZiBsBlh0UxwDLGf+S/vyfXbvg= +cloud.google.com/go/baremetalsolution v1.9.0/go.mod h1:o+stutiS8t+HmjNIG92Gkn8H9+5/q27d6lQp7e9GWdg= cloud.google.com/go/batch v1.12.2/go.mod h1:tbnuTN/Iw59/n1yjAYKV2aZUjvMM2VJqAgvUgft6UEU= cloud.google.com/go/batch v1.14.0/go.mod h1:oeQveyG6NDS/ks2ilOP4LzKRmuIaI7GLe0CkR7WF6pk= +cloud.google.com/go/batch v1.19.0/go.mod h1:dpWfhLmLQZqsTBAFYjZA3pS04fCY5ttTenZcWmSeILw= cloud.google.com/go/beyondcorp v1.1.6/go.mod h1:V1PigSWPGh5L/vRRmyutfnjAbkxLI2aWqJDdxKbwvsQ= cloud.google.com/go/beyondcorp v1.2.0/go.mod h1:sszcgxpPPBEfLzbI0aYCTg6tT1tyt3CmKav3NZIUcvI= +cloud.google.com/go/beyondcorp v1.7.0/go.mod h1:vujdO0wfsBV2y1egrJxGtwKZr5P5V6bIHKWp1phWHBY= cloud.google.com/go/bigquery v1.8.0 h1:PQcPefKFdaIzjQFbiyOgAqyx8q5djaE7x9Sqe712DPA= cloud.google.com/go/bigquery v1.67.0/go.mod h1:HQeP1AHFuAz0Y55heDSb0cjZIhnEkuwFRBGo6EEKHug= cloud.google.com/go/bigquery v1.72.0/go.mod h1:GUbRtmeCckOE85endLherHD9RsujY+gS7i++c1CqssQ= +cloud.google.com/go/bigquery v1.77.0/go.mod h1:J4wuqka/1hEpdJxH2oBrUR0vjTD+r7drGkpcA3yqERM= cloud.google.com/go/bigtable v1.37.0/go.mod h1:HXqddP6hduwzrtiTCqZPpj9ij4hGZb4Zy1WF/dT+yaU= cloud.google.com/go/bigtable v1.41.0/go.mod h1:JlaltP06LEFXaxQdZiarGR9tKsX/II0IkNAKMDrWspI= +cloud.google.com/go/bigtable v1.47.0/go.mod h1:GUM6PdkG3rrDse9kugqvX5+ktwo3ldfLtLi1VFn5Wj4= cloud.google.com/go/billing v1.20.4/go.mod h1:hBm7iUmGKGCnBm6Wp439YgEdt+OnefEq/Ib9SlJYxIU= cloud.google.com/go/billing v1.21.0/go.mod h1:ZGairB3EVnb3i09E2SxFxo50p5unPaMTuo1jh6jW9js= +cloud.google.com/go/billing v1.26.0/go.mod h1:axqDO1uHegh7u5qngkTfqN1djAeLGsWAFAblERgmgEk= cloud.google.com/go/binaryauthorization v1.9.5/go.mod h1:CV5GkS2eiY461Bzv+OH3r5/AsuB6zny+MruRju3ccB8= cloud.google.com/go/binaryauthorization v1.10.0/go.mod h1:WOuiaQkI4PU/okwrcREjSAr2AUtjQgVe+PlrXKOmKKw= +cloud.google.com/go/binaryauthorization v1.15.0/go.mod h1:+0CndCJPtcHuVCNok+qQskWvbP5Sp5m6eGL8Vpu5mss= cloud.google.com/go/certificatemanager v1.9.5/go.mod h1:kn7gxT/80oVGhjL8rurMUYD36AOimgtzSBPadtAeffs= cloud.google.com/go/certificatemanager v1.9.6/go.mod h1:vWogV874jKZkSRDFCMM3r7wqybv8WXs3XhyNff6o/Zo= +cloud.google.com/go/certificatemanager v1.14.0/go.mod h1:QOA8qRoM6/Ik03+srLnBykenGTy0fk78dnPcx5ZWOW8= cloud.google.com/go/channel v1.19.5/go.mod h1:vevu+LK8Oy1Yuf7lcpDbkQQQm5I7oiY5fFTn3uwfQLY= cloud.google.com/go/channel v1.21.0/go.mod h1:8v3TwHtgLmFxTpL2U+e10CLFOQN8u/Vr9RhYcJUS3y8= +cloud.google.com/go/channel v1.26.0/go.mod h1:04T5Wjq+mHlvEUNzExydnBW1vO64q3Q2Wsblp/dpBxY= cloud.google.com/go/cloudbuild v1.22.2/go.mod h1:rPyXfINSgMqMZvuTk1DbZcbKYtvbYF/i9IXQ7eeEMIM= cloud.google.com/go/cloudbuild v1.25.0/go.mod h1:lCu+T6IPkobPo2Nw+vCE7wuaAl9HbXLzdPx/tcF+oWo= +cloud.google.com/go/cloudbuild v1.30.0/go.mod h1:rg52xEmndQQPiC9NV/8sCaVtKxHMU9D9MeU+oE9VGKA= cloud.google.com/go/clouddms v1.8.7/go.mod h1:DhWLd3nzHP8GoHkA6hOhso0R9Iou+IGggNqlVaq/KZ4= cloud.google.com/go/clouddms v1.8.8/go.mod h1:QtCyw+a73dlkDb2q20aTAPvfaTZCepDDi6Gb1AKq0a4= +cloud.google.com/go/clouddms v1.13.0/go.mod h1:aMgrOZ+/EKF/PL+h1sDbS+7fAIYV5rTwD+G/apCeHQk= cloud.google.com/go/cloudtasks v1.13.6/go.mod h1:/IDaQqGKMixD+ayM43CfsvWF2k36GeomEuy9gL4gLmU= cloud.google.com/go/cloudtasks v1.13.7/go.mod h1:H0TThOUG+Ml34e2+ZtW6k6nt4i9KuH3nYAJ5mxh7OM4= +cloud.google.com/go/cloudtasks v1.18.0/go.mod h1:3KeCxwtGEyaySL7CR3lMmEa2I4mq1ynXdgmfNiO4RYE= cloud.google.com/go/compute v1.19.0 h1:+9zda3WGgW1ZSTlVppLCYFIr48Pa35q1uG2N1itbCEQ= cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU= cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY= @@ -65,57 +91,82 @@ cloud.google.com/go/compute v1.24.0 h1:phWcR2eWzRJaL/kOiJwfFsPs4BaKq1j6vnpZrc1Yl cloud.google.com/go/compute v1.24.0/go.mod h1:kw1/T+h/+tK2LJK0wiPPx1intgdAM3j/g3hFDlscY40= cloud.google.com/go/compute v1.37.0/go.mod h1:AsK4VqrSyXBo4SMbRtfAO1VfaMjUEjEwv1UB/AwVp5Q= cloud.google.com/go/compute v1.54.0/go.mod h1:RfBj0L1x/pIM84BrzNX2V21oEv16EKRPBiTcBRRH1Ww= +cloud.google.com/go/compute v1.62.0/go.mod h1:Xm6PbsLgBpAg4va77ljbBdpMjzuU+uPp5Ze2dnZq7lw= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= +cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= cloud.google.com/go/contactcenterinsights v1.17.3/go.mod h1:7Uu2CpxS3f6XxhRdlEzYAkrChpR5P5QfcdGAFEdHOG8= cloud.google.com/go/contactcenterinsights v1.17.4/go.mod h1:kZe6yOnKDfpPz2GphDHynxk/Spx+53UX/pGf+SmWAKM= +cloud.google.com/go/contactcenterinsights v1.22.0/go.mod h1:2Crd36H59Lwkt4gWrLgmnbnF59IIZIa3XYt1gtNqJkQ= cloud.google.com/go/container v1.42.4/go.mod h1:wf9lKc3ayWVbbV/IxKIDzT7E+1KQgzkzdxEJpj1pebE= cloud.google.com/go/container v1.46.0/go.mod h1:A7gMqdQduTk46+zssWDTKbGS2z46UsJNXfKqvMI1ZO4= +cloud.google.com/go/container v1.49.0/go.mod h1:EvqoT2eXfxLweXXUlhAMGR0sOAB00XPzEjoL01esSDs= cloud.google.com/go/containeranalysis v0.14.1/go.mod h1:28e+tlZgauWGHmEbnI5UfIsjMmrkoR1tFN0K2i71jBI= cloud.google.com/go/containeranalysis v0.14.2/go.mod h1:FjppROiUtP9cyMegdWdY/TsBSGc6kqh1GjA2NOJXXL8= +cloud.google.com/go/containeranalysis v0.19.0/go.mod h1:Zq0XHzUIa0oTa7H6aSR8HWqeJnoRI9syUcYJzfozjZQ= cloud.google.com/go/datacatalog v1.26.0/go.mod h1:bLN2HLBAwB3kLTFT5ZKLHVPj/weNz6bR0c7nYp0LE14= cloud.google.com/go/datacatalog v1.26.1/go.mod h1:2Qcq8vsHNxMDgjgadRFmFG47Y+uuIVsyEGUrlrKEdrg= +cloud.google.com/go/datacatalog v1.31.0/go.mod h1:MP8V3kNuESnwMk4mB6zdWmw/4KQ5xZ8dyUNVsggqN5I= cloud.google.com/go/dataflow v0.11.0/go.mod h1:gNHC9fUjlV9miu0hd4oQaXibIuVYTQvZhMdPievKsPk= cloud.google.com/go/dataflow v0.11.1/go.mod h1:3s6y/h5Qz7uuxTmKJKBifkYZ3zs63jS+6VGtSu8Cf7Y= +cloud.google.com/go/dataflow v0.16.0/go.mod h1:BWhSrIGmsMfuYj3J+nJ2Tw7tplRR6r28kvRiqCD3WlQ= cloud.google.com/go/dataform v0.11.2/go.mod h1:IMmueJPEKpptT2ZLWlvIYjw6P/mYHHxA7/SUBiXqZUY= cloud.google.com/go/dataform v0.12.1/go.mod h1:atGS8ReRjfNDUQib0X/o/7Gi2bqHI2G7/J86LKiGimE= +cloud.google.com/go/dataform v0.19.0/go.mod h1:i1a0zkS751kvrY1IIPpUQZ77H5doxx7cs0AP3hnXTMk= cloud.google.com/go/datafusion v1.8.6/go.mod h1:fCyKJF2zUKC+O3hc2F9ja5EUCAbT4zcH692z8HiFZFw= cloud.google.com/go/datafusion v1.8.7/go.mod h1:4dkFb1la41qCEXh1AzYtFwl842bu2ikTUXyKhjvFCb0= +cloud.google.com/go/datafusion v1.13.0/go.mod h1:MQdANs3I/4gitzY+mTBx27rrQyMiUg8uc2Z4TPLWWfc= cloud.google.com/go/datalabeling v0.9.6/go.mod h1:n7o4x0vtPensZOoFwFa4UfZgkSZm8Qs0Pg/T3kQjXSM= cloud.google.com/go/datalabeling v0.9.7/go.mod h1:EEUVn+wNn3jl19P2S13FqE1s9LsKzRsPuuMRq2CMsOk= +cloud.google.com/go/datalabeling v0.14.0/go.mod h1:DYjvP4RhQ0332YgO22APYlBjCebb+SCaS0e2KApDq/Q= cloud.google.com/go/dataplex v1.25.2/go.mod h1:AH2/a7eCYvFP58scJGR7YlSY9qEhM8jq5IeOA/32IZ0= cloud.google.com/go/dataplex v1.28.0/go.mod h1:VB+xlYJiJ5kreonXsa2cHPj0A3CfPh/mgiHG4JFhbUA= +cloud.google.com/go/dataplex v1.34.0/go.mod h1:sOazL+Bs/PTxiMHQ5yBboBvEW9qPrpGogx3+RAgfIt8= cloud.google.com/go/dataproc/v2 v2.11.2/go.mod h1:xwukBjtfiO4vMEa1VdqyFLqJmcv7t3lo+PbLDcTEw+g= cloud.google.com/go/dataproc/v2 v2.15.0/go.mod h1:tSdkodShfzrrUNPDVEL6MdH9/mIEvp/Z9s9PBdbsZg8= +cloud.google.com/go/dataproc/v2 v2.21.0/go.mod h1:oARVSa38kAHvSuG+cozsrY2sE6UajGuvOOf9vS+ADHI= cloud.google.com/go/dataqna v0.9.7/go.mod h1:4ac3r7zm7Wqm8NAc8sDIDM0v7Dz7d1e/1Ka1yMFanUM= cloud.google.com/go/dataqna v0.9.8/go.mod h1:2lHKmGPOqzzuqCc5NI0+Xrd5om4ulxGwPpLB4AnFgpA= +cloud.google.com/go/dataqna v0.13.0/go.mod h1:XiVVFTOEJLBSvm3ILbyjXngGQYpjb/66MSksqz/56fs= cloud.google.com/go/datastore v1.1.0 h1:/May9ojXjRkPBNVrq+oWLqmWCkr4OU5uRY29bu0mRyQ= cloud.google.com/go/datastore v1.20.0/go.mod h1:uFo3e+aEpRfHgtp5pp0+6M0o147KoPaYNaPAKpfh8Ew= cloud.google.com/go/datastore v1.21.0/go.mod h1:9l+KyAHO+YVVcdBbNQZJu8svF17Nw5sMKuFR0LYf1nY= +cloud.google.com/go/datastore v1.23.0/go.mod h1:bOvQQekv4VACRJmH/MBy12MT6M3udfTuCyxw+tzY+8s= cloud.google.com/go/datastream v1.14.1/go.mod h1:JqMKXq/e0OMkEgfYe0nP+lDye5G2IhIlmencWxmesMo= cloud.google.com/go/datastream v1.15.1/go.mod h1:aV1Grr9LFon0YvqryE5/gF1XAhcau2uxN2OvQJPpqRw= +cloud.google.com/go/datastream v1.20.0/go.mod h1:uoWTtfP20W8MXuV2DPcl5zqnVsxQ9QEmmBHX858oYTQ= cloud.google.com/go/deploy v1.27.1/go.mod h1:il2gxiMgV3AMlySoQYe54/xpgVDoEh185nj4XjJ+GRk= cloud.google.com/go/deploy v1.27.3/go.mod h1:7LFIYYTSSdljYRqY3n+JSmIFdD4lv6aMD5xg0crB5iw= +cloud.google.com/go/deploy v1.32.0/go.mod h1:lUG7maG/NkoTXmQ8G1mtcVymnbizfDJh6ER7vljVa/U= cloud.google.com/go/dialogflow v1.68.2/go.mod h1:E0Ocrhf5/nANZzBju8RX8rONf0PuIvz2fVj3XkbAhiY= cloud.google.com/go/dialogflow v1.74.0/go.mod h1:jlKHmd3/KdvWWhGZjoCnWQAQNOMHOhDK6DQ430p3T1I= +cloud.google.com/go/dialogflow v1.82.0/go.mod h1:UtuiGOq9gAlTz9u4Vt+q1syMrx9ANQzTk+lC3WDdSOw= cloud.google.com/go/dlp v1.22.1/go.mod h1:Gc7tGo1UJJTBRt4OvNQhm8XEQ0i9VidAiGXBVtsftjM= cloud.google.com/go/dlp v1.28.0/go.mod h1:C3od1fIK8lf7Kr62aU1Uh0z4OL5Z8s3do3znAiEupAw= +cloud.google.com/go/dlp v1.34.0/go.mod h1:+haQd/n0QTv5BK7wZnCk2qctd5sfKL50jjh9E6N0d/Q= cloud.google.com/go/documentai v1.37.0/go.mod h1:qAf3ewuIUJgvSHQmmUWvM3Ogsr5A16U2WPHmiJldvLA= cloud.google.com/go/documentai v1.40.0/go.mod h1:oDTm0aoG8ldKucW/yzRrLbaTO0NvtgGAWm5KPAT5iNY= +cloud.google.com/go/documentai v1.48.0/go.mod h1:mGjfbNf0cqCHKgxMZZV7frbfoF9T2hKkU1h88QyOy3c= cloud.google.com/go/domains v0.10.6/go.mod h1:3xzG+hASKsVBA8dOPc4cIaoV3OdBHl1qgUpAvXK7pGY= cloud.google.com/go/domains v0.10.7/go.mod h1:T3WG/QUAO/52z4tUPooKS8AY7yXaFxPYn1V3F0/JbNQ= +cloud.google.com/go/domains v0.15.0/go.mod h1:BjoSVNc+LVwoHMnE2fxTQNzGLSWWb6f3a8VAN6+VjVk= cloud.google.com/go/edgecontainer v1.4.3/go.mod h1:q9Ojw2ox0uhAvFisnfPRAXFTB1nfRIOIXVWzdXMZLcE= cloud.google.com/go/edgecontainer v1.4.4/go.mod h1:yyNVHsCKtsX/0mqFdbljQw0Uo660q2dlMPaiqYiC2Tg= +cloud.google.com/go/edgecontainer v1.9.0/go.mod h1:mZmgXuMGTGI6RUUTXsOZa+F2rFF21v0JPnuX7LQEqBE= cloud.google.com/go/errorreporting v0.3.2/go.mod h1:s5kjs5r3l6A8UUyIsgvAhGq6tkqyBCUss0FRpsoVTww= cloud.google.com/go/errorreporting v0.4.0/go.mod h1:dZGEhqzdHZSRxxWLVjC3Ue5CVaROzvP58D9rU6zbBfw= +cloud.google.com/go/errorreporting v0.9.0/go.mod h1:V7ojx7z76JITDZNGyDNkIIa9nNEkQzF6Yj+VHl2YF84= cloud.google.com/go/essentialcontacts v1.7.6/go.mod h1:/Ycn2egr4+XfmAfxpLYsJeJlVf9MVnq9V7OMQr9R4lA= cloud.google.com/go/essentialcontacts v1.7.7/go.mod h1:ytycWAEn/aKUMRKQPMVgMrAtphEMgjbzL8vFwM3tqXs= +cloud.google.com/go/essentialcontacts v1.12.0/go.mod h1:W8fTL17jP6vmsPHQaCT5rOjWGohEssuqDUroxnjST0A= cloud.google.com/go/eventarc v1.15.5/go.mod h1:vDCqGqyY7SRiickhEGt1Zhuj81Ya4F/NtwwL3OZNskg= cloud.google.com/go/eventarc v1.18.0/go.mod h1:/6SDoqh5+9QNUqCX4/oQcJVK16fG/snHBSXu7lrJtO8= +cloud.google.com/go/eventarc v1.23.0/go.mod h1:tIJL0hoWtZXVa5MjcAep/4xB+AXz4AbqQV14ogX5VwU= cloud.google.com/go/filestore v1.10.2/go.mod h1:w0Pr8uQeSRQfCPRsL0sYKW6NKyooRgixCkV9yyLykR4= cloud.google.com/go/filestore v1.10.3/go.mod h1:94ZGyLTx9j+aWKozPQ6Wbq1DuImie/L/HIdGMshtwac= +cloud.google.com/go/filestore v1.15.0/go.mod h1:oD+PvCWu4HqfEdNv65yk2XaLIiP7h4AuAH9Ua5YBRTM= cloud.google.com/go/firestore v1.9.0 h1:IBlRyxgGySXu5VuW0RgGFlTtLukSnNkpDiEOMkQkmpA= cloud.google.com/go/firestore v1.9.0/go.mod h1:HMkjKHNTtRyZNiMzu7YAsLr9K3X2udY2AMwDaMEQiiE= cloud.google.com/go/firestore v1.13.0 h1:/3S4RssUV4GO/kvgJZB+tayjhOfyAHs+KcpJgRVu/Qk= @@ -125,36 +176,51 @@ cloud.google.com/go/firestore v1.15.0 h1:/k8ppuWOtNuDHt2tsRV42yI21uaGnKDEQnRFeBp cloud.google.com/go/firestore v1.15.0/go.mod h1:GWOxFXcv8GZUtYpWHw/w6IuYNux/BtmeVTMmjrm4yhk= cloud.google.com/go/firestore v1.18.0/go.mod h1:5ye0v48PhseZBdcl0qbl3uttu7FIEwEYVaWm0UIEOEU= cloud.google.com/go/firestore v1.21.0/go.mod h1:1xH6HNcnkf/gGyR8udd6pFO4Z7GWJSwLKQMx/u6UrP4= +cloud.google.com/go/firestore v1.22.0/go.mod h1:PaM4i7i7ruALSKmlpHXXZaPObcZw0W7ie5UOPr72iTU= cloud.google.com/go/functions v1.19.6/go.mod h1:0G0RnIlbM4MJEycfbPZlCzSf2lPOjL7toLDwl+r0ZBw= cloud.google.com/go/functions v1.19.7/go.mod h1:xbcKfS7GoIcaXr2FSwmtn9NXal1JR4TV6iYZlgXffwA= +cloud.google.com/go/functions v1.24.0/go.mod h1:t40GeqBAQNuqKlHCxmV/pxhyYJnImLcvRa3GBv4tAy0= cloud.google.com/go/gkebackup v1.7.0/go.mod h1:oPHXUc6X6tg6Zf/7QmKOfXOFaVzBEgMWpLDb4LqngWA= cloud.google.com/go/gkebackup v1.8.1/go.mod h1:GAaAl+O5D9uISH5MnClUop2esQW4pDa2qe/95A4l7YQ= +cloud.google.com/go/gkebackup v1.13.0/go.mod h1:D2MDbHW4V/uKCmS9TnT8hNKX2tPkE/pWp9nSm0TQ9hY= cloud.google.com/go/gkeconnect v0.12.4/go.mod h1:bvpU9EbBpZnXGo3nqJ1pzbHWIfA9fYqgBMJ1VjxaZdk= cloud.google.com/go/gkeconnect v0.12.5/go.mod h1:wMD2RXcsAWlkREZWJDVeDV70PYka1iEb9stFmgpw+5o= +cloud.google.com/go/gkeconnect v0.16.0/go.mod h1:5iWSBQzMIRLwUHUWVhxxcNK45ZPE8ntyBgE0MkavlqQ= cloud.google.com/go/gkehub v0.15.6/go.mod h1:sRT0cOPAgI1jUJrS3gzwdYCJ1NEzVVwmnMKEwrS2QaM= cloud.google.com/go/gkehub v0.16.0/go.mod h1:ADp27Ucor8v81wY+x/5pOxTorxkPj/xswH3AUpN62GU= +cloud.google.com/go/gkehub v0.21.0/go.mod h1:xKePlMrI8LpKErzKMWdH/yQv+GDV60ypCNfTTdT+BN0= cloud.google.com/go/gkemulticloud v1.5.3/go.mod h1:KPFf+/RcfvmuScqwS9/2MF5exZAmXSuoSLPuaQ98Xlk= cloud.google.com/go/gkemulticloud v1.6.0/go.mod h1:bGpd4o/Z5Z/XFlaojkgdVisHRwb+fLJvUPzsmV0I9ok= +cloud.google.com/go/gkemulticloud v1.11.0/go.mod h1:OtfHtgqOgDrXfcdFw8eUkCUI154Q51vvdqZYZV4c4qM= cloud.google.com/go/gsuiteaddons v1.7.7/go.mod h1:zTGmmKG/GEBCONsvMOY2ckDiEsq3FN+lzWGUiXccF9o= cloud.google.com/go/gsuiteaddons v1.7.8/go.mod h1:DBKNHH4YXAdd/rd6zVvtOGAJNGo0ekOh+nIjTUDEJ5U= +cloud.google.com/go/gsuiteaddons v1.12.0/go.mod h1:rm/XT7wmwOFGn7jmWtVV65QmZCakzTbHLSojIC4Hskg= cloud.google.com/go/iam v1.1.5 h1:1jTsCu4bcsNsE4iiqNT5SHwrDRCfRmIaaaVFhRveTJI= cloud.google.com/go/iam v1.1.5/go.mod h1:rB6P/Ic3mykPbFio+vo7403drjlgvoWfYpJhMXEbzv8= cloud.google.com/go/iam v1.5.2/go.mod h1:SE1vg0N81zQqLzQEwxL2WI6yhetBdbNQuTvIKCSkUHE= cloud.google.com/go/iam v1.5.3/go.mod h1:MR3v9oLkZCTlaqljW6Eb2d3HGDGK5/bDv93jhfISFvU= +cloud.google.com/go/iam v1.11.0/go.mod h1:KP+nKGugNJW4LcLx1uEZcq1ok5sQHFaQehQNl4QDgV4= cloud.google.com/go/iap v1.11.1/go.mod h1:qFipMJ4nOIv4yDHZxn31PiS8QxJJH2FlxgH9aFauejw= cloud.google.com/go/iap v1.11.3/go.mod h1:+gXO0ClH62k2LVlfhHzrpiHQNyINlEVmGAE3+DB4ShU= +cloud.google.com/go/iap v1.17.0/go.mod h1:b+r+yjrss2WmAEzNrQQjlEdD5E9B8c47mOF7XnqT+z0= cloud.google.com/go/ids v1.5.6/go.mod h1:y3SGLmEf9KiwKsH7OHvYYVNIJAtXybqsD2z8gppsziQ= cloud.google.com/go/ids v1.5.7/go.mod h1:N3ZQOIgIBwwOu2tzyhmh3JDT+kt8PcoKkn2BRT9Qe4A= +cloud.google.com/go/ids v1.10.0/go.mod h1:uCSFrXfCnRUKBl5PdE/ZqBNp1+vKSKPWpdYGa61WjpQ= cloud.google.com/go/iot v1.8.6/go.mod h1:MThnkiihNkMysWNeNje2Hp0GSOpEq2Wkb/DkBCVYa0U= cloud.google.com/go/iot v1.8.7/go.mod h1:HvVcypV8LPv1yTXSLCNK+YCtqGHhq+p0F3BXETfpN+U= +cloud.google.com/go/iot v1.13.0/go.mod h1:62W4n2fe/Ct66NWJEfCB5suZ3XsL5Atx+MxFjScr+9s= cloud.google.com/go/kms v1.21.2/go.mod h1:8wkMtHV/9Z8mLXEXr1GK7xPSBdi6knuLXIhqjuWcI6w= cloud.google.com/go/kms v1.25.0/go.mod h1:XIdHkzfj0bUO3E+LvwPg+oc7s58/Ns8Nd8Sdtljihbk= +cloud.google.com/go/kms v1.31.0/go.mod h1:YIyXZym11R5uovJJt4oN5eUL3oPmirF3yKeIh6QAf4U= cloud.google.com/go/language v1.14.5/go.mod h1:nl2cyAVjcBct1Hk73tzxuKebk0t2eULFCaruhetdZIA= cloud.google.com/go/language v1.14.6/go.mod h1:7y3J9OexQsfkWNGCxhT+7lb64pa60e12ZCoWDOHxJ1M= +cloud.google.com/go/language v1.18.0/go.mod h1:xSeiVB4UiA9wYmFy2GWjf1Mb1K3uR1Yi/80qoqTxH04= cloud.google.com/go/lifesciences v0.10.6/go.mod h1:1nnZwaZcBThDujs9wXzECnd1S5d+UiDkPuJWAmhRi7Q= cloud.google.com/go/lifesciences v0.10.7/go.mod h1:v3AbTki9iWttEls/Wf4ag3EqeLRHofploOcpsLnu7iY= +cloud.google.com/go/lifesciences v0.15.0/go.mod h1:FwS+QkqPdVWl4SmKUCFozFvsTVWTLH13HCKcwR/MR9U= cloud.google.com/go/logging v1.13.0/go.mod h1:36CoKh6KA/M0PbhPKMq6/qety2DCAErbhXT62TuXALA= cloud.google.com/go/logging v1.13.1/go.mod h1:XAQkfkMBxQRjQek96WLPNze7vsOmay9H5PqfsNYDqvw= +cloud.google.com/go/logging v1.18.0/go.mod h1:ZGKnpBaURITh+g/uom2VhbiFoFWvejcrHPDhxFtU/gI= cloud.google.com/go/longrunning v0.4.1 h1:v+yFJOfKC3yZdY6ZUI933pIYdhyhV8S3NpWrXWmg7jM= cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo= cloud.google.com/go/longrunning v0.5.1 h1:Fr7TXftcqTudoyRJa113hyaqlGdiBQkp0Gq7tErFDWI= @@ -164,114 +230,166 @@ cloud.google.com/go/longrunning v0.5.5 h1:GOE6pZFdSrTb4KAiKnXsJBtlE6mEyaW44oKyMI cloud.google.com/go/longrunning v0.5.5/go.mod h1:WV2LAxD8/rg5Z1cNW6FJ/ZpX4E4VnDnoTk0yawPBB7s= cloud.google.com/go/longrunning v0.6.7/go.mod h1:EAFV3IZAKmM56TyiE6VAP3VoTzhZzySwI/YI1s/nRsY= cloud.google.com/go/longrunning v0.8.0/go.mod h1:UmErU2Onzi+fKDg2gR7dusz11Pe26aknR4kHmJJqIfk= +cloud.google.com/go/longrunning v0.13.0/go.mod h1:8nqFBPOO1U/XkhWl0I19AMZEphrHi73VNABIpKYaTwM= cloud.google.com/go/managedidentities v1.7.6/go.mod h1:pYCWPaI1AvR8Q027Vtp+SFSM/VOVgbjBF4rxp1/z5p4= cloud.google.com/go/managedidentities v1.7.7/go.mod h1:nwNlMxtBo2YJMvsKXRtAD1bL41qiCI9npS7cbqrsJUs= +cloud.google.com/go/managedidentities v1.12.0/go.mod h1:rm72jf/v//0NG73VQNZM1JlV2E95uhJymmSXlgi6hMA= cloud.google.com/go/maps v1.20.4/go.mod h1:Act0Ws4HffrECH+pL8YYy1scdSLegov7+0c6gvKqRzI= cloud.google.com/go/maps v1.26.0/go.mod h1:+auempdONAP8emtm48aCfNo1ZC+3CJniRA1h8J4u7bY= +cloud.google.com/go/maps v1.35.0/go.mod h1:HH1V8tduMn+b9oRMCdl3vok98uvHco/wElZXyJQ/9kU= cloud.google.com/go/mediatranslation v0.9.6/go.mod h1:WS3QmObhRtr2Xu5laJBQSsjnWFPPthsyetlOyT9fJvE= cloud.google.com/go/mediatranslation v0.9.7/go.mod h1:mz3v6PR7+Fd/1bYrRxNFGnd+p4wqdc/fyutqC5QHctw= +cloud.google.com/go/mediatranslation v0.13.0/go.mod h1:kjZrowuigFr+Bf1HM1TCtp1a3E3kfG1ovPK5VEuaNAQ= cloud.google.com/go/memcache v1.11.6/go.mod h1:ZM6xr1mw3F8TWO+In7eq9rKlJc3jlX2MDt4+4H+/+cc= cloud.google.com/go/memcache v1.11.7/go.mod h1:AU1jYlUqCihxapcJ1GGMtlMWDVhzjbfUWBXqsXa4rBg= +cloud.google.com/go/memcache v1.16.0/go.mod h1:y/rXhJiieCF742K958dY29fSfM+Y3wh2thRmWspU2Dg= cloud.google.com/go/metastore v1.14.7/go.mod h1:0dka99KQofeUgdfu+K/Jk1KeT9veWZlxuZdJpZPtuYU= cloud.google.com/go/metastore v1.14.8/go.mod h1:h1XI2LpD4ohJhQYn9TwXqKb5sVt6KSo47ft96SiFF1s= +cloud.google.com/go/metastore v1.19.0/go.mod h1:JGTjGdQ627m2ptDo86XsIKqzzZCk+GG41VEFD7ENsqs= cloud.google.com/go/monitoring v1.24.2/go.mod h1:x7yzPWcgDRnPEv3sI+jJGBkwl5qINf+6qY4eq0I9B4U= cloud.google.com/go/monitoring v1.24.3/go.mod h1:nYP6W0tm3N9H/bOw8am7t62YTzZY+zUeQ+Bi6+2eonI= +cloud.google.com/go/monitoring v1.29.0/go.mod h1:72NOVjJXHY/HBfoLT0+qlCZBT059+9VXLeAnL2PeeVM= cloud.google.com/go/networkconnectivity v1.17.1/go.mod h1:DTZCq8POTkHgAlOAAEDQF3cMEr/B9k1ZbpklqvHEBtg= cloud.google.com/go/networkconnectivity v1.20.0/go.mod h1:9MzGwD4ljiq+Z2Pg3ue27OEewCuHz7IUfw1fITrIdSw= +cloud.google.com/go/networkconnectivity v1.26.0/go.mod h1:Uhzfk7NbiY6RNqV9XFvPWRji58+MkTYsTRfQ3EPtrGg= cloud.google.com/go/networkmanagement v1.19.1/go.mod h1:icgk265dNnilxQzpr6rO9WuAuuCmUOqq9H6WBeM2Af4= cloud.google.com/go/networkmanagement v1.22.0/go.mod h1:RGR62aLOlm72C7DT/3yaMUK43oill6hj9wqktUQ8h6Q= +cloud.google.com/go/networkmanagement v1.28.0/go.mod h1:2YogSU3sD7LvtmWntUAuGARbFQmy3A0En3LrJr69jkU= cloud.google.com/go/networksecurity v0.10.6/go.mod h1:FTZvabFPvK2kR/MRIH3l/OoQ/i53eSix2KA1vhBMJec= cloud.google.com/go/networksecurity v0.11.0/go.mod h1:JLgDsg4tOyJ3eMO8lypjqMftbfd60SJ+P7T+DUmWBsM= +cloud.google.com/go/networksecurity v0.16.0/go.mod h1:LMn10eRVf4K85PMF33yRoKAra7VhCOetxFcLDMh9A74= cloud.google.com/go/notebooks v1.12.6/go.mod h1:3Z4TMEqAKP3pu6DI/U+aEXrNJw9hGZIVbp+l3zw8EuA= cloud.google.com/go/notebooks v1.12.7/go.mod h1:uR9pxAkKmlNloibMr9Q1t8WhIu4P2JeqJs7c064/0Mo= +cloud.google.com/go/notebooks v1.17.0/go.mod h1:NScGIhfQCqLRIlVaUVbm595F6dhqiTl5XS1KaKgitKM= cloud.google.com/go/optimization v1.7.6/go.mod h1:4MeQslrSJGv+FY4rg0hnZBR/tBX2awJ1gXYp6jZpsYY= cloud.google.com/go/optimization v1.7.7/go.mod h1:OY2IAlX23o52qwMAZ0w65wibKuV12a4x6IHDTCq6kcU= +cloud.google.com/go/optimization v1.11.0/go.mod h1:qCWskZMcynh0GBsUrCP6oPwwnUhbwg5UcXvVM9hzOD8= cloud.google.com/go/orchestration v1.11.9/go.mod h1:KKXK67ROQaPt7AxUS1V/iK0Gs8yabn3bzJ1cLHw4XBg= cloud.google.com/go/orchestration v1.11.10/go.mod h1:tz7m1s4wNEvhNNIM3JOMH0lYxBssu9+7si5MCPw/4/0= +cloud.google.com/go/orchestration v1.16.0/go.mod h1:H7MFVP8Z/dtml39nf43sWYPL/2o7J4tdSZAlJrBuqnQ= cloud.google.com/go/orgpolicy v1.15.0/go.mod h1:NTQLwgS8N5cJtdfK55tAnMGtvPSsy95JJhESwYHaJVs= cloud.google.com/go/orgpolicy v1.15.1/go.mod h1:bpvi9YIyU7wCW9WiXL/ZKT7pd2Ovegyr2xENIeRX5q0= +cloud.google.com/go/orgpolicy v1.20.0/go.mod h1:9LHqEGx5P5dhansdKTNIEXpM+QbebAIOs66+HUID4aQ= cloud.google.com/go/osconfig v1.14.6/go.mod h1:LS39HDBH0IJDFgOUkhSZUHFQzmcWaCpYXLrc3A4CVzI= cloud.google.com/go/osconfig v1.15.1/go.mod h1:NegylQQl0+5m+I+4Ey/g3HGeQxKkncQ1q+Il4DZ8PME= +cloud.google.com/go/osconfig v1.21.0/go.mod h1:BofnHqjjvu6lZQv/hqo2+rLCUiY4O6A9UYwwvVrSBjk= cloud.google.com/go/oslogin v1.14.6/go.mod h1:xEvcRZTkMXHfNSKdZ8adxD6wvRzeyAq3cQX3F3kbMRw= cloud.google.com/go/oslogin v1.14.7/go.mod h1:NB6NqBHfDMwznePdBVX+ILllc1oPCdNSGp5u/WIyndY= +cloud.google.com/go/oslogin v1.18.0/go.mod h1:3Oa36T3781Mv+yCSVYlfasi7auHjfPFqvNOd1q92umc= cloud.google.com/go/phishingprotection v0.9.6/go.mod h1:VmuGg03DCI0wRp/FLSvNyjFj+J8V7+uITgHjCD/x4RQ= cloud.google.com/go/phishingprotection v0.9.7/go.mod h1:JTI4HNGyAbWolBoNOoCyCF0e3cqPNrYnlievHU49EwE= +cloud.google.com/go/phishingprotection v0.13.0/go.mod h1:2gyYqwNjePPEocXDkDve3EuJPaRqN/E7fp28K3arR0k= cloud.google.com/go/policytroubleshooter v1.11.6/go.mod h1:jdjYGIveoYolk38Dm2JjS5mPkn8IjVqPsDHccTMu3mY= cloud.google.com/go/policytroubleshooter v1.11.7/go.mod h1:JP/aQ+bUkt4Gz6lQXBi/+A/6nyNRZ0Pvxui5Xl9ieyk= +cloud.google.com/go/policytroubleshooter v1.15.0/go.mod h1:yNuROjN6h+2/TE2JOvBBJMjYIjC6j0UYHq8f2kVHlA4= cloud.google.com/go/privatecatalog v0.10.7/go.mod h1:Fo/PF/B6m4A9vUYt0nEF1xd0U6Kk19/Je3eZGrQ6l60= cloud.google.com/go/privatecatalog v0.10.8/go.mod h1:BkLHi+rtAGYBt5DocXLytHhF0n6F03Tegxgty40Y7aA= +cloud.google.com/go/privatecatalog v0.15.0/go.mod h1:av2b5Rv+oG5ORxUqGlCAYO9s4pXjgc6q2qO9nkTcqT8= cloud.google.com/go/pubsub v1.3.1 h1:ukjixP1wl0LpnZ6LWtZJ0mX5tBmjp1f8Sqer8Z2OMUU= cloud.google.com/go/pubsub v1.49.0/go.mod h1:K1FswTWP+C1tI/nfi3HQecoVeFvL4HUOB1tdaNXKhUY= cloud.google.com/go/pubsub v1.50.1/go.mod h1:6YVJv3MzWJUVdvQXG081sFvS0dWQOdnV+oTo++q/xFk= +cloud.google.com/go/pubsub v1.50.2/go.mod h1:jyCWeZdGFqd4mitSsBERnJcpqaHBsxQoPkNvjj4sp0w= cloud.google.com/go/pubsub/v2 v2.0.0/go.mod h1:0aztFxNzVQIRSZ8vUr79uH2bS3jwLebwK6q1sgEub+E= +cloud.google.com/go/pubsub/v2 v2.5.1/go.mod h1:Pd+qeabMX+576vQJhTN7TelE4k6kJh15dLU/ptOQ/UA= cloud.google.com/go/pubsublite v1.8.2/go.mod h1:4r8GSa9NznExjuLPEJlF1VjOPOpgf3IT6k8x/YgaOPI= cloud.google.com/go/recaptchaenterprise/v2 v2.20.4/go.mod h1:3H8nb8j8N7Ss2eJ+zr+/H7gyorfzcxiDEtVBDvDjwDQ= cloud.google.com/go/recaptchaenterprise/v2 v2.21.0/go.mod h1:HxQYqZC2/zl2CvKN7jJEv71vEdDi1GMGNUiZxnpiuVI= +cloud.google.com/go/recaptchaenterprise/v2 v2.26.0/go.mod h1:+ntF70/j7qBa6G/pwmYA0mkBcDeTCXV6WDqUL7GObfs= cloud.google.com/go/recommendationengine v0.9.6/go.mod h1:nZnjKJu1vvoxbmuRvLB5NwGuh6cDMMQdOLXTnkukUOE= cloud.google.com/go/recommendationengine v0.9.7/go.mod h1:snZ/FL147u86Jqpv1j95R+CyU5NvL/UzYiyDo6UByTM= +cloud.google.com/go/recommendationengine v0.14.0/go.mod h1:UP9cN46tDpZ/N57eDYIWeIRHjMOchtiIyjWjV0Dvr3k= cloud.google.com/go/recommender v1.13.5/go.mod h1:v7x/fzk38oC62TsN5Qkdpn0eoMBh610UgArJtDIgH/E= cloud.google.com/go/recommender v1.13.6/go.mod h1:y5/5womtdOaIM3xx+76vbsiA+8EBTIVfWnxHDFHBGJM= +cloud.google.com/go/recommender v1.18.0/go.mod h1:INRBLfBQJCrgPqjBVFht4OjaFq/WhB/c5V1sqBOdX8g= cloud.google.com/go/redis v1.18.2/go.mod h1:q6mPRhLiR2uLf584Lcl4tsiRn0xiFlu6fnJLwCORMtY= cloud.google.com/go/redis v1.18.3/go.mod h1:x8HtXZbvMBDNT6hMHaQ022Pos5d7SP7YsUH8fCJ2Wm4= +cloud.google.com/go/redis v1.23.0/go.mod h1:EUlUT24BAL6LsE1f/N9Bg3LhRCfH+LzwLGbst3KuZRw= cloud.google.com/go/resourcemanager v1.10.6/go.mod h1:VqMoDQ03W4yZmxzLPrB+RuAoVkHDS5tFUUQUhOtnRTg= cloud.google.com/go/resourcemanager v1.10.7/go.mod h1:rScGkr6j2eFwxAjctvOP/8sqnEpDbQ9r5CKwKfomqjs= +cloud.google.com/go/resourcemanager v1.15.0/go.mod h1:ve0VNxPoDU6XxDuEMCjkineb0YzXQXx3mOWwnNckGDE= cloud.google.com/go/resourcesettings v1.8.3/go.mod h1:BzgfXFHIWOOmHe6ZV9+r3OWfpHJgnqXy8jqwx4zTMLw= cloud.google.com/go/retail v1.20.0/go.mod h1:1CXWDZDJTOsK6lPjkv67gValP9+h1TMadTC9NpFFr9s= cloud.google.com/go/retail v1.26.0/go.mod h1:gMfh6s174Mvy1rK4g50J9TH5sRim8px+Krml25kdrqo= +cloud.google.com/go/retail v1.31.0/go.mod h1:sfq/cT+gfSLuURf/mdVAw5n0pav3hxSP1rT8RfL7Qxk= cloud.google.com/go/run v1.9.3/go.mod h1:Si9yDIkUGr5vsXE2QVSWFmAjJkv/O8s3tJ1eTxw3p1o= cloud.google.com/go/run v1.15.0/go.mod h1:rgFHMdAopLl++57vzeqA+a1o2x0/ILZnEacRD6nC0EA= +cloud.google.com/go/run v1.21.0/go.mod h1:Z5wHbyFirI8XU48EPs5XJf/qmVm1SXZEhuS8EvZOuQU= cloud.google.com/go/scheduler v1.11.7/go.mod h1:gqYs8ndLx2M5D0oMJh48aGS630YYvC432tHCnVWN13s= cloud.google.com/go/scheduler v1.11.8/go.mod h1:bNKU7/f04eoM6iKQpwVLvFNBgGyJNS87RiFN73mIPik= +cloud.google.com/go/scheduler v1.16.0/go.mod h1:0hsZg0MZJADyke1lutI0FHAYJR8Dtm8oIivXkmpACkA= cloud.google.com/go/secretmanager v1.14.7/go.mod h1:uRuB4F6NTFbg0vLQ6HsT7PSsfbY7FqHbtJP1J94qxGc= cloud.google.com/go/secretmanager v1.16.0/go.mod h1://C/e4I8D26SDTz1f3TQcddhcmiC3rMEl0S1Cakvs3Q= +cloud.google.com/go/secretmanager v1.20.0/go.mod h1:9OmSuOeiiUicANglrbdKWSnT3gYkRcXuUQDk7dDW0zU= cloud.google.com/go/security v1.18.5/go.mod h1:D1wuUkDwGqTKD0Nv7d4Fn2Dc53POJSmO4tlg1K1iS7s= cloud.google.com/go/security v1.19.2/go.mod h1:KXmf64mnOsLVKe8mk/bZpU1Rsvxqc0Ej0A6tgCeN93w= +cloud.google.com/go/security v1.24.0/go.mod h1:XaB3p0SE7v2bBitsLBb1hM6R8/oI/k/IujpXFJalFK0= cloud.google.com/go/securitycenter v1.36.2/go.mod h1:80ocoXS4SNWxmpqeEPhttYrmlQzCPVGaPzL3wVcoJvE= cloud.google.com/go/securitycenter v1.38.1/go.mod h1:Ge2D/SlG2lP1FrQD7wXHy8qyeloRenvKXeB4e7zO6z0= +cloud.google.com/go/securitycenter v1.44.0/go.mod h1:7BMMbSTAddVfiE+HrC8tKS6SuRkyK7FRPlkpAZBRV3U= cloud.google.com/go/servicedirectory v1.12.6/go.mod h1:OojC1KhOMDYC45oyTn3Mup08FY/S0Kj7I58dxUMMTpg= cloud.google.com/go/servicedirectory v1.12.7/go.mod h1:gOtN+qbuCMH6tj2dqlDY3qQL7w3V0+nkWaZElnJK8Ps= +cloud.google.com/go/servicedirectory v1.17.0/go.mod h1:CtgjXS1idj3s9Q6tB68021Rzk8Q6decV6+ldXC1BoBk= cloud.google.com/go/shell v1.8.6/go.mod h1:GNbTWf1QA/eEtYa+kWSr+ef/XTCDkUzRpV3JPw0LqSk= cloud.google.com/go/shell v1.8.7/go.mod h1:OTke7qc3laNEW5Jr5OV9VR3IwU5x5VqGOE6705zFex4= +cloud.google.com/go/shell v1.12.0/go.mod h1:TivWrVriy6xQ0wBjNJJridJgODZz8zXUEW2u48kynzY= cloud.google.com/go/spanner v1.81.0/go.mod h1:3yqzHZvK52zLw10mNLG8MefCEYp3iRFJryTLf5u+mJg= cloud.google.com/go/spanner v1.87.0/go.mod h1:tcj735Y2aqphB6/l+X5MmwG4NnV+X1NJIbFSZGaHYXw= +cloud.google.com/go/spanner v1.91.0/go.mod h1:8NB5a7qgwIhGD19Ly+vkpKffPL78vIG9RcrgsuREha0= cloud.google.com/go/speech v1.27.1/go.mod h1:efCfklHFL4Flxcdt9gpEMEJh9MupaBzw3QiSOVeJ6ck= cloud.google.com/go/speech v1.29.0/go.mod h1:wtUmIS/h0ZYU6cPA9klcyST3f6i2FdnvNDqENjrRDds= +cloud.google.com/go/speech v1.35.0/go.mod h1:shnf33sZbGnQQZyek1fdLOR5rRKV6D3jsNqpqyijvj8= cloud.google.com/go/storage v1.14.0 h1:6RRlFMv1omScs6iq2hfE3IvgE+l6RfJPampq8UZc5TU= cloud.google.com/go/storage v1.35.1 h1:B59ahL//eDfx2IIKFBeT5Atm9wnNmj3+8xG/W4WB//w= cloud.google.com/go/storage v1.35.1/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8= cloud.google.com/go/storagetransfer v1.12.4/go.mod h1:p1xLKvpt78aQFRJ8lZGYArgFuL4wljFzitPZoYjl/8A= cloud.google.com/go/storagetransfer v1.13.1/go.mod h1:S858w5l383ffkdqAqrAA+BC7KlhCqeNieK3sFf5Bj4Y= +cloud.google.com/go/storagetransfer v1.18.0/go.mod h1:AbGutEym/KNasoiDpSj/CYbigp5yhgosSgwlhGvQNs4= cloud.google.com/go/talent v1.8.3/go.mod h1:oD3/BilJpJX8/ad8ZUAxlXHCslTg2YBbafFH3ciZSLQ= cloud.google.com/go/talent v1.8.4/go.mod h1:3yukBXUTVFNyKcJpUExW/k5gqEy8qW6OCNj7WdN0MWo= +cloud.google.com/go/talent v1.13.0/go.mod h1:GSwli9V25WQdzeuJDJWH9TlQmA8lPFn7yKsxowdxW9Y= cloud.google.com/go/texttospeech v1.13.0/go.mod h1:g/tW/m0VJnulGncDrAoad6WdELMTes8eb77Idz+4HCo= cloud.google.com/go/texttospeech v1.16.0/go.mod h1:AeSkoH3ziPvapsuyI07TWY4oGxluAjntX+pF4PJ2jy0= +cloud.google.com/go/texttospeech v1.21.0/go.mod h1:p/UVJILAo/S5vsJaWZVdDRzNzA7wXIA+hTACvpMeOBk= cloud.google.com/go/tpu v1.8.3/go.mod h1:Do6Gq+/Jx6Xs3LcY2WhHyGwKDKVw++9jIJp+X+0rxRE= cloud.google.com/go/tpu v1.8.4/go.mod h1:ul0cyWSHr6jHGZYElZe6HvQn35VY93RAlwpDiSBRnPA= +cloud.google.com/go/tpu v1.13.0/go.mod h1:F5gT5BL22Dhsr05JLHdMjAjj+wcTn3Xtuu4jvq9yFug= cloud.google.com/go/trace v1.11.6/go.mod h1:GA855OeDEBiBMzcckLPE2kDunIpC72N+Pq8WFieFjnI= cloud.google.com/go/trace v1.11.7/go.mod h1:TNn9d5V3fQVf6s4SCveVMIBS2LJUqo73GACmq/Tky0s= +cloud.google.com/go/trace v1.16.0/go.mod h1:r+bdAn16dKLSV1G2D5v3e58IlQlizfxWrUfjx7kM7X0= cloud.google.com/go/translate v1.12.5/go.mod h1:o/v+QG/bdtBV1d1edmtau0PwTfActvxPk/gtqdSDBi4= cloud.google.com/go/translate v1.12.7/go.mod h1:wwJp14NZyWvcrFANhIXutXj0pOBkYciBHwSlUOykcjI= +cloud.google.com/go/translate v1.17.0/go.mod h1:3mErnHTQBu9yeLiL35K0HBBuaM6Vk2fD/vyWFz790VU= cloud.google.com/go/video v1.23.5/go.mod h1:ZSpGFCpfTOTmb1IkmHNGC/9yI3TjIa/vkkOKBDo0Vpo= cloud.google.com/go/video v1.27.1/go.mod h1:xzfAC77B4vtnbi/TT3UUxEjCa/+Ehy5EA8w470ytOig= +cloud.google.com/go/video v1.32.0/go.mod h1:KxDL728ZzH+FJwtEb9XkiLTETW5bI37hTWbJiRYeXkk= cloud.google.com/go/videointelligence v1.12.6/go.mod h1:/l34WMndN5/bt04lHodxiYchLVuWPQjCU6SaiTswrIw= cloud.google.com/go/videointelligence v1.12.7/go.mod h1:XAk5hCMY+GihxJ55jNoMdwdXSNZnCl3wGs2+94gK7MA= +cloud.google.com/go/videointelligence v1.16.0/go.mod h1:mmX1JpIWzwozaigrdRNjikZc3aFLNHFKh+OFwAdfiW4= cloud.google.com/go/vision/v2 v2.9.5/go.mod h1:1SiNZPpypqZDbOzU052ZYRiyKjwOcyqgGgqQCI/nlx8= cloud.google.com/go/vision/v2 v2.9.6/go.mod h1:lJC+vP15D5znJvHQYjEoTKnpToX1L93BUlvBmzM0gyg= +cloud.google.com/go/vision/v2 v2.14.0/go.mod h1:ODlLCajJOq4t8thoi1uVvbnfIfix73HsYWhZuIveagQ= cloud.google.com/go/vmmigration v1.8.6/go.mod h1:uZ6/KXmekwK3JmC8PzBM/cKQmq404TTfWtThF6bbf0U= cloud.google.com/go/vmmigration v1.10.0/go.mod h1:LDztCWEb+RwS1bPg4Xzt0fcJS9kVrFxa3ejhH7OW9vg= +cloud.google.com/go/vmmigration v1.15.0/go.mod h1:MP6mQ21ru1usBeCbl805Ioz0Fy+yf3qK2kUkhZ69QQY= cloud.google.com/go/vmwareengine v1.3.5/go.mod h1:QuVu2/b/eo8zcIkxBYY5QSwiyEcAy6dInI7N+keI+Jg= cloud.google.com/go/vmwareengine v1.3.6/go.mod h1:ps0rb+Skgpt9ppHYC0o5DqtJ5ld2FyS8sAqtbHH8t9s= +cloud.google.com/go/vmwareengine v1.8.0/go.mod h1:e66l90IZhm1yQfYZv+YCWjSNSklQZCRmuEvKL8n3Ua0= cloud.google.com/go/vpcaccess v1.8.6/go.mod h1:61yymNplV1hAbo8+kBOFO7Vs+4ZHYI244rSFgmsHC6E= cloud.google.com/go/vpcaccess v1.8.7/go.mod h1:9RYw5bVvk4Z51Rc8vwXT63yjEiMD/l7XyEaDyrNHgmk= +cloud.google.com/go/vpcaccess v1.13.0/go.mod h1:4Uus6E/9FYUtIrwBE1wJ1RosKwb02H6kEd9puJ02TL8= cloud.google.com/go/webrisk v1.11.1/go.mod h1:+9SaepGg2lcp1p0pXuHyz3R2Yi2fHKKb4c1Q9y0qbtA= cloud.google.com/go/webrisk v1.11.2/go.mod h1:yH44GeXz5iz4HFsIlGeoVvnjwnmfbni7Lwj1SelV4f0= +cloud.google.com/go/webrisk v1.16.0/go.mod h1:VIQw8smiaMOlget/xOk6niTkNJTiQc5skEmCuAksxJc= cloud.google.com/go/websecurityscanner v1.7.6/go.mod h1:ucaaTO5JESFn5f2pjdX01wGbQ8D6h79KHrmO2uGZeiY= cloud.google.com/go/websecurityscanner v1.7.7/go.mod h1:ng/PzARaus3Bj4Os4LpUnyYHsbtJky1HbBDmz148v1o= +cloud.google.com/go/websecurityscanner v1.12.0/go.mod h1:cZSc9HqoFdccL1mqZtPIInOd4R8PBGwI20wdnrz6AO8= cloud.google.com/go/workflows v1.14.2/go.mod h1:5nqKjMD+MsJs41sJhdVrETgvD5cOK3hUcAs8ygqYvXQ= cloud.google.com/go/workflows v1.14.3/go.mod h1:CC9+YdVI2Kvp0L58WajHpEfKJxhrtRh3uQ0SYWcmAk4= +cloud.google.com/go/workflows v1.19.0/go.mod h1:TWsrDGgsJy7xAJ07byzHhKKehEWItJG3BivEHVhGH5g= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9 h1:VpgP7xuJadIUuKccphEpTJnWhS2jkQyMt6Y7pJCD7fY= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0= github.com/alecthomas/kingpin/v2 v2.3.2 h1:H0aULhgmSzN8xQ3nX1uxtdlTHYoPLu5AhHxWrKI6ocU= github.com/alecthomas/kingpin/v2 v2.3.2/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE= github.com/alecthomas/kingpin/v2 v2.4.0 h1:f48lwail6p8zpO1bC4TxtqACaGqHYA22qkHjHpqDjYY= @@ -295,16 +413,20 @@ github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObk github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403 h1:cqQfy1jclcSy/FwLjemeg3SR1yaINm74aQyupQ0Bl8M= +github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2/go.mod h1:qwXFYgsP6T7XnJtbKlf1HP8AjxZZyzxMmc+Lq5GjlU4= github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad h1:EmNYJhPYy0pOFjCx2PrgtaBXmee0iUX9hLlxE1xHOJE= +github.com/envoyproxy/go-control-plane v0.14.0/go.mod h1:NcS5X47pLl/hfqxU70yPwL9ZMkUlwlKxtAohpi2wBEU= +github.com/envoyproxy/go-control-plane/envoy v1.37.0/go.mod h1:DReE9MMrmecPy+YvQOAOHNYMALuowAnbjjEMkkWOi6A= +github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4= github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A= +github.com/envoyproxy/protoc-gen-validate v1.3.3/go.mod h1:TsndJ/ngyIdQRhMcVVGDDHINPLWB7C82oDArY51KfB0= github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0= @@ -322,13 +444,12 @@ 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/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= +github.com/golang/glog v1.2.5/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= 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/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/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/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= @@ -440,6 +561,7 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/sftp v1.13.1 h1:I2qBYMChEhIjOgazfJmV3/mZM256btk6wkCDRmW7JYs= github.com/pkg/sftp v1.13.6 h1:JFZT4XbOU7l77xGSpOdW+pwIMqP044IyjXX6FGyEKFo= github.com/pkg/sftp v1.13.6/go.mod h1:tz1ryNURKu77RL+GuCzmoJYxQczL3wLNNpPWagdg4Qk= +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/roadrunner-server/http/v5 v5.2.8 h1:cXUKHxUGlfEa4MlABUno3w6exok+RDkybSR065kGwUE= github.com/roadrunner-server/http/v5 v5.2.8/go.mod h1:5XopTtiPMbF03zEDkdd+mM3KRtgsbZ8o5uHMRS8hx98= @@ -461,6 +583,7 @@ github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmq github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/viper v1.20.0 h1:zrxIyR3RQIOsarIrgL8+sAvALXul9jeEPa06Y0Ph6vY= github.com/spf13/viper v1.20.0/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= +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.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= @@ -499,6 +622,7 @@ go.etcd.io/etcd/client/v3 v3.5.12 h1:v5lCPXn1pf1Uu3M4laUE2hp/geOTc5uPcYYsNe1lDxg go.etcd.io/etcd/client/v3 v3.5.12/go.mod h1:tSbBCakoWmmddL+BKVAJHa9km+O/E+bumDe9mSbPiqw= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/contrib/detectors/gcp v1.42.0/go.mod h1:W9zQ439utxymRrXsUOzZbFX4JhLxXU4+ZnCt8GG7yA8= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0= go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg= @@ -517,6 +641,7 @@ go.temporal.io/api v1.35.0/go.mod h1:OYkuupuCw6s/5TkcKHMb9EcIrOI+vTsbf/CGaprbzb0 go.temporal.io/api v1.36.0/go.mod h1:0nWIrFRVPlcrkopXqxir/UWOtz/NZCo+EE9IX4UwVxw= go.temporal.io/api v1.49.0/go.mod h1:iaxoP/9OXMJcQkETTECfwYq4cw/bj4nwov8b3ZLVnXM= go.temporal.io/api v1.57.0/go.mod h1:iaxoP/9OXMJcQkETTECfwYq4cw/bj4nwov8b3ZLVnXM= +go.temporal.io/api v1.62.11/go.mod h1:iaxoP/9OXMJcQkETTECfwYq4cw/bj4nwov8b3ZLVnXM= go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= @@ -554,6 +679,7 @@ golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbht golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -563,6 +689,7 @@ golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457 h1:zf5N6UOrA487eEFacMe golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0= golang.org/x/telemetry v0.0.0-20250710130107-8d8967aff50b/go.mod h1:4ZwOYna0/zsOKwuR5X/m0QFOJpSZvAxFfkQT+Erd9D4= golang.org/x/telemetry v0.0.0-20260109210033-bd525da824e2/go.mod h1:b7fPSJ0pKZ3ccUh8gnTONJxhn3c/PS6tyzQvyqw4iA8= +golang.org/x/telemetry v0.0.0-20260508192327-42602be52be6/go.mod h1:Eqhaxk/wZsWEH8CRxLwj6xzEJbz7k1EFGqx7nyCoabE= golang.org/x/term v0.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= @@ -576,6 +703,7 @@ golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E= golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww= +golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk= golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= @@ -629,6 +757,7 @@ google.golang.org/genproto/googleapis/api v0.0.0-20240624140628-dc46fd24d27d/go. google.golang.org/genproto/googleapis/api v0.0.0-20240722135656-d784300faade/go.mod h1:mw8MG/Qz5wfgYr6VqVCiZcHe/GJEfI+oGGDCohaVgB0= google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:pKLAc5OolXC3ViWGI62vvC0n10CpwAtRcTNCFwTKBEw= google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409/go.mod h1:fl8J1IvUjCilwZzQowmw2b7HQB2eAuYBabMXzWurF+I= +google.golang.org/genproto/googleapis/api v0.0.0-20260504160031-60b97b32f348/go.mod h1:Yzdzr5OOZFgSsEV2D/Xi9NL3bszpXFAg0hFJiRohcD8= google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4= google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 h1:N3bU/SQDCDyD6R528GJ/PwW9KjYcJA3dgyH+MovAkIM= diff --git a/plugin.go b/plugin.go index b6b4811..3f8fd69 100644 --- a/plugin.go +++ b/plugin.go @@ -12,6 +12,7 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/collectors" "github.com/prometheus/client_golang/prometheus/promhttp" + "github.com/roadrunner-server/api-go/v6/metrics/v1/metricsV1connect" "github.com/roadrunner-server/endure/v2/dep" "github.com/roadrunner-server/errors" "golang.org/x/sys/cpu" @@ -261,10 +262,7 @@ func (p *Plugin) Name() string { return PluginName } -// RPC interface satisfaction -func (p *Plugin) RPC() any { - return &rpc{ - p: p, - log: p.log, - } +// RPC returns the Connect-RPC handler mount for the metrics service. +func (p *Plugin) RPC() (string, http.Handler) { + return metricsV1connect.NewMetricsServiceHandler(&rpc{p: p, log: p.log}) } diff --git a/rpc.go b/rpc.go index 37804fa..8419de6 100644 --- a/rpc.go +++ b/rpc.go @@ -1,330 +1,270 @@ package metrics import ( + "context" + stderr "errors" + "fmt" "log/slog" + "connectrpc.com/connect" "github.com/prometheus/client_golang/prometheus" + metricsV1 "github.com/roadrunner-server/api-go/v6/metrics/v1" "github.com/roadrunner-server/errors" ) +var ( + errUndefinedCollector = stderr.New("undefined collector") + errRequiredLabels = stderr.New("required labels for collector") + errUnsupportedOpForCol = stderr.New("collector does not support the requested operation") + errUnknownCollectorTyp = stderr.New("unknown collector type") +) + type rpc struct { p *Plugin log *slog.Logger } -// Metric represents a single metric produced by the application. -type Metric struct { - // Collector name. - Name string `msgpack:"alias:name"` - // Collector value. - Value float64 `msgpack:"alias:value"` - // Labels associated with metric. Only for vector metrics. Must be provided in a form of label values. - Labels []string `msgpack:"alias:labels"` -} +func (r *rpc) Add(_ context.Context, req *connect.Request[metricsV1.AddRequest]) (*connect.Response[metricsV1.Response], error) { + m := req.Msg.GetMetric() + r.log.Debug("adding metric", "name", m.GetName(), "value", m.GetValue(), "labels", m.GetLabels()) -// Add new metric to the designated collector. -func (r *rpc) Add(m *Metric, ok *bool) error { - const op = errors.Op("metrics_plugin_add") - r.log.Debug("adding metric", "name", m.Name, "value", m.Value, "labels", m.Labels) - c, exist := r.p.collectors.Load(m.Name) - if !exist { - r.log.Error("undefined collector", "collector", m.Name) - return errors.E(op, errors.Errorf("undefined collector %s, try first Declare the desired collector", m.Name)) + col, code, err := r.lookupCollector(m.GetName()) + if err != nil { + return nil, connect.NewError(code, err) } - col := c.(*collector) - - switch c := col.col.(type) { + switch c := col.(type) { case prometheus.Gauge: - c.Add(m.Value) - + c.Add(m.GetValue()) case *prometheus.GaugeVec: - if len(m.Labels) == 0 { - r.log.Error("required labels for collector", "collector", m.Name) - return errors.E(op, errors.Errorf("required labels for collector %s", m.Name)) - } - - gauge, err := c.GetMetricWithLabelValues(m.Labels...) + gv, err := vecMetric(r, c, m) if err != nil { - r.log.Error("failed to get metrics with label values", "collector", m.Name, "labels", m.Labels) - return errors.E(op, err) + return nil, err } - gauge.Add(m.Value) + gv.Add(m.GetValue()) case prometheus.Counter: - c.Add(m.Value) - + c.Add(m.GetValue()) case *prometheus.CounterVec: - if len(m.Labels) == 0 { - return errors.E(op, errors.Errorf("required labels for collector `%s`", m.Name)) - } - - gauge, err := c.GetMetricWithLabelValues(m.Labels...) + cv, err := vecMetric(r, c, m) if err != nil { - r.log.Error("failed to get metrics with label values", "collector", m.Name, "labels", m.Labels) - return errors.E(op, err) + return nil, err } - gauge.Add(m.Value) - + cv.Add(m.GetValue()) default: - return errors.E(op, errors.Errorf("collector %s does not support method `Add`", m.Name)) + return nil, connect.NewError(connect.CodeFailedPrecondition, + fmt.Errorf("%w: %s does not support Add", errUnsupportedOpForCol, m.GetName())) } - // RPC, set ok to true as return value. Need by r.Call reply argument - *ok = true - r.log.Debug("metric successfully added", "name", m.Name, "labels", m.Labels, "value", m.Value) - return nil + r.log.Debug("metric successfully added", "name", m.GetName(), "labels", m.GetLabels(), "value", m.GetValue()) + return connect.NewResponse(&metricsV1.Response{Ok: true}), nil } -// Sub subtract the value from the specific metric (gauge only). -func (r *rpc) Sub(m *Metric, ok *bool) error { - const op = errors.Op("metrics_plugin_sub") - r.log.Debug("subtracting value from metric", "name", m.Name, "value", m.Value, "labels", m.Labels) - c, exist := r.p.collectors.Load(m.Name) - if !exist { - r.log.Error("undefined collector", "name", m.Name, "value", m.Value, "labels", m.Labels) - return errors.E(op, errors.Errorf("undefined collector %s", m.Name)) - } - if c == nil { - // can it be a nil ??? I guess can't - return errors.E(op, errors.Errorf("undefined collector %s", m.Name)) - } +func (r *rpc) Sub(_ context.Context, req *connect.Request[metricsV1.SubRequest]) (*connect.Response[metricsV1.Response], error) { + m := req.Msg.GetMetric() + r.log.Debug("subtracting value from metric", "name", m.GetName(), "value", m.GetValue(), "labels", m.GetLabels()) - col := c.(*collector) + col, code, err := r.lookupCollector(m.GetName()) + if err != nil { + return nil, connect.NewError(code, err) + } - switch c := col.col.(type) { + switch c := col.(type) { case prometheus.Gauge: - c.Sub(m.Value) - + c.Sub(m.GetValue()) case *prometheus.GaugeVec: - if len(m.Labels) == 0 { - r.log.Error("required labels for collector, but none was provided", "name", m.Name, "value", m.Value) - return errors.E(op, errors.Errorf("required labels for collector %s", m.Name)) - } - - gauge, err := c.GetMetricWithLabelValues(m.Labels...) + gv, err := vecMetric(r, c, m) if err != nil { - r.log.Error("failed to get metrics with label values", "collector", m.Name, "labels", m.Labels) - return errors.E(op, err) + return nil, err } - gauge.Sub(m.Value) + gv.Sub(m.GetValue()) default: - return errors.E(op, errors.Errorf("collector `%s` does not support method `Sub`", m.Name)) + return nil, connect.NewError(connect.CodeFailedPrecondition, + fmt.Errorf("%w: %s does not support Sub", errUnsupportedOpForCol, m.GetName())) } - r.log.Debug("subtracting operation finished successfully", "name", m.Name, "labels", m.Labels, "value", m.Value) - *ok = true - return nil + r.log.Debug("subtracting operation finished successfully", "name", m.GetName(), "labels", m.GetLabels(), "value", m.GetValue()) + return connect.NewResponse(&metricsV1.Response{Ok: true}), nil } -// Observe the value (histogram and summary only). -func (r *rpc) Observe(m *Metric, ok *bool) error { - const op = errors.Op("metrics_plugin_observe") - r.log.Debug("observing metric", "name", m.Name, "value", m.Value, "labels", m.Labels) +func (r *rpc) Observe(_ context.Context, req *connect.Request[metricsV1.ObserveRequest]) (*connect.Response[metricsV1.Response], error) { + m := req.Msg.GetMetric() + r.log.Debug("observing metric", "name", m.GetName(), "value", m.GetValue(), "labels", m.GetLabels()) - c, exist := r.p.collectors.Load(m.Name) - if !exist { - r.log.Error("undefined collector", "name", m.Name, "value", m.Value, "labels", m.Labels) - return errors.E(op, errors.Errorf("undefined collector %s", m.Name)) - } - if c == nil { - return errors.E(op, errors.Errorf("undefined collector %s", m.Name)) + col, code, err := r.lookupCollector(m.GetName()) + if err != nil { + return nil, connect.NewError(code, err) } - col := c.(*collector) - - switch c := col.col.(type) { - case *prometheus.SummaryVec: - if len(m.Labels) == 0 { - return errors.E(op, errors.Errorf("required labels for collector `%s`", m.Name)) - } - - observer, err := c.GetMetricWithLabelValues(m.Labels...) - if err != nil { - return errors.E(op, err) - } - observer.Observe(m.Value) - + switch c := col.(type) { + // prometheus.Histogram and prometheus.Summary have identical method sets + // (Metric + Collector + Observe(float64)), so scalar Summary instances + // also match this branch — type-switch picks the first matching interface + // in source order. case prometheus.Histogram: - c.Observe(m.Value) - + c.Observe(m.GetValue()) case *prometheus.HistogramVec: - if len(m.Labels) == 0 { - return errors.E(op, errors.Errorf("required labels for collector `%s`", m.Name)) + ov, err := vecMetric[prometheus.Observer](r, c, m) + if err != nil { + return nil, err } - - observer, err := c.GetMetricWithLabelValues(m.Labels...) + ov.Observe(m.GetValue()) + case *prometheus.SummaryVec: + ov, err := vecMetric[prometheus.Observer](r, c, m) if err != nil { - r.log.Error("failed to get metrics with label values", "collector", m.Name, "labels", m.Labels) - return errors.E(op, err) + return nil, err } - observer.Observe(m.Value) + ov.Observe(m.GetValue()) default: - return errors.E(op, errors.Errorf("collector `%s` does not support method `Observe`", m.Name)) + return nil, connect.NewError(connect.CodeFailedPrecondition, + fmt.Errorf("%w: %s does not support Observe", errUnsupportedOpForCol, m.GetName())) } - r.log.Debug("observe operation finished successfully", "name", m.Name, "labels", m.Labels, "value", m.Value) - - *ok = true - return nil + r.log.Debug("observe operation finished successfully", "name", m.GetName(), "labels", m.GetLabels(), "value", m.GetValue()) + return connect.NewResponse(&metricsV1.Response{Ok: true}), nil } -// Declare is used to register new collector in prometheus -func (r *rpc) Declare(nc *NamedCollector, ok *bool) error { - const op = errors.Op("metrics_plugin_declare") - r.p.mu.Lock() - defer r.p.mu.Unlock() +func (r *rpc) Set(_ context.Context, req *connect.Request[metricsV1.SetRequest]) (*connect.Response[metricsV1.Response], error) { + m := req.Msg.GetMetric() + r.log.Debug("setting metric", "name", m.GetName(), "value", m.GetValue(), "labels", m.GetLabels()) - r.log.Debug("declaring new metric", "name", nc.Name, "type", nc.Type, "namespace", nc.Namespace) - _, exist := r.p.collectors.Load(nc.Name) - if exist { - r.log.Warn("metric with provided name already exist", "name", nc.Name, "type", nc.Type, "namespace", nc.Namespace) - *ok = true - return nil + col, code, err := r.lookupCollector(m.GetName()) + if err != nil { + return nil, connect.NewError(code, err) } - var promCol prometheus.Collector - switch nc.Type { - case Histogram: - opts := prometheus.HistogramOpts{ - Name: nc.Name, - Namespace: nc.Namespace, - Subsystem: nc.Subsystem, - Help: nc.Help, - Buckets: nc.Buckets, - } - - if len(nc.Labels) != 0 { - promCol = prometheus.NewHistogramVec(opts, nc.Labels) - } else { - promCol = prometheus.NewHistogram(opts) - } - case Gauge: - opts := prometheus.GaugeOpts{ - Name: nc.Name, - Namespace: nc.Namespace, - Subsystem: nc.Subsystem, - Help: nc.Help, + switch c := col.(type) { + case prometheus.Gauge: + c.Set(m.GetValue()) + case *prometheus.GaugeVec: + gv, err := vecMetric(r, c, m) + if err != nil { + return nil, err } + gv.Set(m.GetValue()) + default: + return nil, connect.NewError(connect.CodeFailedPrecondition, + fmt.Errorf("%w: %s does not support Set", errUnsupportedOpForCol, m.GetName())) + } - if len(nc.Labels) != 0 { - promCol = prometheus.NewGaugeVec(opts, nc.Labels) - } else { - promCol = prometheus.NewGauge(opts) - } - case Counter: - opts := prometheus.CounterOpts{ - Name: nc.Name, - Namespace: nc.Namespace, - Subsystem: nc.Subsystem, - Help: nc.Help, - } + r.log.Debug("set operation finished successfully", "name", m.GetName(), "labels", m.GetLabels(), "value", m.GetValue()) + return connect.NewResponse(&metricsV1.Response{Ok: true}), nil +} - if len(nc.Labels) != 0 { - promCol = prometheus.NewCounterVec(opts, nc.Labels) - } else { - promCol = prometheus.NewCounter(opts) - } - case Summary: - opts := prometheus.SummaryOpts{ - Name: nc.Name, - Namespace: nc.Namespace, - Subsystem: nc.Subsystem, - Help: nc.Help, - Objectives: nc.Objectives, - } +func (r *rpc) Declare(_ context.Context, req *connect.Request[metricsV1.DeclareRequest]) (*connect.Response[metricsV1.Response], error) { + const op = errors.Op("metrics_rpc_declare") - if len(nc.Labels) != 0 { - promCol = prometheus.NewSummaryVec(opts, nc.Labels) - } else { - promCol = prometheus.NewSummary(opts) - } + nc := req.Msg.GetCollector() + r.p.mu.Lock() + defer r.p.mu.Unlock() - default: - return errors.E(op, errors.Errorf("unknown collector type %s", nc.Type)) + r.log.Debug("declaring new metric", "name", nc.GetName(), "type", nc.GetCollector().GetType(), "namespace", nc.GetCollector().GetNamespace()) + if _, exist := r.p.collectors.Load(nc.GetName()); exist { + r.log.Warn("metric with provided name already exist", "name", nc.GetName()) + return connect.NewResponse(&metricsV1.Response{Ok: true}), nil } - // that method might panic, we handle it by recover - err := r.p.Register(promCol) + promCol, err := buildPromCollector(nc) if err != nil { - *ok = false - return errors.E(op, err) + return nil, connect.NewError(connect.CodeInvalidArgument, errors.E(op, err)) } - col := &collector{ - col: promCol, - registered: true, + if err := r.p.Register(promCol); err != nil { + return nil, connect.NewError(connect.CodeInternal, errors.E(op, err)) } - // add collector to sync.Map - r.p.collectors.Store(nc.Name, col) - - r.log.Debug("metric successfully added", "name", nc.Name, "type", nc.Type, "namespace", nc.Namespace) - - *ok = true - return nil + r.p.collectors.Store(nc.GetName(), &collector{col: promCol, registered: true}) + r.log.Debug("metric successfully added", "name", nc.GetName(), "type", nc.GetCollector().GetType(), "namespace", nc.GetCollector().GetNamespace()) + return connect.NewResponse(&metricsV1.Response{Ok: true}), nil } -// Unregister removes collector from the prometheus registry -func (r *rpc) Unregister(name string, ok *bool) error { - const op = errors.Op("metrics_plugin_unregister") - +func (r *rpc) Unregister(_ context.Context, req *connect.Request[metricsV1.UnregisterRequest]) (*connect.Response[metricsV1.Response], error) { + name := req.Msg.GetName() r.log.Debug("unregistering collector", "name", name) c, exist := r.p.collectors.LoadAndDelete(name) if !exist || c == nil { - return errors.E(op, errors.Errorf("undefined collector %s", name)) + return nil, connect.NewError(connect.CodeNotFound, fmt.Errorf("%w: %s", errUndefinedCollector, name)) } - if col, k := c.(*collector); k { - if r.p.registry.Unregister(col.col) { - *ok = true - r.log.Debug("collector was successfully unregistered", "name", name) - return nil - } - - r.log.Debug("collector was deleted from the RR registry but not from the prometheus collector", "name", name) + col, ok := c.(*collector) + if !ok { + return nil, connect.NewError(connect.CodeInternal, fmt.Errorf("collectors map held non-*collector for %s", name)) } - - return nil + if r.p.registry.Unregister(col.col) { + r.log.Debug("collector was successfully unregistered", "name", name) + return connect.NewResponse(&metricsV1.Response{Ok: true}), nil + } + // Preserves legacy contract: prometheus refused to unregister (already + // gone, or never registered there). The collector is removed from our map + // either way, but the caller deserves to know prometheus state diverged. + r.log.Debug("collector was deleted from the RR registry but not from the prometheus collector", "name", name) + return connect.NewResponse(&metricsV1.Response{Ok: false}), nil } -// Set the metric value (only for gaude). -func (r *rpc) Set(m *Metric, ok *bool) (err error) { - const op = errors.Op("metrics_plugin_set") - r.log.Debug("observing metric", "name", m.Name, "value", m.Value, "labels", m.Labels) - - c, exist := r.p.collectors.Load(m.Name) - if !exist { - return errors.E(op, errors.Errorf("undefined collector %s", m.Name)) +func (r *rpc) lookupCollector(name string) (prometheus.Collector, connect.Code, error) { + c, exist := r.p.collectors.Load(name) + if !exist || c == nil { + r.log.Error("undefined collector", "collector", name) + return nil, connect.CodeNotFound, fmt.Errorf("%w: %s", errUndefinedCollector, name) } - if c == nil { - return errors.E(op, errors.Errorf("undefined collector %s", m.Name)) + col, ok := c.(*collector) + if !ok { + return nil, connect.CodeInternal, fmt.Errorf("collectors map held non-*collector for %s", name) } + return col.col, 0, nil +} - col := c.(*collector) - - switch c := col.col.(type) { - case prometheus.Gauge: - c.Set(m.Value) +func vecMetric[V any, T interface { + GetMetricWithLabelValues(lvs ...string) (V, error) +}](r *rpc, c T, m *metricsV1.Metric) (V, error) { + var zero V + if len(m.GetLabels()) == 0 { + r.log.Error("required labels for collector", "collector", m.GetName()) + return zero, connect.NewError(connect.CodeInvalidArgument, + fmt.Errorf("%w: %s", errRequiredLabels, m.GetName())) + } + v, err := c.GetMetricWithLabelValues(m.GetLabels()...) + if err != nil { + r.log.Error("failed to get metrics with label values", "collector", m.GetName(), "labels", m.GetLabels()) + return zero, connect.NewError(connect.CodeInvalidArgument, err) + } + return v, nil +} - case *prometheus.GaugeVec: - if len(m.Labels) == 0 { - r.log.Error("required labels for collector", "collector", m.Name) - return errors.E(op, errors.Errorf("required labels for collector %s", m.Name)) +func buildPromCollector(nc *metricsV1.NamedCollector) (prometheus.Collector, error) { + col := nc.GetCollector() + switch col.GetType() { + case metricsV1.CollectorType_COLLECTOR_TYPE_HISTOGRAM: + opts := prometheus.HistogramOpts{Name: nc.GetName(), Namespace: col.GetNamespace(), Subsystem: col.GetSubsystem(), Help: col.GetHelp(), Buckets: col.GetBuckets()} + if len(col.GetLabels()) != 0 { + return prometheus.NewHistogramVec(opts, col.GetLabels()), nil } - gauge, err := c.GetMetricWithLabelValues(m.Labels...) - if err != nil { - r.log.Error("failed to get metrics with label values", "collector", m.Name, "labels", m.Labels) - return errors.E(op, err) + return prometheus.NewHistogram(opts), nil + case metricsV1.CollectorType_COLLECTOR_TYPE_GAUGE: + opts := prometheus.GaugeOpts{Name: nc.GetName(), Namespace: col.GetNamespace(), Subsystem: col.GetSubsystem(), Help: col.GetHelp()} + if len(col.GetLabels()) != 0 { + return prometheus.NewGaugeVec(opts, col.GetLabels()), nil } - gauge.Set(m.Value) - + return prometheus.NewGauge(opts), nil + case metricsV1.CollectorType_COLLECTOR_TYPE_COUNTER: + opts := prometheus.CounterOpts{Name: nc.GetName(), Namespace: col.GetNamespace(), Subsystem: col.GetSubsystem(), Help: col.GetHelp()} + if len(col.GetLabels()) != 0 { + return prometheus.NewCounterVec(opts, col.GetLabels()), nil + } + return prometheus.NewCounter(opts), nil + case metricsV1.CollectorType_COLLECTOR_TYPE_SUMMARY: + objectives := make(map[float64]float64, len(col.GetObjectives())) + for _, o := range col.GetObjectives() { + objectives[o.GetQuantile()] = o.GetError() + } + opts := prometheus.SummaryOpts{Name: nc.GetName(), Namespace: col.GetNamespace(), Subsystem: col.GetSubsystem(), Help: col.GetHelp(), Objectives: objectives} + if len(col.GetLabels()) != 0 { + return prometheus.NewSummaryVec(opts, col.GetLabels()), nil + } + return prometheus.NewSummary(opts), nil + case metricsV1.CollectorType_COLLECTOR_TYPE_UNSPECIFIED: + return nil, fmt.Errorf("%w: unspecified", errUnknownCollectorTyp) default: - return errors.E(op, errors.Errorf("collector `%s` does not support method Set", m.Name)) + return nil, fmt.Errorf("%w: %v", errUnknownCollectorTyp, col.GetType()) } - - r.log.Debug("set operation finished successfully", "name", m.Name, "labels", m.Labels, "value", m.Value) - - *ok = true - return nil } diff --git a/rpc_test.go b/rpc_test.go deleted file mode 100644 index 2989797..0000000 --- a/rpc_test.go +++ /dev/null @@ -1,65 +0,0 @@ -package metrics - -import ( - "encoding/json" - "reflect" - "testing" - - "github.com/vmihailenco/msgpack/v5" -) - -type unmarshal func([]byte, any) error - -func Test_Metric_Unmarshal(t *testing.T) { - tests := []struct { - name string - unmarshaler unmarshal - payload []byte - want Metric - }{ - {"json", - json.Unmarshal, - []byte(`{"name":"test","value":123,"labels": ["test1", "test2"]}`), - Metric{ - Name: "test", - Value: 123, - Labels: []string{"test1", "test2"}, - }, - }, - { - "msgpack", - msgpack.Unmarshal, - // {"name":"test","value":123,"labels": ["test1", "test2"]} - []byte{0x83, 0xa4, 0x6e, 0x61, 0x6d, 0x65, 0xa4, 0x74, 0x65, 0x73, 0x74, 0xa5, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x7b, 0xa6, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x92, 0xa5, 0x74, 0x65, 0x73, 0x74, 0x31, 0xa5, 0x74, 0x65, 0x73, 0x74, 0x32}, - Metric{ - Name: "test", - Value: 123, - Labels: []string{"test1", "test2"}, - }, - }, - { - "msgpack pascal case", - msgpack.Unmarshal, - // {"Name":"test","Value":123,"Labels": ["test1", "test2"]} - []byte{0x83, 0xa4, 0x4e, 0x61, 0x6d, 0x65, 0xa4, 0x74, 0x65, 0x73, 0x74, 0xa5, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x7b, 0xa6, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x92, 0xa5, 0x74, 0x65, 0x73, 0x74, 0x31, 0xa5, 0x74, 0x65, 0x73, 0x74, 0x32}, - Metric{ - Name: "test", - Value: 123, - Labels: []string{"test1", "test2"}, - }, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - got := Metric{} - err := test.unmarshaler(test.payload, &got) - if err != nil { - t.Error(err) - return - } - if !reflect.DeepEqual(test.want, got) { - t.Errorf("Want: %v, Got: %v", test.want, got) - } - }) - } -} diff --git a/tests/configs/.rr-metrics-api.yaml b/tests/configs/.rr-metrics-api.yaml new file mode 100644 index 0000000..89c862a --- /dev/null +++ b/tests/configs/.rr-metrics-api.yaml @@ -0,0 +1,15 @@ +version: '3' + +rpc: + listen: tcp://127.0.0.1:6001 + +metrics: + address: "127.0.0.1:2118" + collect: + app_metric_counter: + type: counter + help: "Counter for API tests" + +logs: + mode: development + level: error diff --git a/tests/go.mod b/tests/go.mod index 37c3f8a..639b5d7 100644 --- a/tests/go.mod +++ b/tests/go.mod @@ -5,22 +5,27 @@ go 1.26 toolchain go1.26.0 require ( + connectrpc.com/connect v1.19.2 github.com/prometheus/client_golang v1.23.2 + github.com/roadrunner-server/api-go/v6 v6.0.0-beta.9 github.com/roadrunner-server/config/v6 v6.0.0-beta.3 github.com/roadrunner-server/endure/v2 v2.6.2 - github.com/roadrunner-server/goridge/v4 v4.0.0-beta.1 - github.com/roadrunner-server/http/v6 v6.0.0-beta.6 + github.com/roadrunner-server/http/v6 v6.0.0-beta.7 github.com/roadrunner-server/logger/v6 v6.0.0-beta.3 github.com/roadrunner-server/metrics/v6 v6.0.0 github.com/roadrunner-server/prometheus/v6 v6.0.0-beta.2 - github.com/roadrunner-server/rpc/v6 v6.0.0-beta.3 - github.com/roadrunner-server/server/v6 v6.0.0-beta.5 + github.com/roadrunner-server/rpc/v6 v6.0.0-beta.4 + github.com/roadrunner-server/server/v6 v6.0.0-beta.6 github.com/stretchr/testify v1.11.1 + golang.org/x/net v0.54.0 + google.golang.org/grpc v1.81.0 + google.golang.org/protobuf v1.36.11 ) replace github.com/roadrunner-server/metrics/v6 => ../ require ( + connectrpc.com/grpcreflect v1.3.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/caddyserver/certmagic v0.25.3 // indirect github.com/caddyserver/zerossl v0.1.5 // indirect @@ -49,12 +54,12 @@ require ( github.com/prometheus/common v0.67.5 // indirect github.com/prometheus/procfs v0.20.1 // indirect github.com/quic-go/qpack v0.6.0 // indirect - github.com/quic-go/quic-go v0.59.0 // indirect - github.com/roadrunner-server/api-go/v6 v6.0.0-beta.4 // indirect + github.com/quic-go/quic-go v0.59.1 // indirect github.com/roadrunner-server/api-plugins/v6 v6.0.0-beta.2 // indirect github.com/roadrunner-server/context v1.3.0 // indirect github.com/roadrunner-server/errors v1.5.0 // indirect github.com/roadrunner-server/events v1.0.1 // indirect + github.com/roadrunner-server/goridge/v4 v4.0.0-beta.2 // indirect github.com/roadrunner-server/pool/v2 v2.0.0-beta.1 // indirect github.com/roadrunner-server/tcplisten v1.5.2 // indirect github.com/sagikazarmark/locafero v0.12.0 // indirect @@ -64,8 +69,8 @@ require ( github.com/spf13/pflag v1.0.10 // indirect github.com/spf13/viper v1.21.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect - github.com/tklauser/go-sysconf v0.3.16 // indirect - github.com/tklauser/numcpus v0.11.0 // indirect + github.com/tklauser/go-sysconf v0.4.0 // indirect + github.com/tklauser/numcpus v0.12.0 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect github.com/zeebo/assert v1.3.1 // indirect github.com/zeebo/blake3 v0.2.4 // indirect @@ -82,14 +87,11 @@ require ( go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/crypto v0.51.0 // indirect golang.org/x/mod v0.36.0 // indirect - golang.org/x/net v0.54.0 // indirect golang.org/x/sync v0.20.0 // indirect golang.org/x/sys v0.44.0 // indirect golang.org/x/text v0.37.0 // indirect golang.org/x/tools v0.45.0 // indirect - google.golang.org/genproto v0.0.0-20260504160031-60b97b32f348 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260504160031-60b97b32f348 // indirect - google.golang.org/grpc v1.81.0 // indirect - google.golang.org/protobuf v1.36.11 // indirect + google.golang.org/genproto v0.0.0-20260511170946-3700d4141b60 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260511170946-3700d4141b60 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/tests/go.sum b/tests/go.sum index 1a82496..bd6e84e 100644 --- a/tests/go.sum +++ b/tests/go.sum @@ -1,5 +1,9 @@ code.pfad.fr/check v1.1.0 h1:GWvjdzhSEgHvEHe2uJujDcpmZoySKuHQNrZMfzfO0bE= code.pfad.fr/check v1.1.0/go.mod h1:NiUH13DtYsb7xp5wll0U4SXx7KhXQVCtRgdC96IPfoM= +connectrpc.com/connect v1.19.2 h1:McQ83FGdzL+t60peksi0gXC7MQ/iLKgLduAnThbM0mo= +connectrpc.com/connect v1.19.2/go.mod h1:tN20fjdGlewnSFeZxLKb0xwIZ6ozc3OQs2hTXy4du9w= +connectrpc.com/grpcreflect v1.3.0 h1:Y4V+ACf8/vOb1XOc251Qun7jMB75gCUNw6llvB9csXc= +connectrpc.com/grpcreflect v1.3.0/go.mod h1:nfloOtCS8VUQOQ1+GTdFzVg2CJo4ZGaat8JIovCtDYs= 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/caddyserver/certmagic v0.25.3 h1:mGf5ba8F7xA4c5jfDZZbK2buY1VEkbnwpMDixaju94A= @@ -80,10 +84,10 @@ github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEy github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= -github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw= -github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU= -github.com/roadrunner-server/api-go/v6 v6.0.0-beta.4 h1:wX8IezPUeeBJzlzaBEFSZBE5Bc/Le1Uf/GdFRdFO3HQ= -github.com/roadrunner-server/api-go/v6 v6.0.0-beta.4/go.mod h1:jI30i64yCAxJh7KHc8e1B8NgDcvcnSTI1OIK8lTE+Y0= +github.com/quic-go/quic-go v0.59.1 h1:0Gmua0HW1Tv7ANR7hUYwRyD0MG5OJfgvYSZasGZzBic= +github.com/quic-go/quic-go v0.59.1/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU= +github.com/roadrunner-server/api-go/v6 v6.0.0-beta.9 h1:iD09+1Sc6ZMxEOJsj7YIblzEN1ORZ+NLH7YYO0nqASs= +github.com/roadrunner-server/api-go/v6 v6.0.0-beta.9/go.mod h1:4BbPXAqT0sOG2EVRcymESMwxaKSpHl4hG+UDoCepWu0= github.com/roadrunner-server/api-plugins/v6 v6.0.0-beta.2 h1:GqsZzWQ5jMXRF1O/b8IqFz9PLpS7Ui0K4OyACLql2MI= github.com/roadrunner-server/api-plugins/v6 v6.0.0-beta.2/go.mod h1:2v4yUK5Kvbvq8C3IkDoBkuamq9h+7i/JLjyf7k1j5JM= github.com/roadrunner-server/config/v6 v6.0.0-beta.3 h1:G0EUzJ6Yw4UnleM6BhnOBbYPXKDHRmCJiGhC3nXDBwI= @@ -96,20 +100,20 @@ github.com/roadrunner-server/errors v1.5.0 h1:unG7LKIZrSzkCCF3YLRLA5VyqE0KKomofX github.com/roadrunner-server/errors v1.5.0/go.mod h1:g9fo/T2C13cWRDR9PW1r0ZAOSQfNhWAZawyfkGiaHuI= github.com/roadrunner-server/events v1.0.1 h1:waCkKhxhzdK3VcI1xG22l+h+0J+Nfdpxjhyy01Un+kI= github.com/roadrunner-server/events v1.0.1/go.mod h1:WZRqoEVaFm209t52EuoT7ISUtvX6BrCi6bI/7pjkVC0= -github.com/roadrunner-server/goridge/v4 v4.0.0-beta.1 h1:dO1wKnuMr4xMmH6DY2ZaZ6FWS+Vo50+C7fuAcyO/xBk= -github.com/roadrunner-server/goridge/v4 v4.0.0-beta.1/go.mod h1:+gKla9HAyYlk0TsC9VktwtOL63aimsWT3oPsuCLh4/o= -github.com/roadrunner-server/http/v6 v6.0.0-beta.6 h1:Bhb99PMomKm3Vjn7MtsrEsZaH71yfJBAUw/gOwozXTE= -github.com/roadrunner-server/http/v6 v6.0.0-beta.6/go.mod h1:FFEYeboAk0EmdnjTM6GB2bMnpvVqWks69gctxFtZmn4= +github.com/roadrunner-server/goridge/v4 v4.0.0-beta.2 h1:MgH6oiSgcl+vphsQ6JpyedkXQ/DPf8zVpn0z7rdBp10= +github.com/roadrunner-server/goridge/v4 v4.0.0-beta.2/go.mod h1:Wv9CBO9VIU92e5iZIuehLHKakXgMkOzxoT4/oHDjIUA= +github.com/roadrunner-server/http/v6 v6.0.0-beta.7 h1:uCKQBlD5gOCuGlJQA4h4q+IDK5C3tSGBPCPTUJjRztY= +github.com/roadrunner-server/http/v6 v6.0.0-beta.7/go.mod h1:T5XNZsqAsUMozEthOD5PmRhQYl7HYx7JqV7NAuA459Q= github.com/roadrunner-server/logger/v6 v6.0.0-beta.3 h1:eoJKXAUSyykDfVX6eTUhmAn6Y8pS/LyI5fDP4H+G5rQ= github.com/roadrunner-server/logger/v6 v6.0.0-beta.3/go.mod h1:MwHb3AbltHYtu7nRpml5NeYu7O+W8rCpDBeNTTEoE1M= github.com/roadrunner-server/pool/v2 v2.0.0-beta.1 h1:jpYXFtdD6QGAdAGPgMxrNi3j1CegCRpb2y+A+3GnXFA= github.com/roadrunner-server/pool/v2 v2.0.0-beta.1/go.mod h1:Bo1wT7RtL3eyQHXBUohNhtj/yAmRt6Rq8smuBg5pWkY= github.com/roadrunner-server/prometheus/v6 v6.0.0-beta.2 h1:e6Z5YFRwi1Tcr9T5sgecfqcOUo3XC7fTkcoGVKP6mnw= github.com/roadrunner-server/prometheus/v6 v6.0.0-beta.2/go.mod h1:OYfupkw6fQjZHf83T87KUXNx4/IwCMlBA3J3sqDx5Wg= -github.com/roadrunner-server/rpc/v6 v6.0.0-beta.3 h1:hvVEDIMB9MKI8uWX++MrBzHRzq404ygU0fDs6U2V/3Y= -github.com/roadrunner-server/rpc/v6 v6.0.0-beta.3/go.mod h1:BpDpd2/UceDdsDJNP0iMfmegbXthxiZM4MU6GOJoSXo= -github.com/roadrunner-server/server/v6 v6.0.0-beta.5 h1:poCPSHc768UtMqJRgW3rZT5pDboOnwOD4qc28hhXJ4I= -github.com/roadrunner-server/server/v6 v6.0.0-beta.5/go.mod h1:SbODuCzC2gcbFhAmJDWvjf34pPrUWP5NxxVsTRQDuZ4= +github.com/roadrunner-server/rpc/v6 v6.0.0-beta.4 h1:Qj2nrHIWOHE9Tys+FBG2IdoPtzgIUh6juQ5wXLGGDMw= +github.com/roadrunner-server/rpc/v6 v6.0.0-beta.4/go.mod h1:k5KT3fpnJVd27m0HbGGBiTPXlWI6eJdd6C+ohp5IE0U= +github.com/roadrunner-server/server/v6 v6.0.0-beta.6 h1:CPtH4eIYkeRKi5cPXxb0+J+LI824cqhIGXAfcH+nkjA= +github.com/roadrunner-server/server/v6 v6.0.0-beta.6/go.mod h1:SbODuCzC2gcbFhAmJDWvjf34pPrUWP5NxxVsTRQDuZ4= github.com/roadrunner-server/tcplisten v1.5.2 h1:nn8yXYrhRDkfQ9AAu4V075uT4fZRmOnpxkawgE+bWPA= github.com/roadrunner-server/tcplisten v1.5.2/go.mod h1:DufGBz7Dlx2KrNe/4RukEvGMTqZKB0Uve1GztwcyyR8= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= @@ -130,14 +134,10 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu 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/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= -github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= -github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= -github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ= -github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= -github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= -github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= -github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= +github.com/tklauser/go-sysconf v0.4.0 h1:7H0uAN+7RkwWRaxhYXDLqa5V3LPrJeV8wmD9dRUgPQU= +github.com/tklauser/go-sysconf v0.4.0/go.mod h1:8mTNWyog7H+MpKijp4VmKJAd2bbYQ2zuUwkYRbUArPI= +github.com/tklauser/numcpus v0.12.0 h1:NR85qdvHA9pFse3x3weVZ0r0ST8R6l5RHbZrlRaqob4= +github.com/tklauser/numcpus v0.12.0/go.mod h1:ABHeXzJnr/qqwguhClkZKT1/8VABcYrsyUiUGobwWJg= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= github.com/zeebo/assert v1.3.1 h1:vukIABvugfNMZMQO1ABsyQDJDTVQbn+LWSMy1ol1h6A= @@ -194,10 +194,10 @@ golang.org/x/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8= golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0= gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= -google.golang.org/genproto v0.0.0-20260504160031-60b97b32f348 h1:JjVGDZYWkJWZcxveJGzfkXC5myDVWAd4dZdgbzrDUv8= -google.golang.org/genproto v0.0.0-20260504160031-60b97b32f348/go.mod h1:95PqD4xM+AdOcBGsmgfaofXsiA37uXDtDufVbntT3TU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260504160031-60b97b32f348 h1:pfIbyB44sWzHiCpRqIen67ZQnVXSfIxWrqUMk1qwODE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260504160031-60b97b32f348/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/genproto v0.0.0-20260511170946-3700d4141b60 h1:rhBdfmsOlOZIvz3Y5/BdUzPg2CkO8L7QQPKj96B8554= +google.golang.org/genproto v0.0.0-20260511170946-3700d4141b60/go.mod h1:8xo2Pj1b20ZOCpzlU3B9qieMwVIAXx1QVZWLMlPL6sM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260511170946-3700d4141b60 h1:seT2EwLWM78plQ7wcDfuWBc/4FAEAXDDiaSol4ku4qo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260511170946-3700d4141b60/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= google.golang.org/grpc v1.81.0 h1:W3G9N3KQf3BU+YuCtGKJk0CmxQNbAISICD/9AORxLIw= google.golang.org/grpc v1.81.0/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= diff --git a/tests/metrics_api_test.go b/tests/metrics_api_test.go new file mode 100644 index 0000000..75906ce --- /dev/null +++ b/tests/metrics_api_test.go @@ -0,0 +1,315 @@ +package metrics + +import ( + "bytes" + "context" + "crypto/tls" + "encoding/base64" + "io" + "log/slog" + "net" + "net/http" + "net/url" + "sync" + "testing" + "time" + + "connectrpc.com/connect" + metricsV1 "github.com/roadrunner-server/api-go/v6/metrics/v1" + "github.com/roadrunner-server/api-go/v6/metrics/v1/metricsV1connect" + "github.com/roadrunner-server/config/v6" + "github.com/roadrunner-server/endure/v2" + "github.com/roadrunner-server/logger/v6" + "github.com/roadrunner-server/metrics/v6" + rpcPlugin "github.com/roadrunner-server/rpc/v6" + "github.com/stretchr/testify/require" + "golang.org/x/net/http2" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/proto" +) + +const metricsAPIAddr = "127.0.0.1:6001" + +// startMetricsAPIContainer brings up rpc + metrics + logger with one pre- +// configured counter (`app_metric_counter`) — the API tests exercise the +// wire surface against this collector and against runtime-declared ones. +func startMetricsAPIContainer(t *testing.T) func() { + t.Helper() + + cont := endure.New(slog.LevelError) + cfg := &config.Plugin{ + Version: "2024.2.0", + Path: "configs/.rr-metrics-api.yaml", + } + + require.NoError(t, cont.RegisterAll( + cfg, + &logger.Plugin{}, + &rpcPlugin.Plugin{}, + &metrics.Plugin{}, + )) + require.NoError(t, cont.Init()) + + ch, err := cont.Serve() + require.NoError(t, err) + + wg := &sync.WaitGroup{} + stop := make(chan struct{}) + wg.Go(func() { + select { + case e := <-ch: + t.Errorf("container reported error: %v", e.Error) + case <-stop: + } + }) + + time.Sleep(500 * time.Millisecond) + + return func() { + close(stop) + require.NoError(t, cont.Stop()) + wg.Wait() + } +} + +func TestMetricsConnectAPI(t *testing.T) { + stop := startMetricsAPIContainer(t) + defer stop() + + httpc := &http.Client{ + Timeout: 30 * time.Second, + Transport: &http2.Transport{ + AllowHTTP: true, + DialTLSContext: func(ctx context.Context, network, addr string, _ *tls.Config) (net.Conn, error) { + return (&net.Dialer{Timeout: 30 * time.Second}).DialContext(ctx, network, addr) + }, + }, + } + client := metricsV1connect.NewMetricsServiceClient(httpc, "http://"+metricsAPIAddr) + ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second) + defer cancel() + + // Declare a new gauge, set it, then read it back via Add. + declareResp, err := client.Declare(ctx, connect.NewRequest(&metricsV1.DeclareRequest{ + Collector: &metricsV1.NamedCollector{ + Name: "api_gauge", + Collector: &metricsV1.Collector{ + Type: metricsV1.CollectorType_COLLECTOR_TYPE_GAUGE, + Help: "API test gauge", + }, + }, + })) + require.NoError(t, err) + require.True(t, declareResp.Msg.GetOk()) + + setResp, err := client.Set(ctx, connect.NewRequest(&metricsV1.SetRequest{ + Metric: &metricsV1.Metric{Name: "api_gauge", Value: 42.0}, + })) + require.NoError(t, err) + require.True(t, setResp.Msg.GetOk()) + + // Unknown collector → CodeNotFound. + _, err = client.Add(ctx, connect.NewRequest(&metricsV1.AddRequest{ + Metric: &metricsV1.Metric{Name: "does-not-exist", Value: 1}, + })) + require.Error(t, err) + require.Equal(t, connect.CodeNotFound, connect.CodeOf(err)) + + // Operation unsupported by collector kind → CodeFailedPrecondition. + // Histogram doesn't support Set. + _, err = client.Declare(ctx, connect.NewRequest(&metricsV1.DeclareRequest{ + Collector: &metricsV1.NamedCollector{ + Name: "api_histogram", + Collector: &metricsV1.Collector{ + Type: metricsV1.CollectorType_COLLECTOR_TYPE_HISTOGRAM, + Help: "API test histogram", + Buckets: []float64{0.1, 1.0}, + }, + }, + })) + require.NoError(t, err) + _, err = client.Set(ctx, connect.NewRequest(&metricsV1.SetRequest{ + Metric: &metricsV1.Metric{Name: "api_histogram", Value: 0.5}, + })) + require.Error(t, err) + require.Equal(t, connect.CodeFailedPrecondition, connect.CodeOf(err)) + + // Unregister an existing collector → ok. + unregResp, err := client.Unregister(ctx, connect.NewRequest(&metricsV1.UnregisterRequest{Name: "api_gauge"})) + require.NoError(t, err) + require.True(t, unregResp.Msg.GetOk()) + + // Unregister a missing collector → CodeNotFound. + _, err = client.Unregister(ctx, connect.NewRequest(&metricsV1.UnregisterRequest{Name: "missing"})) + require.Error(t, err) + require.Equal(t, connect.CodeNotFound, connect.CodeOf(err)) + + // Declare with an unspecified collector type → CodeInvalidArgument (the + // zero value when the proto Type field is omitted). + _, err = client.Declare(ctx, connect.NewRequest(&metricsV1.DeclareRequest{ + Collector: &metricsV1.NamedCollector{ + Name: "api_unspecified", + Collector: &metricsV1.Collector{Help: "no type"}, + }, + })) + require.Error(t, err) + require.Equal(t, connect.CodeInvalidArgument, connect.CodeOf(err)) + + // SummaryVec Observe — declare a summary with labels + objectives, then + // observe with the right label values. Locks in the SummaryVec branch of + // the rpc.go Observe type-switch. + _, err = client.Declare(ctx, connect.NewRequest(&metricsV1.DeclareRequest{ + Collector: &metricsV1.NamedCollector{ + Name: "api_summary_vec", + Collector: &metricsV1.Collector{ + Type: metricsV1.CollectorType_COLLECTOR_TYPE_SUMMARY, + Help: "API test summary", + Labels: []string{"region"}, + Objectives: []*metricsV1.Objective{{Quantile: 0.5, Error: 0.05}, {Quantile: 0.99, Error: 0.001}}, + }, + }, + })) + require.NoError(t, err) + obsResp, err := client.Observe(ctx, connect.NewRequest(&metricsV1.ObserveRequest{ + Metric: &metricsV1.Metric{Name: "api_summary_vec", Value: 0.42, Labels: []string{"eu"}}, + })) + require.NoError(t, err) + require.True(t, obsResp.Msg.GetOk()) + + // Scalar Summary Observe — locks in the prometheus.Summary branch of + // the type-switch (buildPromCollector returns NewSummary when labels are + // empty, distinct from NewSummaryVec). + _, err = client.Declare(ctx, connect.NewRequest(&metricsV1.DeclareRequest{ + Collector: &metricsV1.NamedCollector{ + Name: "api_summary_scalar", + Collector: &metricsV1.Collector{ + Type: metricsV1.CollectorType_COLLECTOR_TYPE_SUMMARY, + Help: "API test scalar summary", + Objectives: []*metricsV1.Objective{{Quantile: 0.95, Error: 0.01}}, + }, + }, + })) + require.NoError(t, err) + scalarResp, err := client.Observe(ctx, connect.NewRequest(&metricsV1.ObserveRequest{ + Metric: &metricsV1.Metric{Name: "api_summary_scalar", Value: 1.23}, + })) + require.NoError(t, err) + require.True(t, scalarResp.Msg.GetOk()) +} + +// TestMetricsHTTPApi exercises the metrics RPCs through plain HTTP/1.1 with +// a protojson body — the shape any non-Connect HTTP client uses against this +// handler. +func TestMetricsHTTPApi(t *testing.T) { + stop := startMetricsAPIContainer(t) + defer stop() + + httpc := &http.Client{Timeout: 30 * time.Second} + ctx := t.Context() + + call := func(method string, in proto.Message, out proto.Message) { + body, err := protojson.Marshal(in) + require.NoError(t, err) + + req, err := http.NewRequestWithContext(ctx, http.MethodPost, + "http://"+metricsAPIAddr+"/metrics.v1.MetricsService/"+method, bytes.NewReader(body)) + require.NoError(t, err) + req.Header.Set("Content-Type", "application/json") + + resp, err := httpc.Do(req) + require.NoError(t, err) + defer func() { _ = resp.Body.Close() }() + + respBody, err := io.ReadAll(resp.Body) + require.NoError(t, err) + require.Equalf(t, http.StatusOK, resp.StatusCode, "method=%s body=%s", method, respBody) + require.NoError(t, protojson.Unmarshal(respBody, out)) + } + + var declareResp metricsV1.Response + call("Declare", &metricsV1.DeclareRequest{ + Collector: &metricsV1.NamedCollector{ + Name: "http_counter", + Collector: &metricsV1.Collector{ + Type: metricsV1.CollectorType_COLLECTOR_TYPE_COUNTER, + Help: "HTTP API test counter", + }, + }, + }, &declareResp) + require.True(t, declareResp.GetOk()) + + var addResp metricsV1.Response + call("Add", &metricsV1.AddRequest{ + Metric: &metricsV1.Metric{Name: "http_counter", Value: 7.0}, + }, &addResp) + require.True(t, addResp.GetOk()) +} + +// TestMetricsGRPCApi exercises the metrics RPCs through a regular gRPC client. +// The same Connect handler serves gRPC framing off the same port. +func TestMetricsGRPCApi(t *testing.T) { + stop := startMetricsAPIContainer(t) + defer stop() + + conn, err := grpc.NewClient(metricsAPIAddr, grpc.WithTransportCredentials(insecure.NewCredentials())) + require.NoError(t, err) + defer func() { _ = conn.Close() }() + + client := metricsV1.NewMetricsServiceClient(conn) + ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second) + defer cancel() + + declareResp, err := client.Declare(ctx, &metricsV1.DeclareRequest{ + Collector: &metricsV1.NamedCollector{ + Name: "grpc_counter", + Collector: &metricsV1.Collector{ + Type: metricsV1.CollectorType_COLLECTOR_TYPE_COUNTER, + Help: "gRPC API test counter", + }, + }, + }) + require.NoError(t, err) + require.True(t, declareResp.GetOk()) + + addResp, err := client.Add(ctx, &metricsV1.AddRequest{ + Metric: &metricsV1.Metric{Name: "grpc_counter", Value: 3.0}, + }) + require.NoError(t, err) + require.True(t, addResp.GetOk()) +} + +// TestMetricsHTTPGetIdempotency verifies that all six metrics RPCs reject +// HTTP GET — none of them are marked NO_SIDE_EFFECTS in the proto (every +// method mutates Prometheus state in some way). +func TestMetricsHTTPGetIdempotency(t *testing.T) { + stop := startMetricsAPIContainer(t) + defer stop() + + body, err := protojson.Marshal(&metricsV1.AddRequest{Metric: &metricsV1.Metric{Name: "probe"}}) + require.NoError(t, err) + + q := url.Values{} + q.Set("encoding", "json") + q.Set("base64", "1") + q.Set("message", base64.URLEncoding.EncodeToString(body)) + + methods := []string{"Add", "Sub", "Observe", "Set", "Declare", "Unregister"} + + httpc := &http.Client{Timeout: 30 * time.Second} + for _, m := range methods { + t.Run(m, func(t *testing.T) { + req, err := http.NewRequestWithContext(t.Context(), http.MethodGet, + "http://"+metricsAPIAddr+"/metrics.v1.MetricsService/"+m+"?"+q.Encode(), nil) + require.NoError(t, err) + + resp, err := httpc.Do(req) + require.NoError(t, err) + defer func() { _ = resp.Body.Close() }() + + require.Equalf(t, http.StatusMethodNotAllowed, resp.StatusCode, + "%s via GET should be rejected; got %s", m, resp.Status) + }) + } +} diff --git a/tests/metrics_test.go b/tests/metrics_test.go index a2dad81..96b5d41 100644 --- a/tests/metrics_test.go +++ b/tests/metrics_test.go @@ -1,11 +1,12 @@ package metrics import ( + "context" + "crypto/tls" "io" "log/slog" "net" "net/http" - "net/rpc" "os" "os/signal" "sync" @@ -13,9 +14,13 @@ import ( "testing" "time" + mocklogger "tests/mock" + + "connectrpc.com/connect" + metricsV1 "github.com/roadrunner-server/api-go/v6/metrics/v1" + "github.com/roadrunner-server/api-go/v6/metrics/v1/metricsV1connect" "github.com/roadrunner-server/config/v6" "github.com/roadrunner-server/endure/v2" - goridgeRpc "github.com/roadrunner-server/goridge/v4/pkg/rpc" httpPlugin "github.com/roadrunner-server/http/v6" "github.com/roadrunner-server/logger/v6" "github.com/roadrunner-server/metrics/v6" @@ -24,10 +29,51 @@ import ( "github.com/roadrunner-server/server/v6" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - mocklogger "tests/mock" + "golang.org/x/net/http2" ) -const dialNetwork = "tcp" +func newMetricsClient(address string) metricsV1connect.MetricsServiceClient { + httpc := &http.Client{ + Timeout: 30 * time.Second, + Transport: &http2.Transport{ + AllowHTTP: true, + DialTLSContext: func(ctx context.Context, network, addr string, _ *tls.Config) (net.Conn, error) { + return (&net.Dialer{Timeout: 30 * time.Second}).DialContext(ctx, network, addr) + }, + }, + } + return metricsV1connect.NewMetricsServiceClient(httpc, "http://"+address) +} + +func toProtoCollector(nc metrics.NamedCollector) *metricsV1.NamedCollector { + var t metricsV1.CollectorType + switch nc.Type { + case metrics.Histogram: + t = metricsV1.CollectorType_COLLECTOR_TYPE_HISTOGRAM + case metrics.Gauge: + t = metricsV1.CollectorType_COLLECTOR_TYPE_GAUGE + case metrics.Counter: + t = metricsV1.CollectorType_COLLECTOR_TYPE_COUNTER + case metrics.Summary: + t = metricsV1.CollectorType_COLLECTOR_TYPE_SUMMARY + } + objectives := make([]*metricsV1.Objective, 0, len(nc.Objectives)) + for q, e := range nc.Objectives { + objectives = append(objectives, &metricsV1.Objective{Quantile: q, Error: e}) + } + return &metricsV1.NamedCollector{ + Name: nc.Name, + Collector: &metricsV1.Collector{ + Namespace: nc.Namespace, + Subsystem: nc.Subsystem, + Type: t, + Help: nc.Help, + Labels: nc.Labels, + Buckets: nc.Buckets, + Objectives: objectives, + }, + } +} func TestMetricsInit(t *testing.T) { cont := endure.New(slog.LevelDebug) @@ -638,31 +684,19 @@ func TestUpsertOfMetricsDeclaration(t *testing.T) { func configuredCounterMetric(address string) func(t *testing.T) { return func(t *testing.T) { - conn, err := net.Dial(dialNetwork, address) + client := newMetricsClient(address) + resp, err := client.Add(t.Context(), connect.NewRequest(&metricsV1.AddRequest{ + Metric: &metricsV1.Metric{Name: "app_metric_counter", Value: 100.0}, + })) assert.NoError(t, err) - defer func() { - _ = conn.Close() - }() - client := rpc.NewClientWithCodec(goridgeRpc.NewClientCodec(conn)) - var ret bool - - assert.NoError(t, client.Call("metrics.Add", metrics.Metric{ - Name: "app_metric_counter", - Value: 100.0, - }, &ret)) - assert.True(t, ret) + assert.True(t, resp.Msg.GetOk()) } } func observeMetricNotEnoughLabels(address string) func(t *testing.T) { return func(t *testing.T) { - conn, err := net.Dial(dialNetwork, address) - assert.NoError(t, err) - defer func() { - _ = conn.Close() - }() - client := rpc.NewClientWithCodec(goridgeRpc.NewClientCodec(conn)) - var ret bool + client := newMetricsClient(address) + ctx := t.Context() nc := metrics.NamedCollector{ Name: "observe_observeMetricNotEnoughLabels", @@ -675,29 +709,21 @@ func observeMetricNotEnoughLabels(address string) func(t *testing.T) { }, } - err = client.Call("metrics.Declare", nc, &ret) + declareResp, err := client.Declare(ctx, connect.NewRequest(&metricsV1.DeclareRequest{Collector: toProtoCollector(nc)})) assert.NoError(t, err) - assert.True(t, ret) - ret = false - - assert.Error(t, client.Call("metrics.Observe", metrics.Metric{ - Name: "observe_observeMetric", - Value: 100.0, - Labels: []string{"test"}, - }, &ret)) - assert.False(t, ret) + assert.True(t, declareResp.Msg.GetOk()) + + _, err = client.Observe(ctx, connect.NewRequest(&metricsV1.ObserveRequest{ + Metric: &metricsV1.Metric{Name: "observe_observeMetric", Value: 100.0, Labels: []string{"test"}}, + })) + assert.Error(t, err) } } func observeMetric(address string) func(t *testing.T) { return func(t *testing.T) { - conn, err := net.Dial(dialNetwork, address) - assert.NoError(t, err) - defer func() { - _ = conn.Close() - }() - client := rpc.NewClientWithCodec(goridgeRpc.NewClientCodec(conn)) - var ret bool + client := newMetricsClient(address) + ctx := t.Context() nc := metrics.NamedCollector{ Name: "observe_observeMetric", @@ -710,29 +736,22 @@ func observeMetric(address string) func(t *testing.T) { }, } - err = client.Call("metrics.Declare", nc, &ret) + declareResp, err := client.Declare(ctx, connect.NewRequest(&metricsV1.DeclareRequest{Collector: toProtoCollector(nc)})) + assert.NoError(t, err) + assert.True(t, declareResp.Msg.GetOk()) + + obsResp, err := client.Observe(ctx, connect.NewRequest(&metricsV1.ObserveRequest{ + Metric: &metricsV1.Metric{Name: "observe_observeMetric", Value: 100.0, Labels: []string{"test", "test2"}}, + })) assert.NoError(t, err) - assert.True(t, ret) - ret = false - - assert.NoError(t, client.Call("metrics.Observe", metrics.Metric{ - Name: "observe_observeMetric", - Value: 100.0, - Labels: []string{"test", "test2"}, - }, &ret)) - assert.True(t, ret) + assert.True(t, obsResp.Msg.GetOk()) } } func counterMetric(address string) func(t *testing.T) { return func(t *testing.T) { - conn, err := net.Dial(dialNetwork, address) - assert.NoError(t, err) - defer func() { - _ = conn.Close() - }() - client := rpc.NewClientWithCodec(goridgeRpc.NewClientCodec(conn)) - var ret bool + client := newMetricsClient(address) + ctx := t.Context() nc := metrics.NamedCollector{ Name: "counter_CounterMetric", @@ -745,30 +764,22 @@ func counterMetric(address string) func(t *testing.T) { }, } - err = client.Call("metrics.Declare", nc, &ret) + declareResp, err := client.Declare(ctx, connect.NewRequest(&metricsV1.DeclareRequest{Collector: toProtoCollector(nc)})) assert.NoError(t, err) - assert.True(t, ret) - - ret = false + assert.True(t, declareResp.Msg.GetOk()) - assert.NoError(t, client.Call("metrics.Add", metrics.Metric{ - Name: "counter_CounterMetric", - Value: 100.0, - Labels: []string{"type2", "section2"}, - }, &ret)) - assert.True(t, ret) + addResp, err := client.Add(ctx, connect.NewRequest(&metricsV1.AddRequest{ + Metric: &metricsV1.Metric{Name: "counter_CounterMetric", Value: 100.0, Labels: []string{"type2", "section2"}}, + })) + assert.NoError(t, err) + assert.True(t, addResp.Msg.GetOk()) } } func registerHistogram(address string) func(t *testing.T) { return func(t *testing.T) { - conn, err := net.Dial(dialNetwork, address) - assert.NoError(t, err) - defer func() { - _ = conn.Close() - }() - client := rpc.NewClientWithCodec(goridgeRpc.NewClientCodec(conn)) - var ret bool + client := newMetricsClient(address) + ctx := t.Context() nc := metrics.NamedCollector{ Name: "histogram_registerHistogram", @@ -779,34 +790,22 @@ func registerHistogram(address string) func(t *testing.T) { }, } - err = client.Call("metrics.Declare", nc, &ret) + declareResp, err := client.Declare(ctx, connect.NewRequest(&metricsV1.DeclareRequest{Collector: toProtoCollector(nc)})) assert.NoError(t, err) - assert.True(t, ret) - - ret = false + assert.True(t, declareResp.Msg.GetOk()) - m := metrics.Metric{ - Name: "histogram_registerHistogram", - Value: 10000, - Labels: nil, - } - - err = client.Call("metrics.Add", m, &ret) + // Histogram doesn't support Add — must surface as an error. + _, err = client.Add(ctx, connect.NewRequest(&metricsV1.AddRequest{ + Metric: &metricsV1.Metric{Name: "histogram_registerHistogram", Value: 10000}, + })) assert.Error(t, err) - assert.False(t, ret) } } func subVector(address string) func(t *testing.T) { return func(t *testing.T) { - conn, err := net.Dial(dialNetwork, address) - assert.NoError(t, err) - defer func() { - _ = conn.Close() - }() - - client := rpc.NewClientWithCodec(goridgeRpc.NewClientCodec(conn)) - var ret bool + client := newMetricsClient(address) + ctx := t.Context() nc := metrics.NamedCollector{ Name: "sub_gauge_subVector", @@ -818,43 +817,28 @@ func subVector(address string) func(t *testing.T) { }, } - err = client.Call("metrics.Declare", nc, &ret) + declareResp, err := client.Declare(ctx, connect.NewRequest(&metricsV1.DeclareRequest{Collector: toProtoCollector(nc)})) assert.NoError(t, err) - assert.True(t, ret) - ret = false + assert.True(t, declareResp.Msg.GetOk()) - m := metrics.Metric{ - Name: "sub_gauge_subVector", - Value: 100000, - Labels: []string{"core", "first"}, - } - - err = client.Call("metrics.Add", m, &ret) + addResp, err := client.Add(ctx, connect.NewRequest(&metricsV1.AddRequest{ + Metric: &metricsV1.Metric{Name: "sub_gauge_subVector", Value: 100000, Labels: []string{"core", "first"}}, + })) assert.NoError(t, err) - assert.True(t, ret) - ret = false - - m = metrics.Metric{ - Name: "sub_gauge_subVector", - Value: 99999, - Labels: []string{"core", "first"}, - } + assert.True(t, addResp.Msg.GetOk()) - err = client.Call("metrics.Sub", m, &ret) + subResp, err := client.Sub(ctx, connect.NewRequest(&metricsV1.SubRequest{ + Metric: &metricsV1.Metric{Name: "sub_gauge_subVector", Value: 99999, Labels: []string{"core", "first"}}, + })) assert.NoError(t, err) - assert.True(t, ret) + assert.True(t, subResp.Msg.GetOk()) } } func subMetric(address string) func(t *testing.T) { return func(t *testing.T) { - conn, err := net.Dial(dialNetwork, address) - assert.NoError(t, err) - defer func() { - _ = conn.Close() - }() - client := rpc.NewClientWithCodec(goridgeRpc.NewClientCodec(conn)) - var ret bool + client := newMetricsClient(address) + ctx := t.Context() nc := metrics.NamedCollector{ Name: "sub_gauge_subMetric", @@ -865,41 +849,28 @@ func subMetric(address string) func(t *testing.T) { }, } - err = client.Call("metrics.Declare", nc, &ret) + declareResp, err := client.Declare(ctx, connect.NewRequest(&metricsV1.DeclareRequest{Collector: toProtoCollector(nc)})) assert.NoError(t, err) - assert.True(t, ret) - ret = false - - m := metrics.Metric{ - Name: "sub_gauge_subMetric", - Value: 100000, - } + assert.True(t, declareResp.Msg.GetOk()) - err = client.Call("metrics.Add", m, &ret) + addResp, err := client.Add(ctx, connect.NewRequest(&metricsV1.AddRequest{ + Metric: &metricsV1.Metric{Name: "sub_gauge_subMetric", Value: 100000}, + })) assert.NoError(t, err) - assert.True(t, ret) - ret = false - - m = metrics.Metric{ - Name: "sub_gauge_subMetric", - Value: 99999, - } + assert.True(t, addResp.Msg.GetOk()) - err = client.Call("metrics.Sub", m, &ret) + subResp, err := client.Sub(ctx, connect.NewRequest(&metricsV1.SubRequest{ + Metric: &metricsV1.Metric{Name: "sub_gauge_subMetric", Value: 99999}, + })) assert.NoError(t, err) - assert.True(t, ret) + assert.True(t, subResp.Msg.GetOk()) } } func setOnHistogram(address string) func(t *testing.T) { return func(t *testing.T) { - conn, err := net.Dial(dialNetwork, address) - assert.NoError(t, err) - defer func() { - _ = conn.Close() - }() - client := rpc.NewClientWithCodec(goridgeRpc.NewClientCodec(conn)) - var ret bool + client := newMetricsClient(address) + ctx := t.Context() nc := metrics.NamedCollector{ Name: "histogram_setOnHistogram", @@ -911,32 +882,22 @@ func setOnHistogram(address string) func(t *testing.T) { }, } - err = client.Call("metrics.Declare", nc, &ret) + declareResp, err := client.Declare(ctx, connect.NewRequest(&metricsV1.DeclareRequest{Collector: toProtoCollector(nc)})) assert.NoError(t, err) - assert.True(t, ret) - - ret = false - - m := metrics.Metric{ - Name: "gauge_setOnHistogram", - Value: 100.0, - } + assert.True(t, declareResp.Msg.GetOk()) - err = client.Call("metrics.Set", m, &ret) // expected 2 label values but got 1 in []string{"missing"} + // Histogram does not support Set — must surface as an error. + _, err = client.Set(ctx, connect.NewRequest(&metricsV1.SetRequest{ + Metric: &metricsV1.Metric{Name: "gauge_setOnHistogram", Value: 100.0}, + })) assert.Error(t, err) - assert.False(t, ret) } } func setWithoutLabels(address string) func(t *testing.T) { return func(t *testing.T) { - conn, err := net.Dial(dialNetwork, address) - assert.NoError(t, err) - defer func() { - _ = conn.Close() - }() - client := rpc.NewClientWithCodec(goridgeRpc.NewClientCodec(conn)) - var ret bool + client := newMetricsClient(address) + ctx := t.Context() nc := metrics.NamedCollector{ Name: "gauge_setWithoutLabels", @@ -948,32 +909,22 @@ func setWithoutLabels(address string) func(t *testing.T) { }, } - err = client.Call("metrics.Declare", nc, &ret) + declareResp, err := client.Declare(ctx, connect.NewRequest(&metricsV1.DeclareRequest{Collector: toProtoCollector(nc)})) assert.NoError(t, err) - assert.True(t, ret) - - ret = false - - m := metrics.Metric{ - Name: "gauge_setWithoutLabels", - Value: 100.0, - } + assert.True(t, declareResp.Msg.GetOk()) - err = client.Call("metrics.Set", m, &ret) // expected 2 label values but got 1 in []string{"missing"} + // GaugeVec requires labels — Set with empty labels must error. + _, err = client.Set(ctx, connect.NewRequest(&metricsV1.SetRequest{ + Metric: &metricsV1.Metric{Name: "gauge_setWithoutLabels", Value: 100.0}, + })) assert.Error(t, err) - assert.False(t, ret) } } func missingSection(address string) func(t *testing.T) { return func(t *testing.T) { - conn, err := net.Dial(dialNetwork, address) - assert.NoError(t, err) - defer func() { - _ = conn.Close() - }() - client := rpc.NewClientWithCodec(goridgeRpc.NewClientCodec(conn)) - var ret bool + client := newMetricsClient(address) + ctx := t.Context() nc := metrics.NamedCollector{ Name: "gauge_missing_section_collector", @@ -985,33 +936,23 @@ func missingSection(address string) func(t *testing.T) { }, } - err = client.Call("metrics.Declare", nc, &ret) + declareResp, err := client.Declare(ctx, connect.NewRequest(&metricsV1.DeclareRequest{Collector: toProtoCollector(nc)})) assert.NoError(t, err) - assert.True(t, ret) - - ret = false - - m := metrics.Metric{ - Name: "gauge_missing_section_collector", - Value: 100.0, - Labels: []string{"missing"}, - } + assert.True(t, declareResp.Msg.GetOk()) - err = client.Call("metrics.Set", m, &ret) // expected 2 label values but got 1 in []string{"missing"} + // Two-label collector with one label value — prometheus rejects the + // call, surfaces as an error on the wire. + _, err = client.Set(ctx, connect.NewRequest(&metricsV1.SetRequest{ + Metric: &metricsV1.Metric{Name: "gauge_missing_section_collector", Value: 100.0, Labels: []string{"missing"}}, + })) assert.Error(t, err) - assert.False(t, ret) } } func vectorMetric(address string) func(t *testing.T) { return func(t *testing.T) { - conn, err := net.Dial(dialNetwork, address) - assert.NoError(t, err) - defer func() { - _ = conn.Close() - }() - client := rpc.NewClientWithCodec(goridgeRpc.NewClientCodec(conn)) - var ret bool + client := newMetricsClient(address) + ctx := t.Context() nc := metrics.NamedCollector{ Name: "gauge_2_collector", @@ -1023,33 +964,22 @@ func vectorMetric(address string) func(t *testing.T) { }, } - err = client.Call("metrics.Declare", nc, &ret) + declareResp, err := client.Declare(ctx, connect.NewRequest(&metricsV1.DeclareRequest{Collector: toProtoCollector(nc)})) assert.NoError(t, err) - assert.True(t, ret) - - ret = false - - m := metrics.Metric{ - Name: "gauge_2_collector", - Value: 100.0, - Labels: []string{"core", "first"}, - } + assert.True(t, declareResp.Msg.GetOk()) - err = client.Call("metrics.Set", m, &ret) + setResp, err := client.Set(ctx, connect.NewRequest(&metricsV1.SetRequest{ + Metric: &metricsV1.Metric{Name: "gauge_2_collector", Value: 100.0, Labels: []string{"core", "first"}}, + })) assert.NoError(t, err) - assert.True(t, ret) + assert.True(t, setResp.Msg.GetOk()) } } func setMetric(address string) func(t *testing.T) { return func(t *testing.T) { - conn, err := net.Dial(dialNetwork, address) - assert.NoError(t, err) - defer func() { - _ = conn.Close() - }() - client := rpc.NewClientWithCodec(goridgeRpc.NewClientCodec(conn)) - var ret bool + client := newMetricsClient(address) + ctx := t.Context() nc := metrics.NamedCollector{ Name: "user_gauge_collector", @@ -1060,55 +990,32 @@ func setMetric(address string) func(t *testing.T) { }, } - err = client.Call("metrics.Declare", nc, &ret) + declareResp, err := client.Declare(ctx, connect.NewRequest(&metricsV1.DeclareRequest{Collector: toProtoCollector(nc)})) assert.NoError(t, err) - assert.True(t, ret) - ret = false - - m := metrics.Metric{ - Name: "user_gauge_collector", - Value: 100.0, - } + assert.True(t, declareResp.Msg.GetOk()) - err = client.Call("metrics.Set", m, &ret) + setResp, err := client.Set(ctx, connect.NewRequest(&metricsV1.SetRequest{ + Metric: &metricsV1.Metric{Name: "user_gauge_collector", Value: 100.0}, + })) assert.NoError(t, err) - assert.True(t, ret) + assert.True(t, setResp.Msg.GetOk()) } } func addMetricsTest(address string) func(t *testing.T) { return func(t *testing.T) { - conn, err := net.Dial(dialNetwork, address) + client := newMetricsClient(address) + addResp, err := client.Add(t.Context(), connect.NewRequest(&metricsV1.AddRequest{ + Metric: &metricsV1.Metric{Name: "test_metrics_named_collector", Value: 10000}, + })) assert.NoError(t, err) - defer func() { - _ = conn.Close() - }() - client := rpc.NewClientWithCodec(goridgeRpc.NewClientCodec(conn)) - var ret bool - - m := metrics.Metric{ - Name: "test_metrics_named_collector", - Value: 10000, - Labels: nil, - } - - err = client.Call("metrics.Add", m, &ret) - assert.NoError(t, err) - assert.True(t, ret) + assert.True(t, addResp.Msg.GetOk()) } } func declareMetricsTest(address string) func(t *testing.T) { return func(t *testing.T) { - conn, err := net.Dial(dialNetwork, address) - assert.NoError(t, err) - - defer func() { - _ = conn.Close() - }() - client := rpc.NewClientWithCodec(goridgeRpc.NewClientCodec(conn)) - var ret bool - + client := newMetricsClient(address) nc := metrics.NamedCollector{ Name: "test_metrics_named_collector", Collector: metrics.Collector{ @@ -1116,36 +1023,27 @@ func declareMetricsTest(address string) func(t *testing.T) { Subsystem: "default", Type: metrics.Counter, Help: "NO HELP!", - Labels: nil, - Buckets: nil, }, } - err = client.Call("metrics.Declare", nc, &ret) + declareResp, err := client.Declare(t.Context(), connect.NewRequest(&metricsV1.DeclareRequest{Collector: toProtoCollector(nc)})) assert.NoError(t, err) - assert.True(t, ret) + assert.True(t, declareResp.Msg.GetOk()) } } func unregisterMetric(name string, address string) func(t *testing.T) { return func(t *testing.T) { - conn, err := net.Dial(dialNetwork, address) + client := newMetricsClient(address) + resp, err := client.Unregister(t.Context(), connect.NewRequest(&metricsV1.UnregisterRequest{Name: name})) assert.NoError(t, err) - defer func() { - _ = conn.Close() - }() - client := rpc.NewClientWithCodec(goridgeRpc.NewClientCodec(conn)) - var ret bool - - err = client.Call("metrics.Unregister", name, &ret) - assert.NoError(t, err) - assert.True(t, ret) + assert.True(t, resp.Msg.GetOk()) } } func echoHTTP(port string) func(t *testing.T) { return func(t *testing.T) { - req, err := http.NewRequest("GET", "http://127.0.0.1:"+port, nil) + req, err := http.NewRequestWithContext(t.Context(), http.MethodGet, "http://127.0.0.1:"+port, nil) assert.NoError(t, err) r, err := http.DefaultClient.Do(req) @@ -1160,7 +1058,11 @@ func echoHTTP(port string) func(t *testing.T) { // get request and return body func get(address string) (string, error) { - r, err := http.Get(address) //nolint:gosec + req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, address, nil) + if err != nil { + return "", err + } + r, err := http.DefaultClient.Do(req) if err != nil { return "", err } @@ -1180,7 +1082,11 @@ func get(address string) (string, error) { // get request and return body func getIPV6(address string) (string, error) { - r, err := http.Get(address) //nolint:gosec + req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, address, nil) + if err != nil { + return "", err + } + r, err := http.DefaultClient.Do(req) if err != nil { return "", err }