From d7d303ec6222852fc49c1ba3b9fae20fae34b97a Mon Sep 17 00:00:00 2001 From: Kajetan Date: Fri, 9 Feb 2024 16:23:10 +0100 Subject: [PATCH 1/9] Init google pub sub plugin --- .gitignore | 4 +- go.mod | 47 +-- go.sum | 104 ++--- go.work | 8 + go.work.sum | 372 ++++++++++++++++++ plugin.go | 18 +- pubsubjobs/config.go | 11 +- pubsubjobs/driver.go | 191 +++++++-- pubsubjobs/listener.go | 21 +- tests/configs/.rr-init.yaml | 57 +++ tests/env/docker-compose-emulator-local.yaml | 8 + tests/go.mod | 103 +++++ tests/go.sum | 319 +++++++++++++++ tests/helpers/helpers.go | 203 ++++++++++ tests/jobs_test.go | 101 +++++ tests/php_test_files/composer.json | 33 ++ tests/php_test_files/jobs/jobs_bad_resp.php | 22 ++ .../jobs/jobs_create_memory.php | 47 +++ tests/php_test_files/jobs/jobs_err.php | 31 ++ tests/php_test_files/jobs/jobs_ok.php | 24 ++ tests/php_test_files/jobs/jobs_ok_pq.php | 25 ++ .../jobs/jobs_ok_queue_name_exist.php | 28 ++ tests/php_test_files/jobs/jobs_ok_sleep1.php | 25 ++ tests/php_test_files/jobs/jobs_ok_slow.php | 25 ++ .../php_test_files/jobs/jobs_ok_slow_rand.php | 29 ++ tests/php_test_files/jobs/jobs_send.php | 23 ++ 26 files changed, 1754 insertions(+), 125 deletions(-) create mode 100644 go.work create mode 100644 go.work.sum create mode 100644 tests/configs/.rr-init.yaml create mode 100644 tests/env/docker-compose-emulator-local.yaml create mode 100644 tests/go.mod create mode 100644 tests/go.sum create mode 100644 tests/helpers/helpers.go create mode 100644 tests/jobs_test.go create mode 100644 tests/php_test_files/composer.json create mode 100644 tests/php_test_files/jobs/jobs_bad_resp.php create mode 100644 tests/php_test_files/jobs/jobs_create_memory.php create mode 100644 tests/php_test_files/jobs/jobs_err.php create mode 100644 tests/php_test_files/jobs/jobs_ok.php create mode 100644 tests/php_test_files/jobs/jobs_ok_pq.php create mode 100644 tests/php_test_files/jobs/jobs_ok_queue_name_exist.php create mode 100644 tests/php_test_files/jobs/jobs_ok_sleep1.php create mode 100644 tests/php_test_files/jobs/jobs_ok_slow.php create mode 100644 tests/php_test_files/jobs/jobs_ok_slow_rand.php create mode 100644 tests/php_test_files/jobs/jobs_send.php diff --git a/.gitignore b/.gitignore index 474488e..ac174ce 100644 --- a/.gitignore +++ b/.gitignore @@ -12,5 +12,7 @@ *.out # Dependency directories (remove the comment below to include it) -# vendor/ +**/vendor/ +vendor/ .idea +.DS_Store \ No newline at end of file diff --git a/go.mod b/go.mod index 0c55b23..9e73b8b 100644 --- a/go.mod +++ b/go.mod @@ -5,48 +5,51 @@ go 1.21 toolchain go1.21.3 require ( - cloud.google.com/go/pubsub v1.33.0 + cloud.google.com/go/pubsub v1.36.1 github.com/goccy/go-json v0.10.2 github.com/roadrunner-server/api/v4 v4.10.0 github.com/roadrunner-server/endure/v2 v2.4.3 - github.com/roadrunner-server/errors v1.3.0 - go.opentelemetry.io/contrib/propagators/jaeger v1.21.1 - go.opentelemetry.io/otel v1.21.0 - go.opentelemetry.io/otel/sdk v1.21.0 - go.opentelemetry.io/otel/trace v1.21.0 + github.com/roadrunner-server/errors v1.4.0 + go.opentelemetry.io/contrib/propagators/jaeger v1.23.0 + go.opentelemetry.io/otel v1.23.1 + go.opentelemetry.io/otel/sdk v1.23.1 + go.opentelemetry.io/otel/trace v1.23.1 go.uber.org/zap v1.26.0 ) require ( - cloud.google.com/go v0.111.0 // indirect + cloud.google.com/go v0.112.0 // indirect cloud.google.com/go/compute v1.23.3 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect cloud.google.com/go/iam v1.1.5 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/go-logr/logr v1.3.0 // indirect + github.com/go-logr/logr v1.4.1 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/s2a-go v0.1.7 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect github.com/googleapis/gax-go/v2 v2.12.0 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 // indirect - go.opentelemetry.io/otel/metric v1.21.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 // indirect + go.opentelemetry.io/otel/metric v1.23.1 // indirect + go.uber.org/goleak v1.3.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.17.0 // indirect - golang.org/x/net v0.19.0 // indirect - golang.org/x/oauth2 v0.15.0 // indirect - golang.org/x/sync v0.5.0 // indirect - golang.org/x/sys v0.15.0 // indirect + golang.org/x/crypto v0.18.0 // indirect + golang.org/x/net v0.20.0 // indirect + golang.org/x/oauth2 v0.16.0 // indirect + golang.org/x/sync v0.6.0 // indirect + golang.org/x/sys v0.16.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.5.0 // indirect - google.golang.org/api v0.154.0 // indirect + google.golang.org/api v0.160.0 // indirect google.golang.org/appengine v1.6.8 // indirect - google.golang.org/genproto v0.0.0-20231212172506-995d672761c0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20231212172506-995d672761c0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 // indirect - google.golang.org/grpc v1.60.1 // indirect - google.golang.org/protobuf v1.31.0 // indirect + google.golang.org/genproto v0.0.0-20240116215550-a9fa1716bcac // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240125205218-1f4bbc51befe // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac // indirect + google.golang.org/grpc v1.61.0 // indirect + google.golang.org/protobuf v1.32.0 // indirect ) diff --git a/go.sum b/go.sum index d0b681e..dc6631e 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,6 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.111.0 h1:YHLKNupSD1KqjDbQ3+LVdQ81h/UJbJyZG203cEfnQgM= -cloud.google.com/go v0.111.0/go.mod h1:0mibmpKP1TyOOFYQY5izo0LnT+ecvOQ0Sg3OdmMiNRU= +cloud.google.com/go v0.112.0 h1:tpFCD7hpHFlQ8yPwT3x+QeXqc2T6+n6T+hmABHfDUSM= +cloud.google.com/go v0.112.0/go.mod h1:3jEEVwZ/MHU4djK5t5RHuKOA/GbLddgTdVubX1qnPD4= cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk= cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= @@ -9,17 +9,18 @@ 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/kms v1.15.5 h1:pj1sRfut2eRbD9pFRjNnPNg/CzJPuQAzUujMIM1vVeM= cloud.google.com/go/kms v1.15.5/go.mod h1:cU2H5jnp6G2TDpUGZyqTCoy1n16fbubHZjmVXSMtwDI= -cloud.google.com/go/pubsub v1.33.0 h1:6SPCPvWav64tj0sVX/+npCBKhUi/UjJehy9op/V3p2g= -cloud.google.com/go/pubsub v1.33.0/go.mod h1:f+w71I33OMyxf9VpMVcZbnG5KSUkCOUHYpFd5U1GdRc= +cloud.google.com/go/pubsub v1.36.1 h1:dfEPuGCHGbWUhaMCTHUFjfroILEkx55iUmKBZTP5f+Y= +cloud.google.com/go/pubsub v1.36.1/go.mod h1:iYjCa9EzWOoBiTdd4ps7QoMtMln5NwaZQpK1hbRfBDE= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k= -github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101 h1:7To3pQ+pZo0i3dsWEbinPNFs5gPSBOsJtx3wTT94VBY= +github.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -29,8 +30,8 @@ github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7 github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= -github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/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/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= @@ -69,15 +70,16 @@ github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfF github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/roadrunner-server/api/v4 v4.10.0 h1:tF6vmA6MaQyOL/GQQc+nyj356oX3UoQBd+SXNtsu+bU= github.com/roadrunner-server/api/v4 v4.10.0/go.mod h1:ou9QviOd5dxl3to1+BV4iZ3lnMLxuE/HqESNW5PDnw0= github.com/roadrunner-server/endure/v2 v2.4.3 h1:R9DdsLiLjtSFivZ1HKk/1eDZ0TYaKHQzakVwz9D2hto= github.com/roadrunner-server/endure/v2 v2.4.3/go.mod h1:4n3PdwZ3h/IRL2enDGvEVXtaQgqRnZ74VOyZtOJq528= -github.com/roadrunner-server/errors v1.3.0 h1:kLVXpXne0jMReN7pj8KIhyYyjqKjsPC5DRGqMsd4/Fo= -github.com/roadrunner-server/errors v1.3.0/go.mod h1:XYVuhXvxi3yQaP/zCLB6QRZ0JvQIRaBa0SKFHL4WLKg= +github.com/roadrunner-server/errors v1.4.0 h1:Odjg3VZrj1q5Y8ILwoN+JgERyv0pkhrWPNOM4h68iQ8= +github.com/roadrunner-server/errors v1.4.0/go.mod h1:78PvraAFj+Sxy5nDmo0S+h6rEMLFIDszWZxA3B0sPAs= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -87,24 +89,26 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.einride.tech/aip v0.66.0 h1:XfV+NQX6L7EOYK11yoHHFtndeaWh3KbD9/cN/6iWEt8= +go.einride.tech/aip v0.66.0/go.mod h1:qAhMsfT7plxBX+Oy7Huol6YUvZ0ZzdUz26yZsQwfl1M= 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/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 h1:SpGay3w+nEwMpfVnbqOLH5gY52/foP8RE8UzTZ1pdSE= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 h1:aFJWCqJMNjENlcleuuOkGAPH82y0yULBScfXcIEdS24= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo= -go.opentelemetry.io/contrib/propagators/jaeger v1.21.1 h1:f4beMGDKiVzg9IcX7/VuWVy+oGdjx3dNJ72YehmtY5k= -go.opentelemetry.io/contrib/propagators/jaeger v1.21.1/go.mod h1:U9jhkEl8d1LL+QXY7q3kneJWJugiN3kZJV2OWz3hkBY= -go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc= -go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= -go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4= -go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= -go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8= -go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= -go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc= -go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= -go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= -go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0 h1:UNQQKPfTDe1J81ViolILjTKPr9WetKW6uei2hFgJmFs= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0/go.mod h1:r9vWsPS/3AQItv3OSlEJ/E4mbrhUbbw18meOjArPtKQ= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 h1:sv9kVfal0MK0wBMCOGr+HeJm9v803BkJxGrk2au7j08= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0/go.mod h1:SK2UL73Zy1quvRPonmOmRDiWk1KBV3LyIeeIxcEApWw= +go.opentelemetry.io/contrib/propagators/jaeger v1.23.0 h1:KFxfTCTkH1usVFzDaWzbmNdFX7ybUTCtkLsUTww0nG4= +go.opentelemetry.io/contrib/propagators/jaeger v1.23.0/go.mod h1:xU+81opGquQICJGzwscLXAQLnIPWI+q7Zu4AQSrgXf8= +go.opentelemetry.io/otel v1.23.1 h1:Za4UzOqJYS+MUczKI320AtqZHZb7EqxO00jAHE0jmQY= +go.opentelemetry.io/otel v1.23.1/go.mod h1:Td0134eafDLcTS4y+zQ26GE8u3dEuRBiBCTUIRHaikA= +go.opentelemetry.io/otel/metric v1.23.1 h1:PQJmqJ9u2QaJLBOELl1cxIdPcpbwzbkjfEyelTl2rlo= +go.opentelemetry.io/otel/metric v1.23.1/go.mod h1:mpG2QPlAfnK8yNhNJAxDZruU9Y1/HubbC+KyH8FaCWI= +go.opentelemetry.io/otel/sdk v1.23.1 h1:O7JmZw0h76if63LQdsBMKQDWNb5oEcOThG9IrxscV+E= +go.opentelemetry.io/otel/sdk v1.23.1/go.mod h1:LzdEVR5am1uKOOwfBWFef2DCi1nu3SA8XQxx2IerWFk= +go.opentelemetry.io/otel/trace v1.23.1 h1:4LrmmEd8AU2rFvU1zegmvqW7+kWarxtNOPyeL6HmYY8= +go.opentelemetry.io/otel/trace v1.23.1/go.mod h1:4IpnpJFwr1mo/6HL8XIPJaE9y0+u1KcVmuW7dwFSVrI= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= @@ -112,8 +116,8 @@ go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -128,17 +132,17 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= -golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ= -golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM= +golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= +golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= -golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -147,8 +151,8 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -168,8 +172,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.154.0 h1:X7QkVKZBskztmpPKWQXgjJRPA2dJYrL6r+sYPRLj050= -google.golang.org/api v0.154.0/go.mod h1:qhSMkM85hgqiokIYsrRyKxrjfBeIhgl4Z2JmeRkYylc= +google.golang.org/api v0.160.0 h1:SEspjXHVqE1m5a1fRy8JFB+5jSu+V0GEDKDghF3ttO4= +google.golang.org/api v0.160.0/go.mod h1:0mu0TpK33qnydLvWqbImq2b1eQ5FHRSDCBzAxX9ZHyw= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= @@ -177,19 +181,19 @@ google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20231212172506-995d672761c0 h1:YJ5pD9rF8o9Qtta0Cmy9rdBwkSjrTCT6XTiUQVOtIos= -google.golang.org/genproto v0.0.0-20231212172506-995d672761c0/go.mod h1:l/k7rMz0vFTBPy+tFSGvXEd3z+BcoG1k7EHbqm+YBsY= -google.golang.org/genproto/googleapis/api v0.0.0-20231212172506-995d672761c0 h1:s1w3X6gQxwrLEpxnLd/qXTVLgQE2yXwaOaoa6IlY/+o= -google.golang.org/genproto/googleapis/api v0.0.0-20231212172506-995d672761c0/go.mod h1:CAny0tYF+0/9rmDB9fahA9YLzX3+AEVl1qXbv5hhj6c= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 h1:/jFB8jK5R3Sq3i/lmeZO0cATSzFfZaJq1J2Euan3XKU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0/go.mod h1:FUoWkonphQm3RhTS+kOEhF8h0iDpm4tdXolVCeZ9KKA= +google.golang.org/genproto v0.0.0-20240116215550-a9fa1716bcac h1:ZL/Teoy/ZGnzyrqK/Optxxp2pmVh+fmJ97slxSRyzUg= +google.golang.org/genproto v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:+Rvu7ElI+aLzyDQhpHMFMMltsD6m7nqpuWDd2CwJw3k= +google.golang.org/genproto/googleapis/api v0.0.0-20240125205218-1f4bbc51befe h1:0poefMBYvYbs7g5UkjS6HcxBPaTRAmznle9jnxYoAI8= +google.golang.org/genproto/googleapis/api v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:4jWUdICTdgc3Ibxmr8nAJiiLHwQBY0UI0XZcEMaFKaA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac h1:nUQEQmH/csSvFECKYRv6HWEyypysidKl2I6Qpsglq/0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:daQN87bsDqDoe316QbbvX60nMoJQa4r6Ds0ZuoAe5yA= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU= -google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= +google.golang.org/grpc v1.61.0 h1:TOvOcuXn30kRao+gfcvsebNEa5iZIiLkisYEkf7R7o0= +google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -201,8 +205,8 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= +google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/go.work b/go.work new file mode 100644 index 0000000..5c2a7f8 --- /dev/null +++ b/go.work @@ -0,0 +1,8 @@ +go 1.21 + +toolchain go1.21.0 + +use ( + . + ./tests +) diff --git a/go.work.sum b/go.work.sum new file mode 100644 index 0000000..16a3780 --- /dev/null +++ b/go.work.sum @@ -0,0 +1,372 @@ +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.110.8/go.mod h1:Iz8AkXJf1qmxC3Oxoep8R1T36w8B92yU29PcBhHO5fk= +cloud.google.com/go v0.110.10/go.mod h1:v1OoFqYxiBkUrruItNM3eT4lLByNjxmJSV/xDKJNnic= +cloud.google.com/go/accessapproval v1.7.4/go.mod h1:/aTEh45LzplQgFYdQdwPMR9YdX0UlhBmvB84uAmQKUc= +cloud.google.com/go/accesscontextmanager v1.8.4/go.mod h1:ParU+WbMpD34s5JFEnGAnPBYAgUHozaTmDJU7aCU9+M= +cloud.google.com/go/aiplatform v1.58.0/go.mod h1:pwZMGvqe0JRkI1GWSZCtnAfrR4K1bv65IHILGA//VEU= +cloud.google.com/go/analytics v0.22.0/go.mod h1:eiROFQKosh4hMaNhF85Oc9WO97Cpa7RggD40e/RBy8w= +cloud.google.com/go/apigateway v1.6.4/go.mod h1:0EpJlVGH5HwAN4VF4Iec8TAzGN1aQgbxAWGJsnPCGGY= +cloud.google.com/go/apigeeconnect v1.6.4/go.mod h1:CapQCWZ8TCjnU0d7PobxhpOdVz/OVJ2Hr/Zcuu1xFx0= +cloud.google.com/go/apigeeregistry v0.8.2/go.mod h1:h4v11TDGdeXJDJvImtgK2AFVvMIgGWjSb0HRnBSjcX8= +cloud.google.com/go/appengine v1.8.4/go.mod h1:TZ24v+wXBujtkK77CXCpjZbnuTvsFNT41MUaZ28D6vg= +cloud.google.com/go/area120 v0.8.4/go.mod h1:jfawXjxf29wyBXr48+W+GyX/f8fflxp642D/bb9v68M= +cloud.google.com/go/artifactregistry v1.14.6/go.mod h1:np9LSFotNWHcjnOgh8UVK0RFPCTUGbO0ve3384xyHfE= +cloud.google.com/go/asset v1.17.0/go.mod h1:yYLfUD4wL4X589A9tYrv4rFrba0QlDeag0CMcM5ggXU= +cloud.google.com/go/assuredworkloads v1.11.4/go.mod h1:4pwwGNwy1RP0m+y12ef3Q/8PaiWrIDQ6nD2E8kvWI9U= +cloud.google.com/go/automl v1.13.4/go.mod h1:ULqwX/OLZ4hBVfKQaMtxMSTlPx0GqGbWN8uA/1EqCP8= +cloud.google.com/go/baremetalsolution v1.2.3/go.mod h1:/UAQ5xG3faDdy180rCUv47e0jvpp3BFxT+Cl0PFjw5g= +cloud.google.com/go/batch v1.7.0/go.mod h1:J64gD4vsNSA2O5TtDB5AAux3nJ9iV8U3ilg3JDBYejU= +cloud.google.com/go/beyondcorp v1.0.3/go.mod h1:HcBvnEd7eYr+HGDd5ZbuVmBYX019C6CEXBonXbCVwJo= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.57.1/go.mod h1:iYzC0tGVWt1jqSzBHqCr3lrRn0u13E8e+AqowBsDgug= +cloud.google.com/go/bigquery v1.58.0/go.mod h1:0eh4mWNY0KrBTjUzLjoYImapGORq9gEPT7MWjCy9lik= +cloud.google.com/go/billing v1.18.0/go.mod h1:5DOYQStCxquGprqfuid/7haD7th74kyMBHkjO/OvDtk= +cloud.google.com/go/binaryauthorization v1.8.0/go.mod h1:VQ/nUGRKhrStlGr+8GMS8f6/vznYLkdK5vaKfdCIpvU= +cloud.google.com/go/certificatemanager v1.7.4/go.mod h1:FHAylPe/6IIKuaRmHbjbdLhGhVQ+CWHSD5Jq0k4+cCE= +cloud.google.com/go/channel v1.17.4/go.mod h1:QcEBuZLGGrUMm7kNj9IbU1ZfmJq2apotsV83hbxX7eE= +cloud.google.com/go/cloudbuild v1.15.0/go.mod h1:eIXYWmRt3UtggLnFGx4JvXcMj4kShhVzGndL1LwleEM= +cloud.google.com/go/clouddms v1.7.3/go.mod h1:fkN2HQQNUYInAU3NQ3vRLkV2iWs8lIdmBKOx4nrL6Hc= +cloud.google.com/go/cloudtasks v1.12.4/go.mod h1:BEPu0Gtt2dU6FxZHNqqNdGqIG86qyWKBPGnsb7udGY0= +cloud.google.com/go/compute v1.20.1/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= +cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= +cloud.google.com/go/compute v1.23.1/go.mod h1:CqB3xpmPKKt3OJpW2ndFIXnA9A4xAy/F3Xp1ixncW78= +cloud.google.com/go/contactcenterinsights v1.12.1/go.mod h1:HHX5wrz5LHVAwfI2smIotQG9x8Qd6gYilaHcLLLmNis= +cloud.google.com/go/container v1.29.0/go.mod h1:b1A1gJeTBXVLQ6GGw9/9M4FG94BEGsqJ5+t4d/3N7O4= +cloud.google.com/go/containeranalysis v0.11.3/go.mod h1:kMeST7yWFQMGjiG9K7Eov+fPNQcGhb8mXj/UcTiWw9U= +cloud.google.com/go/datacatalog v1.19.0/go.mod h1:5FR6ZIF8RZrtml0VUao22FxhdjkoG+a0866rEnObryM= +cloud.google.com/go/datacatalog v1.19.2/go.mod h1:2YbODwmhpLM4lOFe3PuEhHK9EyTzQJ5AXgIy7EDKTEE= +cloud.google.com/go/dataflow v0.9.4/go.mod h1:4G8vAkHYCSzU8b/kmsoR2lWyHJD85oMJPHMtan40K8w= +cloud.google.com/go/dataform v0.9.1/go.mod h1:pWTg+zGQ7i16pyn0bS1ruqIE91SdL2FDMvEYu/8oQxs= +cloud.google.com/go/datafusion v1.7.4/go.mod h1:BBs78WTOLYkT4GVZIXQCZT3GFpkpDN4aBY4NDX/jVlM= +cloud.google.com/go/datalabeling v0.8.4/go.mod h1:Z1z3E6LHtffBGrNUkKwbwbDxTiXEApLzIgmymj8A3S8= +cloud.google.com/go/dataplex v1.14.0/go.mod h1:mHJYQQ2VEJHsyoC0OdNyy988DvEbPhqFs5OOLffLX0c= +cloud.google.com/go/dataproc v1.12.0/go.mod h1:zrF3aX0uV3ikkMz6z4uBbIKyhRITnxvr4i3IjKsKrw4= +cloud.google.com/go/dataproc/v2 v2.3.0/go.mod h1:G5R6GBc9r36SXv/RtZIVfB8SipI+xVn0bX5SxUzVYbY= +cloud.google.com/go/dataqna v0.8.4/go.mod h1:mySRKjKg5Lz784P6sCov3p1QD+RZQONRMRjzGNcFd0c= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.15.0/go.mod h1:GAeStMBIt9bPS7jMJA85kgkpsMkvseWWXiaHya9Jes8= +cloud.google.com/go/datastream v1.10.3/go.mod h1:YR0USzgjhqA/Id0Ycu1VvZe8hEWwrkjuXrGbzeDOSEA= +cloud.google.com/go/deploy v1.17.0/go.mod h1:XBr42U5jIr64t92gcpOXxNrqL2PStQCXHuKK5GRUuYo= +cloud.google.com/go/dialogflow v1.48.1/go.mod h1:C1sjs2/g9cEwjCltkKeYp3FFpz8BOzNondEaAlCpt+A= +cloud.google.com/go/dlp v1.11.1/go.mod h1:/PA2EnioBeXTL/0hInwgj0rfsQb3lpE3R8XUJxqUNKI= +cloud.google.com/go/documentai v1.23.7/go.mod h1:ghzBsyVTiVdkfKaUCum/9bGBEyBjDO4GfooEcYKhN+g= +cloud.google.com/go/domains v0.9.4/go.mod h1:27jmJGShuXYdUNjyDG0SodTfT5RwLi7xmH334Gvi3fY= +cloud.google.com/go/edgecontainer v1.1.4/go.mod h1:AvFdVuZuVGdgaE5YvlL1faAoa1ndRR/5XhXZvPBHbsE= +cloud.google.com/go/errorreporting v0.3.0/go.mod h1:xsP2yaAp+OAW4OIm60An2bbLpqIhKXdWR/tawvl7QzU= +cloud.google.com/go/essentialcontacts v1.6.5/go.mod h1:jjYbPzw0x+yglXC890l6ECJWdYeZ5dlYACTFL0U/VuM= +cloud.google.com/go/eventarc v1.13.3/go.mod h1:RWH10IAZIRcj1s/vClXkBgMHwh59ts7hSWcqD3kaclg= +cloud.google.com/go/filestore v1.8.0/go.mod h1:S5JCxIbFjeBhWMTfIYH2Jx24J6BqjwpkkPl+nBA5DlI= +cloud.google.com/go/firestore v1.14.0/go.mod h1:96MVaHLsEhbvkBEdZgfN+AS/GIkco1LRpH9Xp9YZfzQ= +cloud.google.com/go/functions v1.15.4/go.mod h1:CAsTc3VlRMVvx+XqXxKqVevguqJpnVip4DdonFsX28I= +cloud.google.com/go/gkebackup v1.3.4/go.mod h1:gLVlbM8h/nHIs09ns1qx3q3eaXcGSELgNu1DWXYz1HI= +cloud.google.com/go/gkeconnect v0.8.4/go.mod h1:84hZz4UMlDCKl8ifVW8layK4WHlMAFeq8vbzjU0yJkw= +cloud.google.com/go/gkehub v0.14.4/go.mod h1:Xispfu2MqnnFt8rV/2/3o73SK1snL8s9dYJ9G2oQMfc= +cloud.google.com/go/gkemulticloud v1.1.0/go.mod h1:7NpJBN94U6DY1xHIbsDqB2+TFZUfjLUKLjUX8NGLor0= +cloud.google.com/go/grafeas v0.3.0/go.mod h1:P7hgN24EyONOTMyeJH6DxG4zD7fwiYa5Q6GUgyFSOU8= +cloud.google.com/go/gsuiteaddons v1.6.4/go.mod h1:rxtstw7Fx22uLOXBpsvb9DUbC+fiXs7rF4U29KHM/pE= +cloud.google.com/go/iam v1.1.3/go.mod h1:3khUlaBXfPKKe7huYgEpDn6FtgRyMEqbkvBxrQyY5SE= +cloud.google.com/go/iap v1.9.3/go.mod h1:DTdutSZBqkkOm2HEOTBzhZxh2mwwxshfD/h3yofAiCw= +cloud.google.com/go/ids v1.4.4/go.mod h1:z+WUc2eEl6S/1aZWzwtVNWoSZslgzPxAboS0lZX0HjI= +cloud.google.com/go/iot v1.7.4/go.mod h1:3TWqDVvsddYBG++nHSZmluoCAVGr1hAcabbWZNKEZLk= +cloud.google.com/go/language v1.12.2/go.mod h1:9idWapzr/JKXBBQ4lWqVX/hcadxB194ry20m/bTrhWc= +cloud.google.com/go/lifesciences v0.9.4/go.mod h1:bhm64duKhMi7s9jR9WYJYvjAFJwRqNj+Nia7hF0Z7JA= +cloud.google.com/go/logging v1.9.0/go.mod h1:1Io0vnZv4onoUnsVUQY3HZ3Igb1nBchky0A0y7BBBhE= +cloud.google.com/go/longrunning v0.5.2/go.mod h1:nqo6DQbNV2pXhGDbDMoN2bWz68MjZUzqv2YttZiveCs= +cloud.google.com/go/longrunning v0.5.4/go.mod h1:zqNVncI0BOP8ST6XQD1+VcvuShMmq7+xFSzOL++V0dI= +cloud.google.com/go/managedidentities v1.6.4/go.mod h1:WgyaECfHmF00t/1Uk8Oun3CQ2PGUtjc3e9Alh79wyiM= +cloud.google.com/go/maps v1.6.3/go.mod h1:VGAn809ADswi1ASofL5lveOHPnE6Rk/SFTTBx1yuOLw= +cloud.google.com/go/mediatranslation v0.8.4/go.mod h1:9WstgtNVAdN53m6TQa5GjIjLqKQPXe74hwSCxUP6nj4= +cloud.google.com/go/memcache v1.10.4/go.mod h1:v/d8PuC8d1gD6Yn5+I3INzLR01IDn0N4Ym56RgikSI0= +cloud.google.com/go/metastore v1.13.3/go.mod h1:K+wdjXdtkdk7AQg4+sXS8bRrQa9gcOr+foOMF2tqINE= +cloud.google.com/go/monitoring v1.17.0/go.mod h1:KwSsX5+8PnXv5NJnICZzW2R8pWTis8ypC4zmdRD63Tw= +cloud.google.com/go/networkconnectivity v1.14.3/go.mod h1:4aoeFdrJpYEXNvrnfyD5kIzs8YtHg945Og4koAjHQek= +cloud.google.com/go/networkmanagement v1.9.3/go.mod h1:y7WMO1bRLaP5h3Obm4tey+NquUvB93Co1oh4wpL+XcU= +cloud.google.com/go/networksecurity v0.9.4/go.mod h1:E9CeMZ2zDsNBkr8axKSYm8XyTqNhiCHf1JO/Vb8mD1w= +cloud.google.com/go/notebooks v1.11.2/go.mod h1:z0tlHI/lREXC8BS2mIsUeR3agM1AkgLiS+Isov3SS70= +cloud.google.com/go/optimization v1.6.2/go.mod h1:mWNZ7B9/EyMCcwNl1frUGEuY6CPijSkz88Fz2vwKPOY= +cloud.google.com/go/orchestration v1.8.4/go.mod h1:d0lywZSVYtIoSZXb0iFjv9SaL13PGyVOKDxqGxEf/qI= +cloud.google.com/go/orgpolicy v1.12.0/go.mod h1:0+aNV/nrfoTQ4Mytv+Aw+stBDBjNf4d8fYRA9herfJI= +cloud.google.com/go/osconfig v1.12.4/go.mod h1:B1qEwJ/jzqSRslvdOCI8Kdnp0gSng0xW4LOnIebQomA= +cloud.google.com/go/oslogin v1.12.2/go.mod h1:CQ3V8Jvw4Qo4WRhNPF0o+HAM4DiLuE27Ul9CX9g2QdY= +cloud.google.com/go/oslogin v1.13.0/go.mod h1:xPJqLwpTZ90LSE5IL1/svko+6c5avZLluiyylMb/sRA= +cloud.google.com/go/phishingprotection v0.8.4/go.mod h1:6b3kNPAc2AQ6jZfFHioZKg9MQNybDg4ixFd4RPZZ2nE= +cloud.google.com/go/policytroubleshooter v1.10.2/go.mod h1:m4uF3f6LseVEnMV6nknlN2vYGRb+75ylQwJdnOXfnv0= +cloud.google.com/go/privatecatalog v0.9.4/go.mod h1:SOjm93f+5hp/U3PqMZAHTtBtluqLygrDrVO8X8tYtG0= +cloud.google.com/go/pubsub v1.34.0/go.mod h1:alj4l4rBg+N3YTFDDC+/YyFTs6JAjam2QfYsddcAW4c= +cloud.google.com/go/pubsublite v1.8.1/go.mod h1:fOLdU4f5xldK4RGJrBMm+J7zMWNj/k4PxwEZXy39QS0= +cloud.google.com/go/recaptchaenterprise/v2 v2.9.0/go.mod h1:Dak54rw6lC2gBY8FBznpOCAR58wKf+R+ZSJRoeJok4w= +cloud.google.com/go/recommendationengine v0.8.4/go.mod h1:GEteCf1PATl5v5ZsQ60sTClUE0phbWmo3rQ1Js8louU= +cloud.google.com/go/recommender v1.12.0/go.mod h1:+FJosKKJSId1MBFeJ/TTyoGQZiEelQQIZMKYYD8ruK4= +cloud.google.com/go/redis v1.14.1/go.mod h1:MbmBxN8bEnQI4doZPC1BzADU4HGocHBk2de3SbgOkqs= +cloud.google.com/go/resourcemanager v1.9.4/go.mod h1:N1dhP9RFvo3lUfwtfLWVxfUWq8+KUQ+XLlHLH3BoFJ0= +cloud.google.com/go/resourcesettings v1.6.4/go.mod h1:pYTTkWdv2lmQcjsthbZLNBP4QW140cs7wqA3DuqErVI= +cloud.google.com/go/retail v1.14.4/go.mod h1:l/N7cMtY78yRnJqp5JW8emy7MB1nz8E4t2yfOmklYfg= +cloud.google.com/go/run v1.3.3/go.mod h1:WSM5pGyJ7cfYyYbONVQBN4buz42zFqwG67Q3ch07iK4= +cloud.google.com/go/scheduler v1.10.5/go.mod h1:MTuXcrJC9tqOHhixdbHDFSIuh7xZF2IysiINDuiq6NI= +cloud.google.com/go/secretmanager v1.11.4/go.mod h1:wreJlbS9Zdq21lMzWmJ0XhWW2ZxgPeahsqeV/vZoJ3w= +cloud.google.com/go/security v1.15.4/go.mod h1:oN7C2uIZKhxCLiAAijKUCuHLZbIt/ghYEo8MqwD/Ty4= +cloud.google.com/go/securitycenter v1.24.3/go.mod h1:l1XejOngggzqwr4Fa2Cn+iWZGf+aBLTXtB/vXjy5vXM= +cloud.google.com/go/servicedirectory v1.11.3/go.mod h1:LV+cHkomRLr67YoQy3Xq2tUXBGOs5z5bPofdq7qtiAw= +cloud.google.com/go/shell v1.7.4/go.mod h1:yLeXB8eKLxw0dpEmXQ/FjriYrBijNsONpwnWsdPqlKM= +cloud.google.com/go/spanner v1.55.0/go.mod h1:HXEznMUVhC+PC+HDyo9YFG2Ajj5BQDkcbqB9Z2Ffxi0= +cloud.google.com/go/speech v1.21.0/go.mod h1:wwolycgONvfz2EDU8rKuHRW3+wc9ILPsAWoikBEWavY= +cloud.google.com/go/storage v1.35.1/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8= +cloud.google.com/go/storage v1.36.0/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8= +cloud.google.com/go/storagetransfer v1.10.3/go.mod h1:Up8LY2p6X68SZ+WToswpQbQHnJpOty/ACcMafuey8gc= +cloud.google.com/go/talent v1.6.5/go.mod h1:Mf5cma696HmE+P2BWJ/ZwYqeJXEeU0UqjHFXVLadEDI= +cloud.google.com/go/texttospeech v1.7.4/go.mod h1:vgv0002WvR4liGuSd5BJbWy4nDn5Ozco0uJymY5+U74= +cloud.google.com/go/tpu v1.6.4/go.mod h1:NAm9q3Rq2wIlGnOhpYICNI7+bpBebMJbh0yyp3aNw1Y= +cloud.google.com/go/trace v1.10.4/go.mod h1:Nso99EDIK8Mj5/zmB+iGr9dosS/bzWCJ8wGmE6TXNWY= +cloud.google.com/go/translate v1.10.0/go.mod h1:Kbq9RggWsbqZ9W5YpM94Q1Xv4dshw/gr/SHfsl5yCZ0= +cloud.google.com/go/video v1.20.3/go.mod h1:TnH/mNZKVHeNtpamsSPygSR0iHtvrR/cW1/GDjN5+GU= +cloud.google.com/go/videointelligence v1.11.4/go.mod h1:kPBMAYsTPFiQxMLmmjpcZUMklJp3nC9+ipJJtprccD8= +cloud.google.com/go/vision/v2 v2.7.5/go.mod h1:GcviprJLFfK9OLf0z8Gm6lQb6ZFUulvpZws+mm6yPLM= +cloud.google.com/go/vmmigration v1.7.4/go.mod h1:yBXCmiLaB99hEl/G9ZooNx2GyzgsjKnw5fWcINRgD70= +cloud.google.com/go/vmwareengine v1.0.3/go.mod h1:QSpdZ1stlbfKtyt6Iu19M6XRxjmXO+vb5a/R6Fvy2y4= +cloud.google.com/go/vpcaccess v1.7.4/go.mod h1:lA0KTvhtEOb/VOdnH/gwPuOzGgM+CWsmGu6bb4IoMKk= +cloud.google.com/go/webrisk v1.9.4/go.mod h1:w7m4Ib4C+OseSr2GL66m0zMBywdrVNTDKsdEsfMl7X0= +cloud.google.com/go/websecurityscanner v1.6.4/go.mod h1:mUiyMQ+dGpPPRkHgknIZeCzSHJ45+fY4F52nZFDHm2o= +cloud.google.com/go/workflows v1.12.3/go.mod h1:fmOUeeqEwPzIU81foMjTRQIdwQHADi/vEr1cx9R1m5g= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/alecthomas/kingpin/v2 v2.4.0/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE= +github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= +github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= +github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= +github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/eapache/go-resiliency v1.3.0/go.mod h1:5yPzW0MIvSe0JDsv0v+DvcjEv2FyD6iZYSs1ZI+iQho= +github.com/eapache/go-xerial-snappy v0.0.0-20230111030713-bf00bc1b83b6/go.mod h1:YvSRo5mw33fLEx1+DlK6L2VV43tJt5Eyel9n9XBcR+0= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.11.1/go.mod h1:uhMcXKCQMEJHiAb0w+YGefQLaTEw+YhGluxZkrTmD0g= +github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-pkcs11 v0.2.1-0.20230907215043-c6f79328ddf9/go.mod h1:6eQoGcuNJpa7jnd5pMGdkSaQpNDYvPlXWMcjXXThLlY= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/google-cloud-go-testing v0.0.0-20210719221736-1c9a4c676720/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= +github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/consul/api v1.25.1/go.mod h1:iiLVwR/htV7mas/sy0O+XSuEnrdBUUydemjxcUrAt4g= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= +github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= +github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= +github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM= +github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo= +github.com/jcmturner/gokrb5/v8 v8.4.3/go.mod h1:dqRwJGXznQrzw6cWmyo6kH+E7jksEQG/CyVWsJEsJO0= +github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/lyft/protoc-gen-star/v2 v2.0.3/go.mod h1:amey7yeodaJhXSbf/TlLvWiqQfLOSpEk//mLlc+axEk= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= +github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= +github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/nats-io/nats.go v1.31.0/go.mod h1:di3Bm5MLsoB4Bx61CBTsxuarI36WbhAwOm8QrW39+i8= +github.com/nats-io/nkeys v0.4.6/go.mod h1:4DxZNzenSVd1cYQoAa8948QY3QDjrHfcfVADymtkpts= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM= +github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= +github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pkg/sftp v1.13.6/go.mod h1:tz1ryNURKu77RL+GuCzmoJYxQczL3wLNNpPWagdg4Qk= +github.com/rabbitmq/amqp091-go v1.8.1/go.mod h1:+jPrT9iY2eLjRaMSRHUhc3z14E/l85kv/f+6luSD3pc= +github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= +github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo= +go.opentelemetry.io/contrib/propagators/jaeger v1.23.0 h1:KFxfTCTkH1usVFzDaWzbmNdFX7ybUTCtkLsUTww0nG4= +go.opentelemetry.io/contrib/propagators/jaeger v1.23.0/go.mod h1:xU+81opGquQICJGzwscLXAQLnIPWI+q7Zu4AQSrgXf8= +go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= +go.opentelemetry.io/otel v1.23.0 h1:Df0pqjqExIywbMCMTxkAwzjLZtRf+bBKLbUcpxO2C9E= +go.opentelemetry.io/otel v1.23.0/go.mod h1:YCycw9ZeKhcJFrb34iVSkyT0iczq/zYDtZYFufObyB0= +go.opentelemetry.io/otel v1.23.1 h1:Za4UzOqJYS+MUczKI320AtqZHZb7EqxO00jAHE0jmQY= +go.opentelemetry.io/otel v1.23.1/go.mod h1:Td0134eafDLcTS4y+zQ26GE8u3dEuRBiBCTUIRHaikA= +go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= +go.opentelemetry.io/otel/metric v1.23.0 h1:pazkx7ss4LFVVYSxYew7L5I6qvLXHA0Ap2pwV+9Cnpo= +go.opentelemetry.io/otel/metric v1.23.0/go.mod h1:MqUW2X2a6Q8RN96E2/nqNoT+z9BSms20Jb7Bbp+HiTo= +go.opentelemetry.io/otel/metric v1.23.1 h1:PQJmqJ9u2QaJLBOELl1cxIdPcpbwzbkjfEyelTl2rlo= +go.opentelemetry.io/otel/metric v1.23.1/go.mod h1:mpG2QPlAfnK8yNhNJAxDZruU9Y1/HubbC+KyH8FaCWI= +go.opentelemetry.io/otel/sdk v1.23.1 h1:O7JmZw0h76if63LQdsBMKQDWNb5oEcOThG9IrxscV+E= +go.opentelemetry.io/otel/sdk v1.23.1/go.mod h1:LzdEVR5am1uKOOwfBWFef2DCi1nu3SA8XQxx2IerWFk= +go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= +go.opentelemetry.io/otel/trace v1.23.0 h1:37Ik5Ib7xfYVb4V1UtnT97T1jI+AoIYkJyPkuL4iJgI= +go.opentelemetry.io/otel/trace v1.23.0/go.mod h1:GSGTbIClEsuZrGIzoEHqsVfxgn5UkggkflQwDScNUsk= +go.opentelemetry.io/otel/trace v1.23.1 h1:4LrmmEd8AU2rFvU1zegmvqW7+kWarxtNOPyeL6HmYY8= +go.opentelemetry.io/otel/trace v1.23.1/go.mod h1:4IpnpJFwr1mo/6HL8XIPJaE9y0+u1KcVmuW7dwFSVrI= +go.temporal.io/api v1.26.0 h1:N4V0Daqa0qqK5+9LELSZV7clBYrwB4l33iaFfKgycPk= +go.temporal.io/api v1.26.0/go.mod h1:uVAcpQJ6bM4mxZ3m7vSHU65fHjrwy9ktGQMtsNfMZQQ= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= +golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0= +golang.org/x/oauth2 v0.14.0/go.mod h1:lAtNWgaWfL4cm7j2OV8TxGi9Qb7ECORx8DktCY74OwM= +golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.149.0/go.mod h1:Mwn1B7JTXrzXtnvmzQE2BD6bYZQ8DShKZDZbeN9I7qI= +google.golang.org/api v0.150.0/go.mod h1:ccy+MJ6nrYFgE3WgRx/AMXOxOmU8Q4hSa+jjibzhxcg= +google.golang.org/api v0.152.0/go.mod h1:3qNJX5eOmhiWYc67jRA/3GsDw97UFb5ivv7Y2PrriAY= +google.golang.org/api v0.155.0/go.mod h1:GI5qK5f40kCpHfPn6+YzGAByIKWv8ujFnmoWm7Igduk= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:CgAqfJo+Xmu0GwA0411Ht3OU3OntXwsGmrmjI8ioGXI= +google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:J7XzRzVy1+IPwWHZUzoD0IccYZIrXILAQpc+Qy9CMhY= +google.golang.org/genproto v0.0.0-20231212172506-995d672761c0/go.mod h1:l/k7rMz0vFTBPy+tFSGvXEd3z+BcoG1k7EHbqm+YBsY= +google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917/go.mod h1:pZqR+glSb11aJ+JQcczCvgf47+duRuzNSKqE8YAQnV0= +google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:IBQ646DjkDkvUIsVq/cc03FUFQ9wbZu7yE396YcL870= +google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4= +google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917/go.mod h1:CmlNWB9lSezaYELKS5Ym1r44VrrbPUa7JTvw+6MbpJ0= +google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:B5xPO//w8qmBDjGReYLpR6UJPnkldGkCSMoH/2vxJeg= +google.golang.org/genproto/googleapis/bytestream v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:ZSvZ8l+AWJwXw91DoTjWjaVLpWU6o0eZ4YLYpH8aLeQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97/go.mod h1:v7nGkzlmW8P3n/bKmWBn2WpBjpOEx8Q6gMueudAmKfY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:swOH3j0KzcDDgGUWr+SNpyTen5YrXjS3eyPzFYKc6lc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405/go.mod h1:67X1fPuzjcrkymZzZV1vvkFeTn2Rvc6lYF9MYFGCcwE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:oQ5rr10WTTMvP4A36n8JpR1OrO1BEiV4f78CneXZxkA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f/go.mod h1:L9KNLi232K1/xB6f7AlSX692koaRnKaWSR0stBki0Yc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917/go.mod h1:xtjpI3tXFPP051KaWnhvxkiubL/6dJ18vLVf7q2pTOU= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= +google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/plugin.go b/plugin.go index d0a0864..62e202f 100644 --- a/plugin.go +++ b/plugin.go @@ -1,8 +1,7 @@ package google_pub_sub import ( - "github.com/roadrunner-server/api/v4/plugins/v1/jobs" - pq "github.com/roadrunner-server/api/v4/plugins/v1/priority_queue" + "github.com/roadrunner-server/api/v4/plugins/v3/jobs" "github.com/roadrunner-server/endure/v2/dep" "github.com/roadrunner-server/errors" "github.com/roadrunner-server/google-pub-sub/v4/pubsubjobs" @@ -10,7 +9,10 @@ import ( "go.uber.org/zap" ) -const pluginName string = "google-pub-sub" +const ( + pluginName string = "google-pub-sub" + masterPluginName string = "jobs" +) type Plugin struct { log *zap.Logger @@ -34,7 +36,7 @@ type Tracer interface { } func (p *Plugin) Init(log Logger, cfg Configurer) error { - if !cfg.Has(pluginName) { + if !cfg.Has(pluginName) && !cfg.Has(masterPluginName) { return errors.E(errors.Disabled) } @@ -56,12 +58,10 @@ func (p *Plugin) Collects() []*dep.In { } } -// DriverFromConfig constructs kafka driver from the .rr.yaml configuration -func (p *Plugin) DriverFromConfig(configKey string, pq pq.Queue, pipeline jobs.Pipeline, _ chan<- jobs.Commander) (jobs.Driver, error) { - return pubsubjobs.FromConfig(p.tracer, configKey, p.log, p.cfg, pipeline, pq) +func (p *Plugin) DriverFromConfig(configKey string, pq jobs.Queue, pipeline jobs.Pipeline, _ chan<- jobs.Commander) (jobs.Driver, error) { + return pubsubjobs.FromConfig(p.tracer, configKey, pipeline, p.log, p.cfg, pq) } -// DriverFromPipeline constructs kafka driver from pipeline -func (p *Plugin) DriverFromPipeline(pipe jobs.Pipeline, pq pq.Queue, _ chan<- jobs.Commander) (jobs.Driver, error) { +func (p *Plugin) DriverFromPipeline(pipe jobs.Pipeline, pq jobs.Queue, _ chan<- jobs.Commander) (jobs.Driver, error) { return pubsubjobs.FromPipeline(p.tracer, pipe, p.log, p.cfg, pq) } diff --git a/pubsubjobs/config.go b/pubsubjobs/config.go index 53fd8b1..666fb4d 100644 --- a/pubsubjobs/config.go +++ b/pubsubjobs/config.go @@ -1,5 +1,7 @@ package pubsubjobs +import "os" + // pipeline rabbitmq info const ( exchangeKey string = "exchange" @@ -9,11 +11,13 @@ const ( type config struct { // global ProjectID string `mapstructure:"project_id"` + Topic string `mapstructure:"topic"` + SkipTopicDeclaration bool `mapstructure:"skip_topic_declaration"` // local Prefetch int `mapstructure:"prefetch"` - Queue string `mapstructure:"queue"` Priority int64 `mapstructure:"priority"` + Host string `mapstructure:"host"` } func (c *config) InitDefault() { @@ -25,7 +29,8 @@ func (c *config) InitDefault() { c.Priority = 10 } - if c.Addr == "" { - c.Addr = "amqp://guest:guest@127.0.0.1:5672/" + if c.Host != "" { + // No possibility to set up emulator from client init + os.Setenv("PUBSUB_EMULATOR_HOST", c.Host) } } diff --git a/pubsubjobs/driver.go b/pubsubjobs/driver.go index e95c74e..8925478 100644 --- a/pubsubjobs/driver.go +++ b/pubsubjobs/driver.go @@ -2,12 +2,13 @@ package pubsubjobs import ( "context" + "strings" "sync" "sync/atomic" + "time" "cloud.google.com/go/pubsub" - "github.com/roadrunner-server/api/v4/plugins/v1/jobs" - pq "github.com/roadrunner-server/api/v4/plugins/v1/priority_queue" + "github.com/roadrunner-server/api/v4/plugins/v3/jobs" "github.com/roadrunner-server/errors" jprop "go.opentelemetry.io/contrib/propagators/jaeger" "go.opentelemetry.io/otel" @@ -32,19 +33,32 @@ type Configurer interface { } type Driver struct { - mu sync.Mutex - log *zap.Logger - pq pq.Queue - pipeline atomic.Pointer[jobs.Pipeline] - tracer *sdktrace.TracerProvider - prop propagation.TextMapPropagator - consumeAll bool + mu sync.Mutex + cond sync.Cond + + log *zap.Logger + pq jobs.Queue + pipeline atomic.Pointer[jobs.Pipeline] + tracer *sdktrace.TracerProvider + prop propagation.TextMapPropagator + consumeAll bool + skipDeclare bool + topic string + + // if user invoke several resume operations + listeners uint32 + + // func to cancel listener + cancel context.CancelFunc client *pubsub.Client + + stopped uint64 + pauseCh chan struct{} } -// FromConfig initializes rabbitmq pipeline -func FromConfig(tracer *sdktrace.TracerProvider, configKey string, log *zap.Logger, cfg Configurer, pipeline jobs.Pipeline, pq pq.Queue) (*Driver, error) { +// FromConfig initializes google_pub_sub_driver_ pipeline +func FromConfig(tracer *sdktrace.TracerProvider, configKey string, pipe jobs.Pipeline, log *zap.Logger, cfg Configurer, pq jobs.Queue) (*Driver, error) { const op = errors.Op("new_google_pub_sub_consumer") if tracer == nil { @@ -79,24 +93,35 @@ func FromConfig(tracer *sdktrace.TracerProvider, configKey string, log *zap.Logg // PARSE CONFIGURATION END ------- jb := &Driver{ - tracer: tracer, - prop: prop, - log: log, - pq: pq, + tracer: tracer, + prop: prop, + log: log, + skipDeclare: conf.SkipTopicDeclaration, + topic: conf.Topic, + pq: pq, + pauseCh: make(chan struct{}, 1), + cond: sync.Cond{L: &sync.Mutex{}}, } - jb.client, err = pubsub.NewClient(context.Background(), "") + ctx := context.Background() + jb.client, err = pubsub.NewClient(ctx, conf.ProjectID) if err != nil { return nil, err } - jb.pipeline.Store(&pipeline) + err = jb.manageTopic(ctx) + if err != nil { + return nil, errors.E(op, err) + } + + jb.pipeline.Store(&pipe) + time.Sleep(time.Second) return jb, nil } // FromPipeline initializes consumer from pipeline -func FromPipeline(tracer *sdktrace.TracerProvider, pipeline jobs.Pipeline, log *zap.Logger, cfg Configurer, pq pq.Queue) (*Driver, error) { +func FromPipeline(tracer *sdktrace.TracerProvider, pipe jobs.Pipeline, log *zap.Logger, cfg Configurer, pq jobs.Queue) (*Driver, error) { const op = errors.Op("new_google_pub_sub_consumer_from_pipeline") if tracer == nil { tracer = sdktrace.NewTracerProvider() @@ -120,35 +145,69 @@ func FromPipeline(tracer *sdktrace.TracerProvider, pipeline jobs.Pipeline, log * // PARSE CONFIGURATION ------- jb := &Driver{ - prop: prop, - tracer: tracer, - log: log, - pq: pq, + prop: prop, + tracer: tracer, + log: log, + pq: pq, + pauseCh: make(chan struct{}, 1), + skipDeclare: conf.SkipTopicDeclaration, + topic: conf.Topic, + cond: sync.Cond{L: &sync.Mutex{}}, + } + + err = jb.manageTopic(context.Background()) + if err != nil { + return nil, errors.E(op, err) } // register the pipeline - jb.pipeline.Store(&pipeline) + jb.pipeline.Store(&pipe) + time.Sleep(time.Second) return jb, nil } -func (d *Driver) Push(ctx context.Context, job jobs.Job) error { - const op = errors.Op("rabbitmq_push") +func (d *Driver) Push(ctx context.Context, jb jobs.Message) error { + const op = errors.Op("google_pub_sub_driver_push") // check if the pipeline registered ctx, span := trace.SpanFromContext(ctx).TracerProvider().Tracer(tracerName).Start(ctx, "google_pub_sub_push") defer span.End() + result := d.client.Topic(d.topic).Publish(ctx, &pubsub.Message{Data: jb.Payload()}) + id, err := result.Get(ctx) + if err != nil { + return err + } + + d.log.Debug("Message published", zap.String("messageId", id)) + return nil } func (d *Driver) Run(ctx context.Context, p jobs.Pipeline) error { - //start := time.Now().UTC() - const op = errors.Op("rabbit_run") + start := time.Now().UTC() + const op = errors.Op("google_pub_sub_driver_run") _, span := trace.SpanFromContext(ctx).TracerProvider().Tracer(tracerName).Start(ctx, "google_pub_sub_run") defer span.End() + d.mu.Lock() + defer d.mu.Unlock() + + pipe := *d.pipeline.Load() + if pipe.Name() != p.Name() { + return errors.E(op, errors.Errorf("no such pipeline registered: %s", pipe.Name())) + } + + atomic.AddUint32(&d.listeners, 1) + + // start listener + var ctxCancel context.Context + ctxCancel, d.cancel = context.WithCancel(context.Background()) + d.listen(ctxCancel) + + d.log.Debug("pipeline was started", zap.String("driver", pipe.Driver()), zap.String("pipeline", pipe.Name()), zap.Time("start", start), zap.Duration("elapsed", time.Since(start))) return nil } @@ -161,38 +220,108 @@ func (d *Driver) State(ctx context.Context) (*jobs.State, error) { } func (d *Driver) Pause(ctx context.Context, p string) error { - //start := time.Now().UTC() - pipe := *d.pipeline.Load() + start := time.Now().UTC() _, span := trace.SpanFromContext(ctx).TracerProvider().Tracer(tracerName).Start(ctx, "google_pub_sub_resume") defer span.End() + // load atomic value + pipe := *d.pipeline.Load() if pipe.Name() != p { return errors.Errorf("no such pipeline: %s", pipe.Name()) } + l := atomic.LoadUint32(&d.listeners) + // no active listeners + if l == 0 { + return errors.Str("no active listeners, nothing to pause") + } + + atomic.AddUint32(&d.listeners, ^uint32(0)) + + if d.cancel != nil { + d.cancel() + } + + // stop consume + d.pauseCh <- struct{}{} + // if blocked, let 1 item to pass to unblock the listener and close the pipe + d.cond.Signal() + + d.log.Debug("pipeline was paused", zap.String("driver", pipe.Driver()), zap.String("pipeline", pipe.Name()), zap.Time("start", time.Now().UTC()), zap.Duration("elapsed", time.Since(start))) + return nil } func (d *Driver) Resume(ctx context.Context, p string) error { - //start := time.Now().UTC() + start := time.Now().UTC() _, span := trace.SpanFromContext(ctx).TracerProvider().Tracer(tracerName).Start(ctx, "google_pub_sub_resume") defer span.End() + d.mu.Lock() + defer d.mu.Unlock() + + // load atomic value pipe := *d.pipeline.Load() if pipe.Name() != p { return errors.Errorf("no such pipeline: %s", pipe.Name()) } + l := atomic.LoadUint32(&d.listeners) + // no active listeners + if l == 1 { + return errors.Str("listener is already in the active state") + } + + // start listener + var ctxCancel context.Context + ctxCancel, d.cancel = context.WithCancel(context.Background()) + d.listen(ctxCancel) + + // increase num of listeners + atomic.AddUint32(&d.listeners, 1) + d.log.Debug("pipeline was resumed", zap.String("driver", pipe.Driver()), zap.String("pipeline", pipe.Name()), zap.Time("start", time.Now().UTC()), zap.Duration("elapsed", time.Since(start))) + return nil } func (d *Driver) Stop(ctx context.Context) error { - //start := time.Now().UTC() + start := time.Now().UTC() _, span := trace.SpanFromContext(ctx).TracerProvider().Tracer(tracerName).Start(ctx, "google_pub_sub_stop") defer span.End() + atomic.StoreUint64(&d.stopped, 1) + pipe := *d.pipeline.Load() + _ = d.pq.Remove(pipe.Name()) + + if atomic.LoadUint32(&d.listeners) > 0 { + if d.cancel != nil { + d.cancel() + } + // if blocked, let 1 item to pass to unblock the listener and close the pipe + d.cond.Signal() + + d.pauseCh <- struct{}{} + } + + d.log.Debug("pipeline was stopped", zap.String("driver", pipe.Driver()), zap.String("pipeline", pipe.Name()), zap.Time("start", time.Now().UTC()), zap.Duration("elapsed", time.Since(start))) + return nil +} + +func (d *Driver) manageTopic(ctx context.Context) error { + if d.skipDeclare { + return nil + } + + _, err := d.client.CreateTopic(ctx, d.topic) + if err != nil { + if strings.Contains(err.Error(), "Topic already exists") { + return nil + } + return err + } + return nil } diff --git a/pubsubjobs/listener.go b/pubsubjobs/listener.go index 1ac7247..40a8572 100644 --- a/pubsubjobs/listener.go +++ b/pubsubjobs/listener.go @@ -1,13 +1,16 @@ package pubsubjobs -import ( - "context" +import "context" - "cloud.google.com/go/pubsub" -) - -func (d *Driver) listener() { - s, err := d.client.Topic("").Subscriptions().Next() - s.Receive(context.Background(), func(ctx context.Context, message *pubsub.Message) { - }) +func (d *Driver) listen(ctx context.Context) { + go func() { + for { + select { + case <-d.pauseCh: + d.log.Debug("listener was stopped") + return + default: + } + } + }() } diff --git a/tests/configs/.rr-init.yaml b/tests/configs/.rr-init.yaml new file mode 100644 index 0000000..6ba9a26 --- /dev/null +++ b/tests/configs/.rr-init.yaml @@ -0,0 +1,57 @@ +version: '3' + +rpc: + listen: tcp://127.0.0.1:6001 + +server: + command: "php php_test_files/jobs/jobs_ok.php" + relay: "pipes" + relay_timeout: "20s" + +google-pub-sub: + project_id: test + topic: rrTopic + skip_topic_declaration: false + host: 127.0.0.1:8085 + +logs: + level: debug + encoding: console + mode: development + +jobs: + num_pollers: 10 + pipeline_size: 100000 + pool: + num_workers: 10 + max_jobs: 0 + allocate_timeout: 60s + destroy_timeout: 60s + + pipelines: + test-1: + driver: google-pub-sub + config: + prefetch: 1000 + visibility_timeout: 0 + wait_time_seconds: 0 + queue: default + attributes: + DelaySeconds: 0 + MaximumMessageSize: 262144 + MessageRetentionPeriod: 345600 + ReceiveMessageWaitTimeSeconds: 0 + VisibilityTimeout: 30 + tags: + test: "tag" + + test-2: + driver: google-pub-sub + config: + prefetch: 1000 + queue: default-2 + attributes: + MessageRetentionPeriod: 86400 + tags: + test: "tag" + consume: [ "test-1", "test-2" ] diff --git a/tests/env/docker-compose-emulator-local.yaml b/tests/env/docker-compose-emulator-local.yaml new file mode 100644 index 0000000..fc8c96e --- /dev/null +++ b/tests/env/docker-compose-emulator-local.yaml @@ -0,0 +1,8 @@ +version: "3.7" + +services: + pub-sub-emulator: + image: google/cloud-sdk:latest + command: ["gcloud", "beta", "emulators", "pubsub", "start", "--host-port=0.0.0.0:8085", "--project=test"] + ports: + - "8085:8085" \ No newline at end of file diff --git a/tests/go.mod b/tests/go.mod new file mode 100644 index 0000000..db5ed04 --- /dev/null +++ b/tests/go.mod @@ -0,0 +1,103 @@ +module tests + +go 1.21 + +toolchain go1.21.0 + +replace github.com/roadrunner-server/google-pub-sub/v4 => ../ + +require ( + github.com/google/uuid v1.6.0 + github.com/roadrunner-server/api/v4 v4.10.0 + github.com/roadrunner-server/config/v4 v4.6.7 + github.com/roadrunner-server/endure/v2 v2.4.3 + github.com/roadrunner-server/google-pub-sub/v4 v4.0.0-00010101000000-000000000000 + github.com/roadrunner-server/goridge/v3 v3.8.1 + github.com/roadrunner-server/informer/v4 v4.3.14 + github.com/roadrunner-server/jobs/v4 v4.7.13 + github.com/roadrunner-server/logger/v4 v4.2.14 + github.com/roadrunner-server/resetter/v4 v4.0.21 + github.com/roadrunner-server/rpc/v4 v4.2.15 + github.com/roadrunner-server/server/v4 v4.5.7 + github.com/stretchr/testify v1.8.4 +) + +require ( + cloud.google.com/go v0.112.0 // indirect + cloud.google.com/go/compute v1.23.3 // indirect + cloud.google.com/go/compute/metadata v0.2.3 // indirect + cloud.google.com/go/iam v1.1.5 // indirect + cloud.google.com/go/pubsub v1.36.1 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/fatih/color v1.16.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/go-logr/logr v1.4.1 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect + github.com/goccy/go-json v0.10.2 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/s2a-go v0.1.7 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect + github.com/googleapis/gax-go/v2 v2.12.0 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/joho/godotenv v1.5.1 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/pelletier/go-toml/v2 v2.1.1 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/prometheus/client_golang v1.18.0 // indirect + github.com/prometheus/client_model v0.5.0 // indirect + github.com/prometheus/common v0.46.0 // indirect + github.com/prometheus/procfs v0.12.0 // indirect + github.com/roadrunner-server/errors v1.4.0 // indirect + github.com/roadrunner-server/sdk/v4 v4.6.0 // indirect + github.com/roadrunner-server/tcplisten v1.4.0 // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/shirou/gopsutil v3.21.11+incompatible // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cast v1.6.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/viper v1.18.2 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + github.com/tklauser/go-sysconf v0.3.13 // indirect + github.com/tklauser/numcpus v0.7.0 // indirect + github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect + github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect + github.com/yusufpapurcu/wmi v1.2.4 // indirect + go.opencensus.io v0.24.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 // indirect + go.opentelemetry.io/contrib/propagators/jaeger v1.23.0 // indirect + go.opentelemetry.io/otel v1.23.1 // indirect + go.opentelemetry.io/otel/metric v1.23.1 // indirect + go.opentelemetry.io/otel/sdk v1.23.1 // indirect + go.opentelemetry.io/otel/trace v1.23.1 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.26.0 // indirect + golang.org/x/crypto v0.18.0 // indirect + golang.org/x/exp v0.0.0-20240119083558-1b970713d09a // indirect + golang.org/x/net v0.20.0 // indirect + golang.org/x/oauth2 v0.16.0 // indirect + golang.org/x/sync v0.6.0 // indirect + golang.org/x/sys v0.16.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/time v0.5.0 // indirect + google.golang.org/api v0.160.0 // indirect + google.golang.org/appengine v1.6.8 // indirect + google.golang.org/genproto v0.0.0-20240116215550-a9fa1716bcac // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240125205218-1f4bbc51befe // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac // indirect + google.golang.org/grpc v1.61.0 // indirect + google.golang.org/protobuf v1.32.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/tests/go.sum b/tests/go.sum new file mode 100644 index 0000000..0116207 --- /dev/null +++ b/tests/go.sum @@ -0,0 +1,319 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.112.0 h1:tpFCD7hpHFlQ8yPwT3x+QeXqc2T6+n6T+hmABHfDUSM= +cloud.google.com/go v0.112.0/go.mod h1:3jEEVwZ/MHU4djK5t5RHuKOA/GbLddgTdVubX1qnPD4= +cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk= +cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI= +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/iam v1.1.5 h1:1jTsCu4bcsNsE4iiqNT5SHwrDRCfRmIaaaVFhRveTJI= +cloud.google.com/go/iam v1.1.5/go.mod h1:rB6P/Ic3mykPbFio+vo7403drjlgvoWfYpJhMXEbzv8= +cloud.google.com/go/kms v1.15.5 h1:pj1sRfut2eRbD9pFRjNnPNg/CzJPuQAzUujMIM1vVeM= +cloud.google.com/go/kms v1.15.5/go.mod h1:cU2H5jnp6G2TDpUGZyqTCoy1n16fbubHZjmVXSMtwDI= +cloud.google.com/go/pubsub v1.36.1 h1:dfEPuGCHGbWUhaMCTHUFjfroILEkx55iUmKBZTP5f+Y= +cloud.google.com/go/pubsub v1.36.1/go.mod h1:iYjCa9EzWOoBiTdd4ps7QoMtMln5NwaZQpK1hbRfBDE= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +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/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101 h1:7To3pQ+pZo0i3dsWEbinPNFs5gPSBOsJtx3wTT94VBY= +github.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +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/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/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA= +github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= +github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= +github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= +github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= +github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= +github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk= +github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= +github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= +github.com/prometheus/common v0.46.0 h1:doXzt5ybi1HBKpsZOL0sSkaNHJJqkyfEWZGGqqScV0Y= +github.com/prometheus/common v0.46.0/go.mod h1:Tp0qkxpb9Jsg54QMe+EAmqXkSV7Evdy1BTn+g2pa/hQ= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= +github.com/roadrunner-server/api/v4 v4.10.0 h1:tF6vmA6MaQyOL/GQQc+nyj356oX3UoQBd+SXNtsu+bU= +github.com/roadrunner-server/api/v4 v4.10.0/go.mod h1:ou9QviOd5dxl3to1+BV4iZ3lnMLxuE/HqESNW5PDnw0= +github.com/roadrunner-server/config/v4 v4.6.7 h1:Ak6e8iLkT6nbbDANXk0gSXHvm9OjA04pJZ/ErJQdyqE= +github.com/roadrunner-server/config/v4 v4.6.7/go.mod h1:0xhw9NBCb08vQFW3rui9WxHz2hNcr68Jd8HohnFZihw= +github.com/roadrunner-server/endure/v2 v2.4.3 h1:R9DdsLiLjtSFivZ1HKk/1eDZ0TYaKHQzakVwz9D2hto= +github.com/roadrunner-server/endure/v2 v2.4.3/go.mod h1:4n3PdwZ3h/IRL2enDGvEVXtaQgqRnZ74VOyZtOJq528= +github.com/roadrunner-server/errors v1.4.0 h1:Odjg3VZrj1q5Y8ILwoN+JgERyv0pkhrWPNOM4h68iQ8= +github.com/roadrunner-server/errors v1.4.0/go.mod h1:78PvraAFj+Sxy5nDmo0S+h6rEMLFIDszWZxA3B0sPAs= +github.com/roadrunner-server/goridge/v3 v3.8.1 h1:mdS5lDKQwPuVJ2jwW7l5cngJNJiie7xEGwpgw7a6CuQ= +github.com/roadrunner-server/goridge/v3 v3.8.1/go.mod h1:L5UkNzD8aKLz6TzpqmmiHOJ6EnsadsWEYNoqK/4qoK0= +github.com/roadrunner-server/informer/v4 v4.3.14 h1:eJWI079rMBJBkYVWwtFdkozGw9hwI2iBfl/E5LzVfSM= +github.com/roadrunner-server/informer/v4 v4.3.14/go.mod h1:Ir5FsF7/BndYNtzLJvRz/4sJaMGe3jojDDG6FAWeeXk= +github.com/roadrunner-server/jobs/v4 v4.7.13 h1:8bzZzMEykSnFhzkCWuQ/Lq1Q2LR1F9+P0dc1rA0eB9A= +github.com/roadrunner-server/jobs/v4 v4.7.13/go.mod h1:32iG5PZaDPOAKX6WbOwPm4rAHzCYC3s3DIPy374MjFc= +github.com/roadrunner-server/logger/v4 v4.2.14 h1:BVJqmdsI+tIkspSyTd5MsqvHX2w2oxGYjRTSh5kDL1w= +github.com/roadrunner-server/logger/v4 v4.2.14/go.mod h1:z8ainOGoCNey7vEAEfiVAMYmOmfAUoN3z13Hc0co1Ho= +github.com/roadrunner-server/resetter/v4 v4.0.21 h1:C3BF7YLTcZfqTKUZUudaTQ/IUfinz4VmshTEhywipx0= +github.com/roadrunner-server/resetter/v4 v4.0.21/go.mod h1:yn/1UXSceuBQ+R+loXSKqmKtx6htcVRza2Ifni0i3FA= +github.com/roadrunner-server/rpc/v4 v4.2.15 h1:cGKsF2tYqe8k+oRM3aWsbddpihY1vhjd9n6/w6AqEqA= +github.com/roadrunner-server/rpc/v4 v4.2.15/go.mod h1:Hxduua91f9pAkMzoJzG+a5Ep3azHeBsp2I842A6X+mQ= +github.com/roadrunner-server/sdk/v4 v4.6.0 h1:dXMN7V8+VKjQAZamhKBizPGSqrpfAfVG6r4OCF66hNY= +github.com/roadrunner-server/sdk/v4 v4.6.0/go.mod h1:YzRn2S947MqcnBcOuwu04CpAhsQGf6JBf+xY+njqu5o= +github.com/roadrunner-server/server/v4 v4.5.7 h1:F337i6cGmwssle8jrkjwOEmYd230wny6wGnFIr17yrQ= +github.com/roadrunner-server/server/v4 v4.5.7/go.mod h1:ca/vVreZ/w7bta1NcJNEysyEoyKoYkh/n0cILed786Y= +github.com/roadrunner-server/tcplisten v1.4.0 h1:yWo09zktv/CSV6VywLfw4pwNcUchgTiIrW4uIICtO5M= +github.com/roadrunner-server/tcplisten v1.4.0/go.mod h1:A6+VSnW2ETGnN/e/CMdP63ZXqQDaC0UDMU6QmyuB0yM= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= +github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= +github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= +github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +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.13 h1:GBUpcahXSpR2xN01jhkNAbTLRk2Yzgggk8IM08lq3r4= +github.com/tklauser/go-sysconf v0.3.13/go.mod h1:zwleP4Q4OehZHGn4CYZDipCgg9usW5IJePewFCGVEa0= +github.com/tklauser/numcpus v0.7.0 h1:yjuerZP127QG9m5Zh/mSO4wqurYil27tHrqwRoRjpr4= +github.com/tklauser/numcpus v0.7.0/go.mod h1:bb6dMVcj8A42tSE7i32fsIUCbQNllK5iDguyOZRUzAY= +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/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= +github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +go.einride.tech/aip v0.66.0 h1:XfV+NQX6L7EOYK11yoHHFtndeaWh3KbD9/cN/6iWEt8= +go.einride.tech/aip v0.66.0/go.mod h1:qAhMsfT7plxBX+Oy7Huol6YUvZ0ZzdUz26yZsQwfl1M= +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/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0 h1:UNQQKPfTDe1J81ViolILjTKPr9WetKW6uei2hFgJmFs= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0/go.mod h1:r9vWsPS/3AQItv3OSlEJ/E4mbrhUbbw18meOjArPtKQ= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 h1:sv9kVfal0MK0wBMCOGr+HeJm9v803BkJxGrk2au7j08= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0/go.mod h1:SK2UL73Zy1quvRPonmOmRDiWk1KBV3LyIeeIxcEApWw= +go.opentelemetry.io/contrib/propagators/jaeger v1.23.0 h1:KFxfTCTkH1usVFzDaWzbmNdFX7ybUTCtkLsUTww0nG4= +go.opentelemetry.io/contrib/propagators/jaeger v1.23.0/go.mod h1:xU+81opGquQICJGzwscLXAQLnIPWI+q7Zu4AQSrgXf8= +go.opentelemetry.io/otel v1.23.1 h1:Za4UzOqJYS+MUczKI320AtqZHZb7EqxO00jAHE0jmQY= +go.opentelemetry.io/otel v1.23.1/go.mod h1:Td0134eafDLcTS4y+zQ26GE8u3dEuRBiBCTUIRHaikA= +go.opentelemetry.io/otel/metric v1.23.1 h1:PQJmqJ9u2QaJLBOELl1cxIdPcpbwzbkjfEyelTl2rlo= +go.opentelemetry.io/otel/metric v1.23.1/go.mod h1:mpG2QPlAfnK8yNhNJAxDZruU9Y1/HubbC+KyH8FaCWI= +go.opentelemetry.io/otel/sdk v1.23.1 h1:O7JmZw0h76if63LQdsBMKQDWNb5oEcOThG9IrxscV+E= +go.opentelemetry.io/otel/sdk v1.23.1/go.mod h1:LzdEVR5am1uKOOwfBWFef2DCi1nu3SA8XQxx2IerWFk= +go.opentelemetry.io/otel/trace v1.23.1 h1:4LrmmEd8AU2rFvU1zegmvqW7+kWarxtNOPyeL6HmYY8= +go.opentelemetry.io/otel/trace v1.23.1/go.mod h1:4IpnpJFwr1mo/6HL8XIPJaE9y0+u1KcVmuW7dwFSVrI= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA= +golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= +golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.160.0 h1:SEspjXHVqE1m5a1fRy8JFB+5jSu+V0GEDKDghF3ttO4= +google.golang.org/api v0.160.0/go.mod h1:0mu0TpK33qnydLvWqbImq2b1eQ5FHRSDCBzAxX9ZHyw= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20240116215550-a9fa1716bcac h1:ZL/Teoy/ZGnzyrqK/Optxxp2pmVh+fmJ97slxSRyzUg= +google.golang.org/genproto v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:+Rvu7ElI+aLzyDQhpHMFMMltsD6m7nqpuWDd2CwJw3k= +google.golang.org/genproto/googleapis/api v0.0.0-20240125205218-1f4bbc51befe h1:0poefMBYvYbs7g5UkjS6HcxBPaTRAmznle9jnxYoAI8= +google.golang.org/genproto/googleapis/api v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:4jWUdICTdgc3Ibxmr8nAJiiLHwQBY0UI0XZcEMaFKaA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac h1:nUQEQmH/csSvFECKYRv6HWEyypysidKl2I6Qpsglq/0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:daQN87bsDqDoe316QbbvX60nMoJQa4r6Ds0ZuoAe5yA= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.61.0 h1:TOvOcuXn30kRao+gfcvsebNEa5iZIiLkisYEkf7R7o0= +google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= +google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/tests/helpers/helpers.go b/tests/helpers/helpers.go new file mode 100644 index 0000000..a0e3804 --- /dev/null +++ b/tests/helpers/helpers.go @@ -0,0 +1,203 @@ +package helpers + +import ( + "bytes" + "net" + "net/http" + "net/rpc" + "testing" + "time" + + "github.com/google/uuid" + jobsProto "github.com/roadrunner-server/api/v4/build/jobs/v1" + jobState "github.com/roadrunner-server/api/v4/plugins/v1/jobs" + goridgeRpc "github.com/roadrunner-server/goridge/v3/pkg/rpc" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +const ( + push string = "jobs.Push" + pause string = "jobs.Pause" + destroy string = "jobs.Destroy" + resume string = "jobs.Resume" + stat string = "jobs.Stat" +) + +func ResumePipes(address string, pipes ...string) func(t *testing.T) { + return func(t *testing.T) { + conn, err := net.Dial("tcp", address) + require.NoError(t, err) + client := rpc.NewClientWithCodec(goridgeRpc.NewClientCodec(conn)) + + pipe := &jobsProto.Pipelines{Pipelines: make([]string, len(pipes))} + + for i := 0; i < len(pipes); i++ { + pipe.GetPipelines()[i] = pipes[i] + } + + er := &jobsProto.Empty{} + err = client.Call(resume, pipe, er) + require.NoError(t, err) + } +} + +func PushToPipe(pipeline string, autoAck bool, address string) func(t *testing.T) { + return func(t *testing.T) { + conn, err := net.Dial("tcp", address) + require.NoError(t, err) + client := rpc.NewClientWithCodec(goridgeRpc.NewClientCodec(conn)) + + req := &jobsProto.PushRequest{Job: createDummyJob(pipeline, autoAck)} + + er := &jobsProto.Empty{} + err = client.Call(push, req, er) + require.NoError(t, err) + } +} + +func PushToPipeDelayed(address string, pipeline string, delay int64) func(t *testing.T) { + return func(t *testing.T) { + conn, err := net.Dial("tcp", address) + assert.NoError(t, err) + client := rpc.NewClientWithCodec(goridgeRpc.NewClientCodec(conn)) + + req := &jobsProto.PushRequest{Job: &jobsProto.Job{ + Job: "some/php/namespace", + Id: uuid.NewString(), + Payload: []byte(`{"hello":"world"}`), + Headers: map[string]*jobsProto.HeaderValue{"test": {Value: []string{"test2"}}}, + Options: &jobsProto.Options{ + Priority: 1, + Pipeline: pipeline, + Delay: delay, + }, + }} + + er := &jobsProto.Empty{} + err = client.Call(push, req, er) + assert.NoError(t, err) + } +} + +func createDummyJob(pipeline string, autoAck bool) *jobsProto.Job { + return &jobsProto.Job{ + Job: "some/php/namespace", + Id: uuid.NewString(), + Payload: []byte(`{"hello":"world"}`), + Headers: map[string]*jobsProto.HeaderValue{"test": {Value: []string{"test2"}}}, + Options: &jobsProto.Options{ + AutoAck: autoAck, + Priority: 1, + Pipeline: pipeline, + Topic: pipeline, + }, + } +} + +func PausePipelines(address string, pipes ...string) func(t *testing.T) { + return func(t *testing.T) { + conn, err := net.Dial("tcp", address) + assert.NoError(t, err) + client := rpc.NewClientWithCodec(goridgeRpc.NewClientCodec(conn)) + + pipe := &jobsProto.Pipelines{Pipelines: make([]string, len(pipes))} + + for i := 0; i < len(pipes); i++ { + pipe.GetPipelines()[i] = pipes[i] + } + + er := &jobsProto.Empty{} + err = client.Call(pause, pipe, er) + assert.NoError(t, err) + } +} + +func DestroyPipelines(address string, pipes ...string) func(t *testing.T) { + return func(t *testing.T) { + conn, err := net.Dial("tcp", address) + assert.NoError(t, err) + client := rpc.NewClientWithCodec(goridgeRpc.NewClientCodec(conn)) + + pipe := &jobsProto.Pipelines{Pipelines: make([]string, len(pipes))} + + for i := 0; i < len(pipes); i++ { + pipe.GetPipelines()[i] = pipes[i] + } + + for i := 0; i < 10; i++ { + er := &jobsProto.Empty{} + err = client.Call(destroy, pipe, er) + if err != nil { + time.Sleep(time.Second) + continue + } + assert.NoError(t, err) + break + } + } +} + +func Stats(address string, state *jobState.State) func(t *testing.T) { + return func(t *testing.T) { + conn, err := net.Dial("tcp", address) + require.NoError(t, err) + client := rpc.NewClientWithCodec(goridgeRpc.NewClientCodec(conn)) + + st := &jobsProto.Stats{} + er := &jobsProto.Empty{} + + err = client.Call(stat, er, st) + require.NoError(t, err) + require.NotNil(t, st) + + state.Queue = st.Stats[0].Queue + state.Pipeline = st.Stats[0].Pipeline + state.Driver = st.Stats[0].Driver + state.Active = st.Stats[0].Active + state.Delayed = st.Stats[0].Delayed + state.Reserved = st.Stats[0].Reserved + state.Ready = st.Stats[0].Ready + state.Priority = st.Stats[0].Priority + } +} + +func EnableProxy(name string, t *testing.T) { + buf := new(bytes.Buffer) + buf.WriteString(`{"enabled":true}`) + + resp, err := http.Post("http://127.0.0.1:8474/proxies/"+name, "application/json", buf) //nolint:noctx + require.NoError(t, err) + require.Equal(t, 200, resp.StatusCode) + if resp.Body != nil { + _ = resp.Body.Close() + } +} + +func DisableProxy(name string, t *testing.T) { + buf := new(bytes.Buffer) + buf.WriteString(`{"enabled":false}`) + + resp, err := http.Post("http://127.0.0.1:8474/proxies/"+name, "application/json", buf) //nolint:noctx + require.NoError(t, err) + require.Equal(t, 200, resp.StatusCode) + if resp.Body != nil { + _ = resp.Body.Close() + } +} + +func DeleteProxy(name string, t *testing.T) { + client := &http.Client{} + + req, err := http.NewRequest(http.MethodDelete, "http://127.0.0.1:8474/proxies/"+name, nil) //nolint:noctx + require.NoError(t, err) + + resp, err := client.Do(req) + require.NoError(t, err) + + require.NoError(t, err) + require.Equal(t, 204, resp.StatusCode) + if resp.Body != nil { + _ = resp.Body.Close() + } +} diff --git a/tests/jobs_test.go b/tests/jobs_test.go new file mode 100644 index 0000000..d2c95fa --- /dev/null +++ b/tests/jobs_test.go @@ -0,0 +1,101 @@ +package pubsubjobs + +import ( + "log/slog" + "os" + "os/signal" + "sync" + "syscall" + "testing" + "time" + + "tests/helpers" + + "github.com/roadrunner-server/config/v4" + "github.com/roadrunner-server/endure/v2" + "github.com/roadrunner-server/informer/v4" + "github.com/roadrunner-server/jobs/v4" + "github.com/roadrunner-server/logger/v4" + "github.com/roadrunner-server/resetter/v4" + rpcPlugin "github.com/roadrunner-server/rpc/v4" + "github.com/roadrunner-server/server/v4" + googlePubSub "github.com/roadrunner-server/google-pub-sub/v4" + "github.com/stretchr/testify/assert" +) + +func TestInit(t *testing.T) { + cont := endure.New(slog.LevelDebug) + + cfg := &config.Plugin{ + Version: "2023.3.0", + Path: "configs/.rr-init.yaml", + Prefix: "rr", + } + + err := cont.RegisterAll( + cfg, + &server.Plugin{}, + &rpcPlugin.Plugin{}, + &logger.Plugin{}, + &jobs.Plugin{}, + &resetter.Plugin{}, + &informer.Plugin{}, + &googlePubSub.Plugin{}, + ) + assert.NoError(t, err) + + err = cont.Init() + if err != nil { + t.Fatal(err) + } + + ch, err := cont.Serve() + if err != nil { + t.Fatal(err) + } + + sig := make(chan os.Signal, 1) + signal.Notify(sig, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) + + wg := &sync.WaitGroup{} + wg.Add(1) + + stopCh := make(chan struct{}, 1) + + go func() { + defer wg.Done() + for { + select { + case e := <-ch: + assert.Fail(t, "error", e.Error.Error()) + err = cont.Stop() + if err != nil { + assert.FailNow(t, "error", err.Error()) + } + case <-sig: + err = cont.Stop() + if err != nil { + assert.FailNow(t, "error", err.Error()) + } + return + case <-stopCh: + // timeout + err = cont.Stop() + if err != nil { + assert.FailNow(t, "error", err.Error()) + } + return + } + } + }() + + time.Sleep(time.Second * 3) + t.Run("PushPipeline", helpers.PushToPipe("test-1", false, "127.0.0.1:6001")) + t.Run("PushPipeline", helpers.PushToPipe("test-2", false, "127.0.0.1:6001")) + time.Sleep(time.Second * 2) + + t.Run("DestroyPipeline", helpers.DestroyPipelines("127.0.0.1:6001", "test-1", "test-2")) + + stopCh <- struct{}{} + wg.Wait() +} diff --git a/tests/php_test_files/composer.json b/tests/php_test_files/composer.json new file mode 100644 index 0000000..d5dfa2e --- /dev/null +++ b/tests/php_test_files/composer.json @@ -0,0 +1,33 @@ +{ + "minimum-stability": "dev", + "prefer-stable": true, + "require": { + "ext-curl": "*", + "guzzlehttp/guzzle": "^6.5", + "nyholm/psr7": "^1.5", + "spiral/roadrunner-http": "^3.0", + "spiral/roadrunner-worker": "^3.0", + "spiral/tokenizer": ">=2.7", + "spiral/roadrunner-jobs": "^4.0", + "monolog/monolog": "^3.1", + "php-http/guzzle6-adapter": "~2", + "spiral/goridge": "^4.0", + "roadrunner-php/app-logger": "^1.0", + "open-telemetry/sdk": "^0.0.10", + "psr/http-factory": "^1.0", + "php-http/message-factory": "^1.1", + "spiral/roadrunner-services": "^2.0" + }, + "autoload": { + "psr-4": { + "": "src" + } + }, + "name": "test/test", + "description": "test", + "config": { + "allow-plugins": { + "php-http/discovery": true + } + } +} diff --git a/tests/php_test_files/jobs/jobs_bad_resp.php b/tests/php_test_files/jobs/jobs_bad_resp.php new file mode 100644 index 0000000..6233246 --- /dev/null +++ b/tests/php_test_files/jobs/jobs_bad_resp.php @@ -0,0 +1,22 @@ +waitPayload()) { + try { + $rr->respond(new RoadRunner\Payload('foo')); + } catch (\Throwable $e) { + $rr->error((string)$e); + } +} diff --git a/tests/php_test_files/jobs/jobs_create_memory.php b/tests/php_test_files/jobs/jobs_create_memory.php new file mode 100644 index 0000000..3b56d69 --- /dev/null +++ b/tests/php_test_files/jobs/jobs_create_memory.php @@ -0,0 +1,47 @@ +create(new MemoryCreateInfo( + name: 'example', + priority: 10, + prefetch: 100, +)); + +$queue1->resume(); + +// Create task prototype with default headers +$task1 = $queue1->create('ping', '{"site": "https://example.com"}') // Create task with "echo" name + ->withHeader('attempts', 4) // Number of attempts to execute the task + ->withHeader('retry-delay', 10); // Delay between attempts + +// Push "echo" task to the queue +$task1 = $queue1->dispatch($task1); + +// Select "local" pipeline from jobs +$queue2 = $jobs->connect('local'); +$queue2->resume(); + +// Create task prototype with default headers +$task = $queue2->create('ping', '{"site": "https://example.com"}') // Create task with "echo" name + ->withHeader('attempts', 4) // Number of attempts to execute the task + ->withHeader('retry-delay', 10); // Delay between attempts + +// Push "echo" task to the queue +$task = $queue2->dispatch($task); + +$consumer = new Spiral\RoadRunner\Jobs\Consumer(); + +while ($task = $consumer->waitTask()) { + $task->complete(); +} diff --git a/tests/php_test_files/jobs/jobs_err.php b/tests/php_test_files/jobs/jobs_err.php new file mode 100644 index 0000000..73509dc --- /dev/null +++ b/tests/php_test_files/jobs/jobs_err.php @@ -0,0 +1,31 @@ +waitTask()) { + try { + $headers = $task->getHeaders(); + $total_attempts = (int)$task->getHeaderLine("attempts") + 1; + + if ($total_attempts > 3) { + $task->complete(); + } else { + $task->withHeader("attempts",$total_attempts)->withDelay(5)->fail("failed", true); + } + } catch (\Throwable $e) { + $rr->error((string)$e); + } +} diff --git a/tests/php_test_files/jobs/jobs_ok.php b/tests/php_test_files/jobs/jobs_ok.php new file mode 100644 index 0000000..a2176c6 --- /dev/null +++ b/tests/php_test_files/jobs/jobs_ok.php @@ -0,0 +1,24 @@ +waitTask()) { + try { + $task->complete(); + } catch (\Throwable $e) { + $rr->error((string)$e); + } +} diff --git a/tests/php_test_files/jobs/jobs_ok_pq.php b/tests/php_test_files/jobs/jobs_ok_pq.php new file mode 100644 index 0000000..277a1dd --- /dev/null +++ b/tests/php_test_files/jobs/jobs_ok_pq.php @@ -0,0 +1,25 @@ +waitTask()) { + try { + sleep(15); + $task->complete(); + } catch (\Throwable $e) { + $rr->error((string)$e); + } +} diff --git a/tests/php_test_files/jobs/jobs_ok_queue_name_exist.php b/tests/php_test_files/jobs/jobs_ok_queue_name_exist.php new file mode 100644 index 0000000..a51c488 --- /dev/null +++ b/tests/php_test_files/jobs/jobs_ok_queue_name_exist.php @@ -0,0 +1,28 @@ +waitTask()) { + try { + if ('unknown' === $task->getQueue()) { + throw new RuntimeException('Queue name was not found'); + } + + $task->complete(); + } catch (\Throwable $e) { + $rr->error((string)$e); + } +} diff --git a/tests/php_test_files/jobs/jobs_ok_sleep1.php b/tests/php_test_files/jobs/jobs_ok_sleep1.php new file mode 100644 index 0000000..2894b6e --- /dev/null +++ b/tests/php_test_files/jobs/jobs_ok_sleep1.php @@ -0,0 +1,25 @@ +waitTask()) { + try { + sleep(1); + $task->complete(); + } catch (\Throwable $e) { + $rr->error((string)$e); + } +} diff --git a/tests/php_test_files/jobs/jobs_ok_slow.php b/tests/php_test_files/jobs/jobs_ok_slow.php new file mode 100644 index 0000000..cc06dab --- /dev/null +++ b/tests/php_test_files/jobs/jobs_ok_slow.php @@ -0,0 +1,25 @@ +waitTask()) { + try { + sleep(60); + $task->complete(); + } catch (\Throwable $e) { + $rr->error((string)$e); + } +} diff --git a/tests/php_test_files/jobs/jobs_ok_slow_rand.php b/tests/php_test_files/jobs/jobs_ok_slow_rand.php new file mode 100644 index 0000000..52a260c --- /dev/null +++ b/tests/php_test_files/jobs/jobs_ok_slow_rand.php @@ -0,0 +1,29 @@ +waitTask()) { + try { + $val = random_int(0, 1000); + if ($val > 995) { + sleep(60); + } + + $task->complete(); + } catch (\Throwable $e) { + $rr->error((string)$e); + } +} diff --git a/tests/php_test_files/jobs/jobs_send.php b/tests/php_test_files/jobs/jobs_send.php new file mode 100644 index 0000000..10f971c --- /dev/null +++ b/tests/php_test_files/jobs/jobs_send.php @@ -0,0 +1,23 @@ +connect('test-1'); + +$queue->dispatch( + $queue->create( + 'my-name', + ['foo' => 'bar'], + new KafkaOptions( + topic: 'mytopic', + offset: PartitionOffset::OFFSET_NEWEST + ) + ) +); From 4180ef73af1713cf6b011f7eb12db9642a8ae50e Mon Sep 17 00:00:00 2001 From: Kajetan Date: Fri, 9 Feb 2024 16:28:22 +0100 Subject: [PATCH 2/9] CI init config --- .github/dependabot.yml | 33 ++++++++ .github/pull_request_template.md | 24 ++++++ .github/workflows/codeql-analysis.yml | 73 +++++++++++++++++ .github/workflows/linters.yml | 23 ++++++ .github/workflows/linux.yml | 108 ++++++++++++++++++++++++++ 5 files changed, 261 insertions(+) create mode 100644 .github/dependabot.yml create mode 100644 .github/pull_request_template.md create mode 100644 .github/workflows/codeql-analysis.yml create mode 100644 .github/workflows/linters.yml create mode 100644 .github/workflows/linux.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..138fd11 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,33 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates +version: 2 + +updates: + - package-ecosystem: gomod # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: daily + reviewers: + - "rustatian" + assignees: + - "rustatian" + + - package-ecosystem: "docker" + directory: "/" + schedule: + interval: daily + reviewers: + - "rustatian" + assignees: + - "rustatian" + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: daily + reviewers: + - "rustatian" + assignees: + - "rustatian" diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..c346785 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,24 @@ +# Reason for This PR + +`[Author TODO: add issue # or explain reasoning.]` + +## Description of Changes + +`[Author TODO: add description of changes.]` + +## License Acceptance + +By submitting this pull request, I confirm that my contribution is made under +the terms of the MIT license. + +## PR Checklist + +`[Author TODO: Meet these criteria.]` +`[Reviewer TODO: Verify that these criteria are met. Request changes if not]` + +- [ ] All commits in this PR are signed (`git commit -s`). +- [ ] The reason for this PR is clearly provided (issue no. or explanation). +- [ ] The description of changes is clear and encompassing. +- [ ] Any required documentation changes (code and docs) are included in this PR. +- [ ] Any user-facing changes are mentioned in `CHANGELOG.md`. +- [ ] All added/changed functionality is tested. diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 0000000..0a5599e --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,73 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +name: "CodeQL" + +on: + push: + branches: [ master ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ master ] + schedule: + - cron: '0 15 * * 6' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + # Override automatic language detection by changing the below list + # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] + language: [ 'go' ] + # Learn more... + # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + # We must fetch at least the immediate parents so that if this is + # a pull request then we can checkout the head. + fetch-depth: 2 + + # Initializes the Golang environment for the CodeQL tools. + # https://github.com/github/codeql-action/issues/1842#issuecomment-1704398087 + - name: Install Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v3 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml new file mode 100644 index 0000000..f23dd39 --- /dev/null +++ b/.github/workflows/linters.yml @@ -0,0 +1,23 @@ +name: Linters + +on: [push, pull_request] + +jobs: + golangci-lint: + name: Golang-CI (lint) + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 # action page: + with: + go-version: stable + + - name: Run linter + uses: golangci/golangci-lint-action@v3.7.0 # Action page: + with: + version: v1.54 # without patch version + only-new-issues: false # show only new issues if it's a pull request + args: --timeout=10m --build-tags=race ./... diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml new file mode 100644 index 0000000..2df8787 --- /dev/null +++ b/.github/workflows/linux.yml @@ -0,0 +1,108 @@ +name: Google pub sub + +on: + push: + branches: + - master + - stable + pull_request: + branches: + - master + - stable + +jobs: + google_pub_sub_test: + name: Google pub sub plugin (Go ${{ matrix.go }}, PHP ${{ matrix.php }}, OS ${{matrix.os}}) + runs-on: ${{ matrix.os }} + timeout-minutes: 60 + strategy: + matrix: + php: [ "8.2" ] + go: [ stable ] + os: [ "ubuntu-latest" ] + steps: + - name: Set up Go ${{ matrix.go }} + uses: actions/setup-go@v5 # action page: + with: + go-version: ${{ matrix.go }} + + - name: Set up PHP ${{ matrix.php }} + uses: shivammathur/setup-php@v2 # action page: + with: + php-version: ${{ matrix.php }} + extensions: sockets + + - name: Check out code + uses: actions/checkout@v4 + + - name: Get Composer Cache Directory + id: composer-cache + run: | + cd tests/php_test_files + echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + + - name: Init Composer Cache # Docs: + uses: actions/cache@v4 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ matrix.php }}-${{ hashFiles('**/composer.json') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install Composer dependencies + run: cd tests/php_test_files && composer update --prefer-dist --no-progress --ansi + + - name: Init Go modules Cache # Docs: + uses: actions/cache@v4 + with: + path: ~/go/pkg/mod + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: ${{ runner.os }}-go- + + - name: Install Go dependencies + run: go mod download + + - name: Run tests with coverage + env: + RR_TEST_ENV: ${{ secrets.RR_TEST_ENV }} + RR_GOOGLE_PUB_SUB_TEST_ENDPOINT: ${{ secrets.RR_GOOGLE_PUB_SUB__TEST_ENDPOINT }} + RR_GOOGLE_PUB_SUB__TEST_REGION: ${{ secrets.RR_GOOGLE_PUB_SUB__TEST_REGION }} + RR_GOOGLE_PUB_SUB__TEST_KEY: ${{ secrets.RR_GOOGLE_PUB_SUB__TEST_KEY }} + RR_GOOGLE_PUB_SUB__TEST_SECRET: ${{ secrets.RR_GOOGLE_PUB_SUB__TEST_SECRET }} + run: | + cd tests + mkdir ./coverage-ci + docker compose -f env/docker-compose-emulator-local.yaml up -d + sleep 30 + go test -timeout 20m -v -race -cover -tags=debug -failfast -coverpkg=$(cat pkgs.txt) -coverprofile=./coverage-ci/sqs.out -covermode=atomic jobs_sqs_test.go jobs_sqs_fifo_test.go + + - name: Run unit tests with coverage + run: | + go test -timeout 20m -v -race -cover -tags=debug -failfast -coverpkg=$(cat ./tests/pkgs.txt) -coverprofile=./tests/coverage-ci/sqs_u.out -covermode=atomic ./... + + - name: Archive code coverage results + uses: actions/upload-artifact@v4 + with: + name: coverage + path: ./tests/coverage-ci/ + + codecov: + name: Upload codecov + runs-on: ubuntu-latest + needs: + - google_pub_sub_test + + timeout-minutes: 60 + steps: + - name: Download code coverage results + uses: actions/download-artifact@v4 + - run: | + cd coverage + echo 'mode: atomic' > summary.txt + tail -q -n +2 *.out >> summary.txt + sed -i '2,${/roadrunner/!d}' summary.txt + + - name: upload to codecov + uses: codecov/codecov-action@v4 # Docs: + with: + file: ./coverage/summary.txt + fail_ci_if_error: false From 81394dd587662231a82b1d057e8f98e45eaeec7e Mon Sep 17 00:00:00 2001 From: Kajetan Date: Fri, 9 Feb 2024 18:05:57 +0100 Subject: [PATCH 3/9] Add consumer --- pubsubjobs/config.go | 12 +- pubsubjobs/driver.go | 85 +++++++---- pubsubjobs/item.go | 146 ++++++++++++++++-- pubsubjobs/listener.go | 46 +++++- tests/configs/.rr-declare.yaml | 28 ++++ tests/configs/.rr-jobs-err.yaml | 30 ++++ tests/configs/.rr-pq.yaml | 55 +++++++ tests/helpers/helpers.go | 55 +++---- tests/jobs_test.go | 256 +++++++++++++++++++++++++++++++- tests/mock/logger.go | 64 ++++++++ tests/mock/observer.go | 199 +++++++++++++++++++++++++ 11 files changed, 891 insertions(+), 85 deletions(-) create mode 100644 tests/configs/.rr-declare.yaml create mode 100644 tests/configs/.rr-jobs-err.yaml create mode 100644 tests/configs/.rr-pq.yaml create mode 100644 tests/mock/logger.go create mode 100644 tests/mock/observer.go diff --git a/pubsubjobs/config.go b/pubsubjobs/config.go index 666fb4d..ffe0ff5 100644 --- a/pubsubjobs/config.go +++ b/pubsubjobs/config.go @@ -4,18 +4,20 @@ import "os" // pipeline rabbitmq info const ( - exchangeKey string = "exchange" + pref string = "prefetch" + skipTopicDeclaration string = "skip_topic_declaration" + topic string = "topic" ) // config is used to parse pipeline configuration type config struct { // global - ProjectID string `mapstructure:"project_id"` - Topic string `mapstructure:"topic"` - SkipTopicDeclaration bool `mapstructure:"skip_topic_declaration"` + ProjectID string `mapstructure:"project_id"` + Topic string `mapstructure:"topic"` + SkipTopicDeclaration bool `mapstructure:"skip_topic_declaration"` // local - Prefetch int `mapstructure:"prefetch"` + Prefetch int32 `mapstructure:"prefetch"` Priority int64 `mapstructure:"priority"` Host string `mapstructure:"host"` } diff --git a/pubsubjobs/driver.go b/pubsubjobs/driver.go index 8925478..0504540 100644 --- a/pubsubjobs/driver.go +++ b/pubsubjobs/driver.go @@ -2,12 +2,14 @@ package pubsubjobs import ( "context" + "strconv" "strings" "sync" "sync/atomic" "time" "cloud.google.com/go/pubsub" + "github.com/goccy/go-json" "github.com/roadrunner-server/api/v4/plugins/v3/jobs" "github.com/roadrunner-server/errors" jprop "go.opentelemetry.io/contrib/propagators/jaeger" @@ -36,14 +38,16 @@ type Driver struct { mu sync.Mutex cond sync.Cond - log *zap.Logger - pq jobs.Queue - pipeline atomic.Pointer[jobs.Pipeline] - tracer *sdktrace.TracerProvider - prop propagation.TextMapPropagator - consumeAll bool - skipDeclare bool - topic string + log *zap.Logger + pq jobs.Queue + pipeline atomic.Pointer[jobs.Pipeline] + tracer *sdktrace.TracerProvider + prop propagation.TextMapPropagator + consumeAll bool + skipDeclare bool + topic string + msgInFlight *int64 + msgInFlightLimit *int32 // if user invoke several resume operations listeners uint32 @@ -93,14 +97,16 @@ func FromConfig(tracer *sdktrace.TracerProvider, configKey string, pipe jobs.Pip // PARSE CONFIGURATION END ------- jb := &Driver{ - tracer: tracer, - prop: prop, - log: log, - skipDeclare: conf.SkipTopicDeclaration, - topic: conf.Topic, - pq: pq, - pauseCh: make(chan struct{}, 1), - cond: sync.Cond{L: &sync.Mutex{}}, + tracer: tracer, + prop: prop, + log: log, + skipDeclare: conf.SkipTopicDeclaration, + topic: conf.Topic, + pq: pq, + pauseCh: make(chan struct{}, 1), + cond: sync.Cond{L: &sync.Mutex{}}, + msgInFlightLimit: ptr(conf.Prefetch), + msgInFlight: ptr(int64(0)), } ctx := context.Background() @@ -145,14 +151,22 @@ func FromPipeline(tracer *sdktrace.TracerProvider, pipe jobs.Pipeline, log *zap. // PARSE CONFIGURATION ------- jb := &Driver{ - prop: prop, - tracer: tracer, - log: log, - pq: pq, - pauseCh: make(chan struct{}, 1), - skipDeclare: conf.SkipTopicDeclaration, - topic: conf.Topic, - cond: sync.Cond{L: &sync.Mutex{}}, + prop: prop, + tracer: tracer, + log: log, + pq: pq, + pauseCh: make(chan struct{}, 1), + skipDeclare: pipe.Bool(skipTopicDeclaration, false), + topic: pipe.String(topic, "default"), + cond: sync.Cond{L: &sync.Mutex{}}, + msgInFlightLimit: ptr(int32(pipe.Int(pref, 10))), + msgInFlight: ptr(int64(0)), + } + + ctx := context.Background() + jb.client, err = pubsub.NewClient(ctx, conf.ProjectID) + if err != nil { + return nil, err } err = jb.manageTopic(context.Background()) @@ -174,7 +188,24 @@ func (d *Driver) Push(ctx context.Context, jb jobs.Message) error { ctx, span := trace.SpanFromContext(ctx).TracerProvider().Tracer(tracerName).Start(ctx, "google_pub_sub_push") defer span.End() - result := d.client.Topic(d.topic).Publish(ctx, &pubsub.Message{Data: jb.Payload()}) + job := fromJob(jb) + + data, err := json.Marshal(job.headers) + if err != nil { + return err + } + + result := d.client.Topic(d.topic).Publish(ctx, &pubsub.Message{ + Data: jb.Payload(), + Attributes: map[string]string{ + jobs.RRID: job.Ident, + jobs.RRJob: job.Job, + jobs.RRDelay: strconv.Itoa(int(job.Options.Delay)), + jobs.RRHeaders: string(data), + jobs.RRPriority: strconv.Itoa(int(job.Options.Priority)), + jobs.RRAutoAck: btos(job.Options.AutoAck), + }, + }) id, err := result.Get(ctx) if err != nil { return err @@ -325,3 +356,7 @@ func (d *Driver) manageTopic(ctx context.Context) error { return nil } + +func ptr[T any](val T) *T { + return &val +} diff --git a/pubsubjobs/item.go b/pubsubjobs/item.go index 10db8c9..88065e8 100644 --- a/pubsubjobs/item.go +++ b/pubsubjobs/item.go @@ -1,18 +1,18 @@ package pubsubjobs import ( + "strconv" + "sync" + "sync/atomic" "time" "unsafe" + "cloud.google.com/go/pubsub" "github.com/goccy/go-json" + "go.uber.org/zap" - "github.com/roadrunner-server/api/v4/plugins/v1/jobs" -) - -var _ jobs.Acknowledger = (*Item)(nil) - -const ( - auto string = "deduced_by_rr" + "github.com/roadrunner-server/api/v4/plugins/v3/jobs" + "github.com/roadrunner-server/errors" ) type Item struct { @@ -23,7 +23,7 @@ type Item struct { // Payload is string data (usually JSON) passed to Job broker. Payload string `json:"payload"` // Headers with key-values pairs - Headers map[string][]string `json:"headers"` + headers map[string][]string `json:"headers"` // Options contains set of PipelineOptions specific to job execution. Can be empty. Options *Options `json:"options,omitempty"` } @@ -41,6 +41,11 @@ type Options struct { AutoAck bool `json:"auto_ack"` // AMQP Queue Queue string `json:"queue,omitempty"` + // Private ================ + cond *sync.Cond + message *pubsub.Message + msgInFlight *int64 + stopped *uint64 } // DelayDuration returns delay duration in a form of time.Duration. @@ -56,13 +61,17 @@ func (i *Item) Priority() int64 { return i.Options.Priority } +func (i *Item) GroupID() string { + return i.Options.Pipeline +} + // Body packs job payload into binary payload. func (i *Item) Body() []byte { return strToBytes(i.Payload) } -func (i *Item) Metadata() map[string][]string { - return i.Headers +func (i *Item) Headers() map[string][]string { + return i.headers } // Context packs job context (job, id) into binary payload. @@ -80,7 +89,7 @@ func (i *Item) Context() ([]byte, error) { ID: i.Ident, Job: i.Job, Driver: pluginName, - Headers: i.Headers, + Headers: i.headers, Queue: i.Options.Queue, Pipeline: i.Options.Pipeline, }, @@ -94,10 +103,37 @@ func (i *Item) Context() ([]byte, error) { } func (i *Item) Ack() error { + if atomic.LoadUint64(i.Options.stopped) == 1 { + return errors.Str("failed to acknowledge the JOB, the pipeline is probably stopped") + } + defer func() { + i.Options.cond.Signal() + atomic.AddInt64(i.Options.msgInFlight, ^int64(0)) + }() + // just return in case of auto-ack + if i.Options.AutoAck { + return nil + } + + i.Options.message.Ack() return nil } func (i *Item) Nack() error { + if atomic.LoadUint64(i.Options.stopped) == 1 { + return errors.Str("failed to acknowledge the JOB, the pipeline is probably stopped") + } + defer func() { + i.Options.cond.Signal() + atomic.AddInt64(i.Options.msgInFlight, ^int64(0)) + }() + // message already deleted + if i.Options.AutoAck { + return nil + } + + i.Options.message.Nack() + return nil } @@ -110,21 +146,20 @@ func (i *Item) Respond(_ []byte, _ string) error { return nil } -func fromJob(job jobs.Job) *Item { +func fromJob(job jobs.Message) *Item { return &Item{ Job: job.Name(), Ident: job.ID(), - Payload: job.Payload(), - Headers: job.Headers(), + Payload: string(job.Payload()), + headers: job.Headers(), Options: &Options{ Priority: job.Priority(), - Pipeline: job.Pipeline(), + Pipeline: job.GroupID(), Delay: job.Delay(), AutoAck: job.AutoAck(), }, } } - func bytesToStr(data []byte) string { if len(data) == 0 { return "" @@ -140,3 +175,82 @@ func strToBytes(data string) []byte { return unsafe.Slice(unsafe.StringData(data), len(data)) } + +func (c *Driver) unpack(message *pubsub.Message) *Item { + attributes := message.Attributes + + var rrid string + if val, ok := attributes[jobs.RRID]; ok { + rrid = val + } + + var rrj string + if val, ok := attributes[jobs.RRJob]; ok { + rrj = val + } + + h := make(map[string][]string) + if val, ok := attributes[jobs.RRHeaders]; ok { + err := json.Unmarshal([]byte(val), &h) + if err != nil { + c.log.Debug("failed to unpack the headers, not a JSON", zap.Error(err)) + } + } + + var autoAck bool + if val, ok := attributes[jobs.RRAutoAck]; ok { + autoAck = stob(val) + } + + var dl int + var err error + if val, ok := attributes[jobs.RRDelay]; ok { + dl, err = strconv.Atoi(val) + if err != nil { + c.log.Debug("failed to unpack the delay, not a number", zap.Error(err)) + } + } + + var priority int + if val, ok := attributes[jobs.RRPriority]; ok { + priority, err = strconv.Atoi(val) + if err != nil { + priority = int((*c.pipeline.Load()).Priority()) + c.log.Debug("failed to unpack the priority; inheriting the pipeline's default priority", zap.Error(err)) + } + } + + return &Item{ + Job: rrj, + Ident: rrid, + Payload: string(message.Data), + headers: h, + Options: &Options{ + AutoAck: autoAck, + Delay: int64(dl), + Priority: int64(priority), + Pipeline: (*c.pipeline.Load()).Name(), + // private + message: message, + msgInFlight: c.msgInFlight, + cond: &c.cond, + stopped: &c.stopped, + }, + } +} + +func btos(b bool) string { + if b { + return "true" + } + + return "false" +} + +func stob(s string) bool { + if s != "" { + return s == "true" + } + + return false +} diff --git a/pubsubjobs/listener.go b/pubsubjobs/listener.go index 40a8572..590ed0b 100644 --- a/pubsubjobs/listener.go +++ b/pubsubjobs/listener.go @@ -1,6 +1,13 @@ package pubsubjobs -import "context" +import ( + "context" + "sync/atomic" + + "cloud.google.com/go/pubsub" + "go.opentelemetry.io/otel/propagation" + "go.uber.org/zap" +) func (d *Driver) listen(ctx context.Context) { go func() { @@ -10,6 +17,43 @@ func (d *Driver) listen(ctx context.Context) { d.log.Debug("listener was stopped") return default: + s, err := d.client.Topic(d.topic).Subscriptions(ctx).Next() + if err != nil { + d.log.Error("subscription iteration", zap.Error(err)) + continue + } + + s.Receive(context.Background(), func(ctx context.Context, message *pubsub.Message) { + d.cond.L.Lock() + // lock when we hit the limit + for atomic.LoadInt64(d.msgInFlight) >= int64(atomic.LoadInt32(d.msgInFlightLimit)) { + d.log.Debug("prefetch limit was reached, waiting for the jobs to be processed", zap.Int64("current", atomic.LoadInt64(d.msgInFlight)), zap.Int32("limit", atomic.LoadInt32(d.msgInFlightLimit))) + d.cond.Wait() + } + + d.log.Debug("receive message", zap.Stringp("ID", &message.ID)) + item := d.unpack(message) + + ctxspan, span := d.tracer.Tracer(tracerName).Start(d.prop.Extract(context.Background(), propagation.HeaderCarrier(item.headers)), "google_pub_sub_listener") + if item.Options.AutoAck { + item.Ack() + d.log.Debug("auto ack is turned on, message acknowledged") + span.End() + } + + if item.headers == nil { + item.headers = make(map[string][]string, 2) + } + + d.prop.Inject(ctxspan, propagation.HeaderCarrier(item.headers)) + + d.pq.Insert(item) + // increase the current number of messages + atomic.AddInt64(d.msgInFlight, 1) + d.log.Debug("message pushed to the priority queue", zap.Int64("current", atomic.LoadInt64(d.msgInFlight)), zap.Int32("limit", atomic.LoadInt32(d.msgInFlightLimit))) + d.cond.L.Unlock() + span.End() + }) } } }() diff --git a/tests/configs/.rr-declare.yaml b/tests/configs/.rr-declare.yaml new file mode 100644 index 0000000..0663e4c --- /dev/null +++ b/tests/configs/.rr-declare.yaml @@ -0,0 +1,28 @@ +version: '3' + +rpc: + listen: tcp://127.0.0.1:6001 + +server: + command: "php php_test_files/jobs/jobs_ok.php" + relay: "pipes" + relay_timeout: "20s" + +google-pub-sub: + project_id: test + topic: rrTopic + skip_topic_declaration: false + host: 127.0.0.1:8085 + +logs: + level: debug + encoding: console + mode: development + +jobs: + num_pollers: 1 + pipeline_size: 100000 + pool: + num_workers: 10 + allocate_timeout: 60s + destroy_timeout: 60s \ No newline at end of file diff --git a/tests/configs/.rr-jobs-err.yaml b/tests/configs/.rr-jobs-err.yaml new file mode 100644 index 0000000..4eb37e1 --- /dev/null +++ b/tests/configs/.rr-jobs-err.yaml @@ -0,0 +1,30 @@ +version: '3' + +rpc: + listen: tcp://127.0.0.1:6001 + +server: + command: "php php_test_files/jobs/jobs_err.php" + relay: "pipes" + relay_timeout: "20s" + +google-pub-sub: + project_id: test + topic: rrTopic + skip_topic_declaration: false + host: 127.0.0.1:8085 + +logs: + level: debug + encoding: console + mode: development + +jobs: + num_pollers: 10 + timeout: 60 + pipeline_size: 100000 + pool: + num_workers: 10 + max_jobs: 0 + allocate_timeout: 60s + destroy_timeout: 60s \ No newline at end of file diff --git a/tests/configs/.rr-pq.yaml b/tests/configs/.rr-pq.yaml new file mode 100644 index 0000000..4318edb --- /dev/null +++ b/tests/configs/.rr-pq.yaml @@ -0,0 +1,55 @@ +version: '3' + +rpc: + listen: tcp://127.0.0.1:6601 + +server: + command: "php php_test_files/jobs/jobs_ok_pq.php" + relay: "pipes" + +google-pub-sub: + project_id: test + topic: rrTopic + skip_topic_declaration: false + host: 127.0.0.1:8085 + +logs: + level: debug + encoding: console + mode: development + +jobs: + num_pollers: 2 + pipeline_size: 100000 + pool: + num_workers: 2 + allocate_timeout: 60s + destroy_timeout: 60s + + pipelines: + test-1-pq: + driver: google-pub-sub + config: + prefetch: 1000 + visibility_timeout: 0 + wait_time_seconds: 0 + queue: default-1-pq + attributes: + DelaySeconds: 0 + MaximumMessageSize: 262144 + MessageRetentionPeriod: 345600 + ReceiveMessageWaitTimeSeconds: 0 + VisibilityTimeout: 30 + tags: + test: "tag-pq" + + test-2-pq: + driver: google-pub-sub + config: + prefetch: 1000 + queue: default-2-pq + attributes: + MessageRetentionPeriod: 86400 + tags: + test: "tag" + consume: [ "test-1-pq", "test-2-pq" ] diff --git a/tests/helpers/helpers.go b/tests/helpers/helpers.go index a0e3804..0b93b27 100644 --- a/tests/helpers/helpers.go +++ b/tests/helpers/helpers.go @@ -1,9 +1,7 @@ package helpers import ( - "bytes" "net" - "net/http" "net/rpc" "testing" "time" @@ -162,42 +160,25 @@ func Stats(address string, state *jobState.State) func(t *testing.T) { } } -func EnableProxy(name string, t *testing.T) { - buf := new(bytes.Buffer) - buf.WriteString(`{"enabled":true}`) - - resp, err := http.Post("http://127.0.0.1:8474/proxies/"+name, "application/json", buf) //nolint:noctx - require.NoError(t, err) - require.Equal(t, 200, resp.StatusCode) - if resp.Body != nil { - _ = resp.Body.Close() - } -} - -func DisableProxy(name string, t *testing.T) { - buf := new(bytes.Buffer) - buf.WriteString(`{"enabled":false}`) - - resp, err := http.Post("http://127.0.0.1:8474/proxies/"+name, "application/json", buf) //nolint:noctx - require.NoError(t, err) - require.Equal(t, 200, resp.StatusCode) - if resp.Body != nil { - _ = resp.Body.Close() - } -} - -func DeleteProxy(name string, t *testing.T) { - client := &http.Client{} - - req, err := http.NewRequest(http.MethodDelete, "http://127.0.0.1:8474/proxies/"+name, nil) //nolint:noctx - require.NoError(t, err) +func DeclarePipe(queue string, address string, pipeline string) func(t *testing.T) { + return func(t *testing.T) { + conn, err := net.Dial("tcp", address) + assert.NoError(t, err) + client := rpc.NewClientWithCodec(goridgeRpc.NewClientCodec(conn)) - resp, err := client.Do(req) - require.NoError(t, err) + pipe := &jobsProto.DeclareRequest{Pipeline: map[string]string{ + "driver": "google-pub-sub", + "name": pipeline, + "queue": queue, + "prefetch": "10", + "priority": "3", + "visibility_timeout": "0", + "wait_time_seconds": "3", + "tags": `{"key":"value"}`, + }} - require.NoError(t, err) - require.Equal(t, 204, resp.StatusCode) - if resp.Body != nil { - _ = resp.Body.Close() + er := &jobsProto.Empty{} + err = client.Call("jobs.Declare", pipe, er) + assert.NoError(t, err) } } diff --git a/tests/jobs_test.go b/tests/jobs_test.go index d2c95fa..66e8e13 100644 --- a/tests/jobs_test.go +++ b/tests/jobs_test.go @@ -10,17 +10,19 @@ import ( "time" "tests/helpers" + mocklogger "tests/mock" "github.com/roadrunner-server/config/v4" "github.com/roadrunner-server/endure/v2" + googlePubSub "github.com/roadrunner-server/google-pub-sub/v4" "github.com/roadrunner-server/informer/v4" "github.com/roadrunner-server/jobs/v4" "github.com/roadrunner-server/logger/v4" "github.com/roadrunner-server/resetter/v4" rpcPlugin "github.com/roadrunner-server/rpc/v4" "github.com/roadrunner-server/server/v4" - googlePubSub "github.com/roadrunner-server/google-pub-sub/v4" "github.com/stretchr/testify/assert" + "go.uber.org/zap" ) func TestInit(t *testing.T) { @@ -99,3 +101,255 @@ func TestInit(t *testing.T) { stopCh <- struct{}{} wg.Wait() } + +func TestDeclare(t *testing.T) { + cont := endure.New(slog.LevelDebug) + + cfg := &config.Plugin{ + Version: "2023.3.0", + Path: "configs/.rr-declare.yaml", + Prefix: "rr", + } + + err := cont.RegisterAll( + cfg, + &server.Plugin{}, + &rpcPlugin.Plugin{}, + &logger.Plugin{}, + &jobs.Plugin{}, + &resetter.Plugin{}, + &informer.Plugin{}, + &googlePubSub.Plugin{}, + ) + assert.NoError(t, err) + + err = cont.Init() + if err != nil { + t.Fatal(err) + } + + ch, err := cont.Serve() + if err != nil { + t.Fatal(err) + } + + sig := make(chan os.Signal, 1) + signal.Notify(sig, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) + + wg := &sync.WaitGroup{} + wg.Add(1) + + stopCh := make(chan struct{}, 1) + + go func() { + defer wg.Done() + for { + select { + case e := <-ch: + assert.Fail(t, "error", e.Error.Error()) + err = cont.Stop() + if err != nil { + assert.FailNow(t, "error", err.Error()) + } + case <-sig: + err = cont.Stop() + if err != nil { + assert.FailNow(t, "error", err.Error()) + } + return + case <-stopCh: + // timeout + err = cont.Stop() + if err != nil { + assert.FailNow(t, "error", err.Error()) + } + return + } + } + }() + + time.Sleep(time.Second * 3) + + t.Run("DeclarePipeline", helpers.DeclarePipe("default", "127.0.0.1:6001", "test-3")) + t.Run("ConsumePipeline", helpers.ResumePipes("127.0.0.1:6001", "test-3")) + t.Run("PushPipeline", helpers.PushToPipe("test-3", false, "127.0.0.1:6001")) + time.Sleep(time.Second) + t.Run("PausePipeline", helpers.PausePipelines("127.0.0.1:6001", "test-3")) + time.Sleep(time.Second) + t.Run("DestroyPipeline", helpers.DestroyPipelines("127.0.0.1:6001", "test-3")) + + time.Sleep(time.Second * 5) + stopCh <- struct{}{} + wg.Wait() +} + +func TestJobsError(t *testing.T) { + cont := endure.New(slog.LevelDebug) + + cfg := &config.Plugin{ + Version: "2023.3.0", + Path: "configs/.rr-jobs-err.yaml", + Prefix: "rr", + } + + err := cont.RegisterAll( + cfg, + &server.Plugin{}, + &rpcPlugin.Plugin{}, + &logger.Plugin{}, + &jobs.Plugin{}, + &resetter.Plugin{}, + &informer.Plugin{}, + &googlePubSub.Plugin{}, + ) + assert.NoError(t, err) + + err = cont.Init() + if err != nil { + t.Fatal(err) + } + + ch, err := cont.Serve() + if err != nil { + t.Fatal(err) + } + + sig := make(chan os.Signal, 1) + signal.Notify(sig, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) + + wg := &sync.WaitGroup{} + wg.Add(1) + + stopCh := make(chan struct{}, 1) + + go func() { + defer wg.Done() + for { + select { + case e := <-ch: + assert.Fail(t, "error", e.Error.Error()) + err = cont.Stop() + if err != nil { + assert.FailNow(t, "error", err.Error()) + } + case <-sig: + err = cont.Stop() + if err != nil { + assert.FailNow(t, "error", err.Error()) + } + return + case <-stopCh: + // timeout + err = cont.Stop() + if err != nil { + assert.FailNow(t, "error", err.Error()) + } + return + } + } + }() + + time.Sleep(time.Second * 3) + + t.Run("DeclarePipeline", helpers.DeclarePipe("default", "127.0.0.1:6001", "test-3")) + t.Run("ConsumePipeline", helpers.ResumePipes("127.0.0.1:6001", "test-3")) + t.Run("PushPipeline", helpers.PushToPipe("test-3", false, "127.0.0.1:6001")) + time.Sleep(time.Second * 25) + t.Run("PausePipeline", helpers.PausePipelines("127.0.0.1:6001", "test-3")) + time.Sleep(time.Second) + t.Run("DestroyPipeline", helpers.DestroyPipelines("127.0.0.1:6001", "test-3")) + + time.Sleep(time.Second * 5) + stopCh <- struct{}{} + wg.Wait() + + time.Sleep(time.Second * 5) +} + +func TestRemovePQ(t *testing.T) { + cont := endure.New(slog.LevelDebug) + + cfg := &config.Plugin{ + Version: "2023.2.0", + Path: "configs/.rr-pq.yaml", + Prefix: "rr", + } + + l, oLogger := mocklogger.ZapTestLogger(zap.DebugLevel) + err := cont.RegisterAll( + cfg, + &server.Plugin{}, + &rpcPlugin.Plugin{}, + l, + &jobs.Plugin{}, + &resetter.Plugin{}, + &informer.Plugin{}, + &googlePubSub.Plugin{}, + ) + assert.NoError(t, err) + + err = cont.Init() + if err != nil { + t.Fatal(err) + } + + ch, err := cont.Serve() + if err != nil { + t.Fatal(err) + } + + sig := make(chan os.Signal, 1) + signal.Notify(sig, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) + + wg := &sync.WaitGroup{} + wg.Add(1) + + stopCh := make(chan struct{}, 1) + + go func() { + defer wg.Done() + for { + select { + case e := <-ch: + assert.Fail(t, "error", e.Error.Error()) + err = cont.Stop() + if err != nil { + assert.FailNow(t, "error", err.Error()) + } + case <-sig: + err = cont.Stop() + if err != nil { + assert.FailNow(t, "error", err.Error()) + } + return + case <-stopCh: + // timeout + err = cont.Stop() + if err != nil { + assert.FailNow(t, "error", err.Error()) + } + return + } + } + }() + + time.Sleep(time.Second * 3) + + for i := 0; i < 10; i++ { + t.Run("PushPipeline", helpers.PushToPipe("test-1-pq", false, "127.0.0.1:6601")) + t.Run("PushPipeline", helpers.PushToPipe("test-2-pq", false, "127.0.0.1:6601")) + } + time.Sleep(time.Second * 3) + + t.Run("DestroyPipeline", helpers.DestroyPipelines("127.0.0.1:6601", "test-1-pq", "test-2-pq")) + + stopCh <- struct{}{} + wg.Wait() + + assert.Equal(t, 0, oLogger.FilterMessageSnippet("job was processed successfully").Len()) + assert.Equal(t, 2, oLogger.FilterMessageSnippet("pipeline was started").Len()) + assert.Equal(t, 2, oLogger.FilterMessageSnippet("pipeline was stopped").Len()) + assert.Equal(t, 20, oLogger.FilterMessageSnippet("job was pushed successfully").Len()) + assert.Equal(t, 2, oLogger.FilterMessageSnippet("job processing was started").Len()) + assert.Equal(t, 2, oLogger.FilterMessageSnippet("listener was stopped").Len()) +} diff --git a/tests/mock/logger.go b/tests/mock/logger.go new file mode 100644 index 0000000..9bc251f --- /dev/null +++ b/tests/mock/logger.go @@ -0,0 +1,64 @@ +package mocklogger + +import ( + "github.com/roadrunner-server/endure/v2/dep" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +type ZapLoggerMock struct { + l *zap.Logger +} + +type Logger interface { + NamedLogger(string) *zap.Logger +} + +func ZapTestLogger(enab zapcore.LevelEnabler) (*ZapLoggerMock, *ObservedLogs) { + core, logs := New(enab) + obsLog := zap.New(core, zap.Development()) + + return &ZapLoggerMock{ + l: obsLog, + }, logs +} + +func (z *ZapLoggerMock) Init() error { + return nil +} + +func (z *ZapLoggerMock) Serve() chan error { + return make(chan error, 1) +} + +func (z *ZapLoggerMock) Stop() error { + return z.l.Sync() +} + +func (z *ZapLoggerMock) Provides() []*dep.Out { + return []*dep.Out{ + dep.Bind((*Logger)(nil), z.ProvideLogger), + } +} + +func (z *ZapLoggerMock) Weight() uint { + return 100 +} + +func (z *ZapLoggerMock) ProvideLogger() *Log { + return NewLogger(z.l) +} + +type Log struct { + base *zap.Logger +} + +func NewLogger(log *zap.Logger) *Log { + return &Log{ + base: log, + } +} + +func (l *Log) NamedLogger(string) *zap.Logger { + return l.base +} diff --git a/tests/mock/observer.go b/tests/mock/observer.go new file mode 100644 index 0000000..061cec7 --- /dev/null +++ b/tests/mock/observer.go @@ -0,0 +1,199 @@ +package mocklogger + +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import ( + "strings" + "sync" + "time" + + "go.uber.org/zap/zapcore" +) + +// An LoggedEntry is an encoding-agnostic representation of a log message. +// Field availability is context dependant. +type LoggedEntry struct { + zapcore.Entry + Context []zapcore.Field +} + +// ContextMap returns a map for all fields in Context. +func (e LoggedEntry) ContextMap() map[string]any { + encoder := zapcore.NewMapObjectEncoder() + for _, f := range e.Context { + f.AddTo(encoder) + } + return encoder.Fields +} + +// ObservedLogs is a concurrency-safe, ordered collection of observed logs. +type ObservedLogs struct { + mu sync.RWMutex + logs []LoggedEntry +} + +// Len returns the number of items in the collection. +func (o *ObservedLogs) Len() int { + o.mu.RLock() + n := len(o.logs) + o.mu.RUnlock() + return n +} + +// All returns a copy of all the observed logs. +func (o *ObservedLogs) All() []LoggedEntry { + o.mu.RLock() + ret := make([]LoggedEntry, len(o.logs)) + copy(ret, o.logs) + o.mu.RUnlock() + return ret +} + +// TakeAll returns a copy of all the observed logs, and truncates the observed +// slice. +func (o *ObservedLogs) TakeAll() []LoggedEntry { + o.mu.Lock() + ret := o.logs + o.logs = nil + o.mu.Unlock() + return ret +} + +// AllUntimed returns a copy of all the observed logs, but overwrites the +// observed timestamps with time.Time's zero value. This is useful when making +// assertions in tests. +func (o *ObservedLogs) AllUntimed() []LoggedEntry { + ret := o.All() + for i := range ret { + ret[i].Time = time.Time{} + } + return ret +} + +// FilterLevelExact filters entries to those logged at exactly the given level. +func (o *ObservedLogs) FilterLevelExact(level zapcore.Level) *ObservedLogs { + return o.Filter(func(e LoggedEntry) bool { + return e.Level == level + }) +} + +// FilterMessage filters entries to those that have the specified message. +func (o *ObservedLogs) FilterMessage(msg string) *ObservedLogs { + return o.Filter(func(e LoggedEntry) bool { + return e.Message == msg + }) +} + +// FilterMessageSnippet filters entries to those that have a message containing the specified snippet. +func (o *ObservedLogs) FilterMessageSnippet(snippet string) *ObservedLogs { + return o.Filter(func(e LoggedEntry) bool { + return strings.Contains(e.Message, snippet) + }) +} + +// FilterField filters entries to those that have the specified field. +func (o *ObservedLogs) FilterField(field zapcore.Field) *ObservedLogs { + return o.Filter(func(e LoggedEntry) bool { + for _, ctxField := range e.Context { + if ctxField.Equals(field) { + return true + } + } + return false + }) +} + +// FilterFieldKey filters entries to those that have the specified key. +func (o *ObservedLogs) FilterFieldKey(key string) *ObservedLogs { + return o.Filter(func(e LoggedEntry) bool { + for _, ctxField := range e.Context { + if ctxField.Key == key { + return true + } + } + return false + }) +} + +// Filter returns a copy of this ObservedLogs containing only those entries +// for which the provided function returns true. +func (o *ObservedLogs) Filter(keep func(LoggedEntry) bool) *ObservedLogs { + o.mu.RLock() + defer o.mu.RUnlock() + + var filtered []LoggedEntry + for _, entry := range o.logs { + if keep(entry) { + filtered = append(filtered, entry) + } + } + return &ObservedLogs{logs: filtered} +} + +func (o *ObservedLogs) add(log LoggedEntry) { + o.mu.Lock() + o.logs = append(o.logs, log) + o.mu.Unlock() +} + +// New creates a new Core that buffers logs in memory (without any encoding). +// It's particularly useful in tests. +func New(enab zapcore.LevelEnabler) (zapcore.Core, *ObservedLogs) { + ol := &ObservedLogs{} + return &contextObserver{ + LevelEnabler: enab, + logs: ol, + }, ol +} + +type contextObserver struct { + zapcore.LevelEnabler + logs *ObservedLogs + context []zapcore.Field +} + +func (co *contextObserver) Check(ent zapcore.Entry, ce *zapcore.CheckedEntry) *zapcore.CheckedEntry { + if co.Enabled(ent.Level) { + return ce.AddCore(ent, co) + } + return ce +} + +func (co *contextObserver) With(fields []zapcore.Field) zapcore.Core { + return &contextObserver{ + LevelEnabler: co.LevelEnabler, + logs: co.logs, + context: append(co.context[:len(co.context):len(co.context)], fields...), + } +} + +func (co *contextObserver) Write(ent zapcore.Entry, fields []zapcore.Field) error { + all := make([]zapcore.Field, 0, len(fields)+len(co.context)) + all = append(all, co.context...) + all = append(all, fields...) + co.logs.add(LoggedEntry{ent, all}) + + return nil +} + +func (co *contextObserver) Sync() error { + return nil +} From 129ea3437302ea1d0e9b61e3bc2cb8b07a6f93f1 Mon Sep 17 00:00:00 2001 From: Kajetan Date: Fri, 9 Feb 2024 19:48:04 +0100 Subject: [PATCH 4/9] Add consumer --- pubsubjobs/driver.go | 28 ++++++++-- pubsubjobs/item.go | 1 + pubsubjobs/listener.go | 17 +++--- tests/helpers/helpers.go | 45 ++++++++++++++++ tests/jobs_test.go | 113 +++++++++++++++++++++++++++++++++++++-- 5 files changed, 187 insertions(+), 17 deletions(-) diff --git a/pubsubjobs/driver.go b/pubsubjobs/driver.go index 0504540..2f1f7ac 100644 --- a/pubsubjobs/driver.go +++ b/pubsubjobs/driver.go @@ -48,6 +48,7 @@ type Driver struct { topic string msgInFlight *int64 msgInFlightLimit *int32 + sub string // if user invoke several resume operations listeners uint32 @@ -107,6 +108,7 @@ func FromConfig(tracer *sdktrace.TracerProvider, configKey string, pipe jobs.Pip cond: sync.Cond{L: &sync.Mutex{}}, msgInFlightLimit: ptr(conf.Prefetch), msgInFlight: ptr(int64(0)), + sub: pipe.Name(), } ctx := context.Background() @@ -161,6 +163,7 @@ func FromPipeline(tracer *sdktrace.TracerProvider, pipe jobs.Pipeline, log *zap. cond: sync.Cond{L: &sync.Mutex{}}, msgInFlightLimit: ptr(int32(pipe.Int(pref, 10))), msgInFlight: ptr(int64(0)), + sub: pipe.Name(), } ctx := context.Background() @@ -346,13 +349,30 @@ func (d *Driver) manageTopic(ctx context.Context) error { return nil } - _, err := d.client.CreateTopic(ctx, d.topic) + topic, err := d.client.CreateTopic(ctx, d.topic) if err != nil { - if strings.Contains(err.Error(), "Topic already exists") { - return nil + if !strings.Contains(err.Error(), "Topic already exists") { + return err + } + + topic = d.client.Topic(d.topic) + } else { + d.log.Debug("created topic", zap.String("topic", d.topic)) + } + + _, err = d.client.CreateSubscription(ctx, d.sub, pubsub.SubscriptionConfig{ + Topic: topic, + AckDeadline: 10 * time.Second, + ExpirationPolicy: time.Duration(0), + }) + if err != nil { + if !strings.Contains(err.Error(), "Subscription already exists") { + return err } - return err } + d.log.Debug("created subscription", zap.String("topic", d.topic), zap.String("subscription", d.sub)) + + topic.Stop() return nil } diff --git a/pubsubjobs/item.go b/pubsubjobs/item.go index 88065e8..82b9c83 100644 --- a/pubsubjobs/item.go +++ b/pubsubjobs/item.go @@ -127,6 +127,7 @@ func (i *Item) Nack() error { i.Options.cond.Signal() atomic.AddInt64(i.Options.msgInFlight, ^int64(0)) }() + // message already deleted if i.Options.AutoAck { return nil diff --git a/pubsubjobs/listener.go b/pubsubjobs/listener.go index 590ed0b..2d66ac2 100644 --- a/pubsubjobs/listener.go +++ b/pubsubjobs/listener.go @@ -17,13 +17,7 @@ func (d *Driver) listen(ctx context.Context) { d.log.Debug("listener was stopped") return default: - s, err := d.client.Topic(d.topic).Subscriptions(ctx).Next() - if err != nil { - d.log.Error("subscription iteration", zap.Error(err)) - continue - } - - s.Receive(context.Background(), func(ctx context.Context, message *pubsub.Message) { + d.client.Subscription(d.sub).Receive(ctx, func(ctx context.Context, message *pubsub.Message) { d.cond.L.Lock() // lock when we hit the limit for atomic.LoadInt64(d.msgInFlight) >= int64(atomic.LoadInt32(d.msgInFlightLimit)) { @@ -36,9 +30,14 @@ func (d *Driver) listen(ctx context.Context) { ctxspan, span := d.tracer.Tracer(tracerName).Start(d.prop.Extract(context.Background(), propagation.HeaderCarrier(item.headers)), "google_pub_sub_listener") if item.Options.AutoAck { - item.Ack() + _, err := message.AckWithResult().Get(ctx) + if err != nil { + d.log.Error("message acknowledge", zap.Error(err)) + span.RecordError(err) + span.End() + return + } d.log.Debug("auto ack is turned on, message acknowledged") - span.End() } if item.headers == nil { diff --git a/tests/helpers/helpers.go b/tests/helpers/helpers.go index 0b93b27..4fb6da2 100644 --- a/tests/helpers/helpers.go +++ b/tests/helpers/helpers.go @@ -1,11 +1,15 @@ package helpers import ( + "context" "net" "net/rpc" + "os" + "strings" "testing" "time" + "cloud.google.com/go/pubsub" "github.com/google/uuid" jobsProto "github.com/roadrunner-server/api/v4/build/jobs/v1" jobState "github.com/roadrunner-server/api/v4/plugins/v1/jobs" @@ -182,3 +186,44 @@ func DeclarePipe(queue string, address string, pipeline string) func(t *testing. assert.NoError(t, err) } } + +func CleanEmulator() error { + os.Setenv("PUBSUB_EMULATOR_HOST", "127.0.0.1:8085") + ctx := context.Background() + client, err := pubsub.NewClient(ctx, "test") + if err != nil { + return err + } + + for { + sub, err := client.Subscriptions(ctx).Next() + if err != nil { + if strings.Contains(err.Error(), "no more items in iterator") { + break; + } + + return err + } + + err = sub.Delete(ctx) + if err != nil { + return err + } + } + + for { + topic, err := client.Topics(ctx).Next() + if err != nil { + if strings.Contains(err.Error(), "no more items in iterator") { + return nil + } + + return err + } + + err = topic.Delete(ctx) + if err != nil { + return err + } + } +} diff --git a/tests/jobs_test.go b/tests/jobs_test.go index 66e8e13..1f9213c 100644 --- a/tests/jobs_test.go +++ b/tests/jobs_test.go @@ -22,10 +22,16 @@ import ( rpcPlugin "github.com/roadrunner-server/rpc/v4" "github.com/roadrunner-server/server/v4" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "go.uber.org/zap" ) func TestInit(t *testing.T) { + err := helpers.CleanEmulator() + if err != nil { + assert.FailNow(t, "error", err.Error()) + } + cont := endure.New(slog.LevelDebug) cfg := &config.Plugin{ @@ -34,7 +40,7 @@ func TestInit(t *testing.T) { Prefix: "rr", } - err := cont.RegisterAll( + err = cont.RegisterAll( cfg, &server.Plugin{}, &rpcPlugin.Plugin{}, @@ -103,6 +109,11 @@ func TestInit(t *testing.T) { } func TestDeclare(t *testing.T) { + err := helpers.CleanEmulator() + if err != nil { + assert.FailNow(t, "error", err.Error()) + } + cont := endure.New(slog.LevelDebug) cfg := &config.Plugin{ @@ -111,7 +122,7 @@ func TestDeclare(t *testing.T) { Prefix: "rr", } - err := cont.RegisterAll( + err = cont.RegisterAll( cfg, &server.Plugin{}, &rpcPlugin.Plugin{}, @@ -184,6 +195,11 @@ func TestDeclare(t *testing.T) { } func TestJobsError(t *testing.T) { + err := helpers.CleanEmulator() + if err != nil { + assert.FailNow(t, "error", err.Error()) + } + cont := endure.New(slog.LevelDebug) cfg := &config.Plugin{ @@ -192,7 +208,7 @@ func TestJobsError(t *testing.T) { Prefix: "rr", } - err := cont.RegisterAll( + err = cont.RegisterAll( cfg, &server.Plugin{}, &rpcPlugin.Plugin{}, @@ -267,6 +283,11 @@ func TestJobsError(t *testing.T) { } func TestRemovePQ(t *testing.T) { + err := helpers.CleanEmulator() + if err != nil { + assert.FailNow(t, "error", err.Error()) + } + cont := endure.New(slog.LevelDebug) cfg := &config.Plugin{ @@ -276,7 +297,7 @@ func TestRemovePQ(t *testing.T) { } l, oLogger := mocklogger.ZapTestLogger(zap.DebugLevel) - err := cont.RegisterAll( + err = cont.RegisterAll( cfg, &server.Plugin{}, &rpcPlugin.Plugin{}, @@ -353,3 +374,87 @@ func TestRemovePQ(t *testing.T) { assert.Equal(t, 2, oLogger.FilterMessageSnippet("job processing was started").Len()) assert.Equal(t, 2, oLogger.FilterMessageSnippet("listener was stopped").Len()) } + +func TestAutoAck(t *testing.T) { + err := helpers.CleanEmulator() + if err != nil { + assert.FailNow(t, "error", err.Error()) + } + + cont := endure.New(slog.LevelDebug) + + cfg := &config.Plugin{ + Version: "2023.3.0", + Path: "configs/.rr-init.yaml", + Prefix: "rr", + } + + l, oLogger := mocklogger.ZapTestLogger(zap.DebugLevel) + err = cont.RegisterAll( + cfg, + &server.Plugin{}, + &rpcPlugin.Plugin{}, + l, + &jobs.Plugin{}, + &resetter.Plugin{}, + &informer.Plugin{}, + &googlePubSub.Plugin{}, + ) + assert.NoError(t, err) + + err = cont.Init() + if err != nil { + t.Fatal(err) + } + + ch, err := cont.Serve() + if err != nil { + t.Fatal(err) + } + + sig := make(chan os.Signal, 1) + signal.Notify(sig, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) + + wg := &sync.WaitGroup{} + wg.Add(1) + + stopCh := make(chan struct{}, 1) + + go func() { + defer wg.Done() + for { + select { + case e := <-ch: + assert.Fail(t, "error", e.Error.Error()) + err = cont.Stop() + if err != nil { + assert.FailNow(t, "error", err.Error()) + } + case <-sig: + err = cont.Stop() + if err != nil { + assert.FailNow(t, "error", err.Error()) + } + return + case <-stopCh: + // timeout + err = cont.Stop() + if err != nil { + assert.FailNow(t, "error", err.Error()) + } + return + } + } + }() + + time.Sleep(time.Second * 3) + t.Run("PushPipeline", helpers.PushToPipe("test-1", true, "127.0.0.1:6001")) + t.Run("PushPipeline", helpers.PushToPipe("test-2", true, "127.0.0.1:6001")) + time.Sleep(time.Second * 2) + t.Run("DestroyPipeline", helpers.DestroyPipelines("127.0.0.1:6001", "test-1", "test-2")) + + stopCh <- struct{}{} + wg.Wait() + + require.Equal(t, 2, oLogger.FilterMessageSnippet("auto ack is turned on, message acknowledged").Len()) +} From 148f9b0333a85c25f8efbfb041f00f026b71497c Mon Sep 17 00:00:00 2001 From: Kajetan Date: Fri, 9 Feb 2024 20:17:29 +0100 Subject: [PATCH 5/9] Add consumer --- pubsubjobs/driver.go | 11 +++-------- pubsubjobs/item.go | 7 ------- pubsubjobs/listener.go | 6 +++++- 3 files changed, 8 insertions(+), 16 deletions(-) diff --git a/pubsubjobs/driver.go b/pubsubjobs/driver.go index 2f1f7ac..18cae17 100644 --- a/pubsubjobs/driver.go +++ b/pubsubjobs/driver.go @@ -43,7 +43,6 @@ type Driver struct { pipeline atomic.Pointer[jobs.Pipeline] tracer *sdktrace.TracerProvider prop propagation.TextMapPropagator - consumeAll bool skipDeclare bool topic string msgInFlight *int64 @@ -64,7 +63,7 @@ type Driver struct { // FromConfig initializes google_pub_sub_driver_ pipeline func FromConfig(tracer *sdktrace.TracerProvider, configKey string, pipe jobs.Pipeline, log *zap.Logger, cfg Configurer, pq jobs.Queue) (*Driver, error) { - const op = errors.Op("new_google_pub_sub_consumer") + const op = errors.Op("google_pub_sub_consumer") if tracer == nil { tracer = sdktrace.NewTracerProvider() @@ -130,7 +129,7 @@ func FromConfig(tracer *sdktrace.TracerProvider, configKey string, pipe jobs.Pip // FromPipeline initializes consumer from pipeline func FromPipeline(tracer *sdktrace.TracerProvider, pipe jobs.Pipeline, log *zap.Logger, cfg Configurer, pq jobs.Queue) (*Driver, error) { - const op = errors.Op("new_google_pub_sub_consumer_from_pipeline") + const op = errors.Op("google_pub_sub_consumer_from_pipeline") if tracer == nil { tracer = sdktrace.NewTracerProvider() } @@ -185,9 +184,7 @@ func FromPipeline(tracer *sdktrace.TracerProvider, pipe jobs.Pipeline, log *zap. } func (d *Driver) Push(ctx context.Context, jb jobs.Message) error { - const op = errors.Op("google_pub_sub_driver_push") // check if the pipeline registered - ctx, span := trace.SpanFromContext(ctx).TracerProvider().Tracer(tracerName).Start(ctx, "google_pub_sub_push") defer span.End() @@ -246,7 +243,6 @@ func (d *Driver) Run(ctx context.Context, p jobs.Pipeline) error { } func (d *Driver) State(ctx context.Context) (*jobs.State, error) { - const op = errors.Op("google_pub_sub_driver_state") _, span := trace.SpanFromContext(ctx).TracerProvider().Tracer(tracerName).Start(ctx, "google_pub_sub_state") defer span.End() @@ -362,8 +358,7 @@ func (d *Driver) manageTopic(ctx context.Context) error { _, err = d.client.CreateSubscription(ctx, d.sub, pubsub.SubscriptionConfig{ Topic: topic, - AckDeadline: 10 * time.Second, - ExpirationPolicy: time.Duration(0), + AckDeadline: 30 * time.Second, }) if err != nil { if !strings.Contains(err.Error(), "Subscription already exists") { diff --git a/pubsubjobs/item.go b/pubsubjobs/item.go index 82b9c83..c6784c0 100644 --- a/pubsubjobs/item.go +++ b/pubsubjobs/item.go @@ -161,13 +161,6 @@ func fromJob(job jobs.Message) *Item { }, } } -func bytesToStr(data []byte) string { - if len(data) == 0 { - return "" - } - - return unsafe.String(unsafe.SliceData(data), len(data)) -} func strToBytes(data string) []byte { if data == "" { diff --git a/pubsubjobs/listener.go b/pubsubjobs/listener.go index 2d66ac2..ef4f08d 100644 --- a/pubsubjobs/listener.go +++ b/pubsubjobs/listener.go @@ -17,7 +17,7 @@ func (d *Driver) listen(ctx context.Context) { d.log.Debug("listener was stopped") return default: - d.client.Subscription(d.sub).Receive(ctx, func(ctx context.Context, message *pubsub.Message) { + err := d.client.Subscription(d.sub).Receive(ctx, func(ctx context.Context, message *pubsub.Message) { d.cond.L.Lock() // lock when we hit the limit for atomic.LoadInt64(d.msgInFlight) >= int64(atomic.LoadInt32(d.msgInFlightLimit)) { @@ -53,6 +53,10 @@ func (d *Driver) listen(ctx context.Context) { d.cond.L.Unlock() span.End() }) + + if err != nil { + d.log.Error("subscribing error", zap.Error(err)) + } } } }() From c1dda19d40171f8b4488bf29f170601e9b36df36 Mon Sep 17 00:00:00 2001 From: Kajetan Date: Sat, 10 Feb 2024 09:58:15 +0100 Subject: [PATCH 6/9] CI fix --- .github/workflows/linux.yml | 4 ++-- pubsubjobs/driver.go | 2 +- pubsubjobs/item.go | 10 +++++----- pubsubjobs/listener.go | 8 ++++---- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 2df8787..6e33100 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -73,11 +73,11 @@ jobs: mkdir ./coverage-ci docker compose -f env/docker-compose-emulator-local.yaml up -d sleep 30 - go test -timeout 20m -v -race -cover -tags=debug -failfast -coverpkg=$(cat pkgs.txt) -coverprofile=./coverage-ci/sqs.out -covermode=atomic jobs_sqs_test.go jobs_sqs_fifo_test.go + go test -timeout 20m -v -race -cover -tags=debug -failfast -coverpkg=$(cat pkgs.txt) -coverprofile=./coverage-ci/pubsub.out -covermode=atomic jobs_test.go - name: Run unit tests with coverage run: | - go test -timeout 20m -v -race -cover -tags=debug -failfast -coverpkg=$(cat ./tests/pkgs.txt) -coverprofile=./tests/coverage-ci/sqs_u.out -covermode=atomic ./... + go test -timeout 20m -v -race -cover -tags=debug -failfast -coverpkg=$(cat ./tests/pkgs.txt) -coverprofile=./tests/coverage-ci/pubsub_u.out -covermode=atomic ./... - name: Archive code coverage results uses: actions/upload-artifact@v4 diff --git a/pubsubjobs/driver.go b/pubsubjobs/driver.go index 18cae17..77ad086 100644 --- a/pubsubjobs/driver.go +++ b/pubsubjobs/driver.go @@ -190,7 +190,7 @@ func (d *Driver) Push(ctx context.Context, jb jobs.Message) error { job := fromJob(jb) - data, err := json.Marshal(job.headers) + data, err := json.Marshal(job.Metadata) if err != nil { return err } diff --git a/pubsubjobs/item.go b/pubsubjobs/item.go index c6784c0..7bf76cd 100644 --- a/pubsubjobs/item.go +++ b/pubsubjobs/item.go @@ -23,7 +23,7 @@ type Item struct { // Payload is string data (usually JSON) passed to Job broker. Payload string `json:"payload"` // Headers with key-values pairs - headers map[string][]string `json:"headers"` + Metadata map[string][]string `json:"headers"` // Options contains set of PipelineOptions specific to job execution. Can be empty. Options *Options `json:"options,omitempty"` } @@ -71,7 +71,7 @@ func (i *Item) Body() []byte { } func (i *Item) Headers() map[string][]string { - return i.headers + return i.Metadata } // Context packs job context (job, id) into binary payload. @@ -89,7 +89,7 @@ func (i *Item) Context() ([]byte, error) { ID: i.Ident, Job: i.Job, Driver: pluginName, - Headers: i.headers, + Headers: i.Metadata, Queue: i.Options.Queue, Pipeline: i.Options.Pipeline, }, @@ -152,7 +152,7 @@ func fromJob(job jobs.Message) *Item { Job: job.Name(), Ident: job.ID(), Payload: string(job.Payload()), - headers: job.Headers(), + Metadata: job.Headers(), Options: &Options{ Priority: job.Priority(), Pipeline: job.GroupID(), @@ -218,7 +218,7 @@ func (c *Driver) unpack(message *pubsub.Message) *Item { Job: rrj, Ident: rrid, Payload: string(message.Data), - headers: h, + Metadata: h, Options: &Options{ AutoAck: autoAck, Delay: int64(dl), diff --git a/pubsubjobs/listener.go b/pubsubjobs/listener.go index ef4f08d..41c3bac 100644 --- a/pubsubjobs/listener.go +++ b/pubsubjobs/listener.go @@ -28,7 +28,7 @@ func (d *Driver) listen(ctx context.Context) { d.log.Debug("receive message", zap.Stringp("ID", &message.ID)) item := d.unpack(message) - ctxspan, span := d.tracer.Tracer(tracerName).Start(d.prop.Extract(context.Background(), propagation.HeaderCarrier(item.headers)), "google_pub_sub_listener") + ctxspan, span := d.tracer.Tracer(tracerName).Start(d.prop.Extract(context.Background(), propagation.HeaderCarrier(item.Metadata)), "google_pub_sub_listener") if item.Options.AutoAck { _, err := message.AckWithResult().Get(ctx) if err != nil { @@ -40,11 +40,11 @@ func (d *Driver) listen(ctx context.Context) { d.log.Debug("auto ack is turned on, message acknowledged") } - if item.headers == nil { - item.headers = make(map[string][]string, 2) + if item.Metadata == nil { + item.Metadata = make(map[string][]string, 2) } - d.prop.Inject(ctxspan, propagation.HeaderCarrier(item.headers)) + d.prop.Inject(ctxspan, propagation.HeaderCarrier(item.Metadata)) d.pq.Insert(item) // increase the current number of messages From c69ad8bf947f47689fbf33472046e429273d9fc6 Mon Sep 17 00:00:00 2001 From: Kajetan Date: Sat, 10 Feb 2024 15:46:23 +0100 Subject: [PATCH 7/9] Clean up --- pubsubjobs/driver.go | 21 ++++++--- pubsubjobs/listener.go | 10 +--- tests/jobs_test.go | 2 +- tests/php_test_files/jobs/jobs_bad_resp.php | 22 --------- .../jobs/jobs_create_memory.php | 47 ------------------- tests/php_test_files/jobs/jobs_err.php | 5 -- tests/php_test_files/jobs/jobs_ok.php | 5 -- tests/php_test_files/jobs/jobs_ok_pq.php | 7 +-- .../jobs/jobs_ok_queue_name_exist.php | 28 ----------- tests/php_test_files/jobs/jobs_ok_sleep1.php | 25 ---------- tests/php_test_files/jobs/jobs_ok_slow.php | 25 ---------- .../php_test_files/jobs/jobs_ok_slow_rand.php | 29 ------------ tests/php_test_files/jobs/jobs_send.php | 23 --------- 13 files changed, 18 insertions(+), 231 deletions(-) delete mode 100644 tests/php_test_files/jobs/jobs_bad_resp.php delete mode 100644 tests/php_test_files/jobs/jobs_create_memory.php delete mode 100644 tests/php_test_files/jobs/jobs_ok_queue_name_exist.php delete mode 100644 tests/php_test_files/jobs/jobs_ok_sleep1.php delete mode 100644 tests/php_test_files/jobs/jobs_ok_slow.php delete mode 100644 tests/php_test_files/jobs/jobs_ok_slow_rand.php delete mode 100644 tests/php_test_files/jobs/jobs_send.php diff --git a/pubsubjobs/driver.go b/pubsubjobs/driver.go index 77ad086..0e723ee 100644 --- a/pubsubjobs/driver.go +++ b/pubsubjobs/driver.go @@ -58,7 +58,7 @@ type Driver struct { client *pubsub.Client stopped uint64 - pauseCh chan struct{} + stopCh chan struct{} } // FromConfig initializes google_pub_sub_driver_ pipeline @@ -103,7 +103,7 @@ func FromConfig(tracer *sdktrace.TracerProvider, configKey string, pipe jobs.Pip skipDeclare: conf.SkipTopicDeclaration, topic: conf.Topic, pq: pq, - pauseCh: make(chan struct{}, 1), + stopCh: make(chan struct{}, 2), cond: sync.Cond{L: &sync.Mutex{}}, msgInFlightLimit: ptr(conf.Prefetch), msgInFlight: ptr(int64(0)), @@ -156,7 +156,7 @@ func FromPipeline(tracer *sdktrace.TracerProvider, pipe jobs.Pipeline, log *zap. tracer: tracer, log: log, pq: pq, - pauseCh: make(chan struct{}, 1), + stopCh: make(chan struct{}, 2), skipDeclare: pipe.Bool(skipTopicDeclaration, false), topic: pipe.String(topic, "default"), cond: sync.Cond{L: &sync.Mutex{}}, @@ -184,10 +184,17 @@ func FromPipeline(tracer *sdktrace.TracerProvider, pipe jobs.Pipeline, log *zap. } func (d *Driver) Push(ctx context.Context, jb jobs.Message) error { + const op = errors.Op("google_pub_sub_push") // check if the pipeline registered ctx, span := trace.SpanFromContext(ctx).TracerProvider().Tracer(tracerName).Start(ctx, "google_pub_sub_push") defer span.End() + // load atomic value + pipe := *d.pipeline.Load() + if pipe.Name() != jb.GroupID() { + return errors.E(op, errors.Errorf("no such pipeline: %s, actual: %s", jb.GroupID(), pipe.Name())) + } + job := fromJob(jb) data, err := json.Marshal(job.Metadata) @@ -274,7 +281,7 @@ func (d *Driver) Pause(ctx context.Context, p string) error { } // stop consume - d.pauseCh <- struct{}{} + d.stopCh <- struct{}{} // if blocked, let 1 item to pass to unblock the listener and close the pipe d.cond.Signal() @@ -333,7 +340,7 @@ func (d *Driver) Stop(ctx context.Context) error { // if blocked, let 1 item to pass to unblock the listener and close the pipe d.cond.Signal() - d.pauseCh <- struct{}{} + d.stopCh <- struct{}{} } d.log.Debug("pipeline was stopped", zap.String("driver", pipe.Driver()), zap.String("pipeline", pipe.Name()), zap.Time("start", time.Now().UTC()), zap.Duration("elapsed", time.Since(start))) @@ -357,8 +364,8 @@ func (d *Driver) manageTopic(ctx context.Context) error { } _, err = d.client.CreateSubscription(ctx, d.sub, pubsub.SubscriptionConfig{ - Topic: topic, - AckDeadline: 30 * time.Second, + Topic: topic, + AckDeadline: 10 * time.Minute, }) if err != nil { if !strings.Contains(err.Error(), "Subscription already exists") { diff --git a/pubsubjobs/listener.go b/pubsubjobs/listener.go index 41c3bac..6803ebf 100644 --- a/pubsubjobs/listener.go +++ b/pubsubjobs/listener.go @@ -13,7 +13,7 @@ func (d *Driver) listen(ctx context.Context) { go func() { for { select { - case <-d.pauseCh: + case <-d.stopCh: d.log.Debug("listener was stopped") return default: @@ -30,13 +30,7 @@ func (d *Driver) listen(ctx context.Context) { ctxspan, span := d.tracer.Tracer(tracerName).Start(d.prop.Extract(context.Background(), propagation.HeaderCarrier(item.Metadata)), "google_pub_sub_listener") if item.Options.AutoAck { - _, err := message.AckWithResult().Get(ctx) - if err != nil { - d.log.Error("message acknowledge", zap.Error(err)) - span.RecordError(err) - span.End() - return - } + message.Ack() d.log.Debug("auto ack is turned on, message acknowledged") } diff --git a/tests/jobs_test.go b/tests/jobs_test.go index 1f9213c..ca28daa 100644 --- a/tests/jobs_test.go +++ b/tests/jobs_test.go @@ -456,5 +456,5 @@ func TestAutoAck(t *testing.T) { stopCh <- struct{}{} wg.Wait() - require.Equal(t, 2, oLogger.FilterMessageSnippet("auto ack is turned on, message acknowledged").Len()) + require.Equal(t, 4, oLogger.FilterMessageSnippet("auto ack is turned on, message acknowledged").Len()) } diff --git a/tests/php_test_files/jobs/jobs_bad_resp.php b/tests/php_test_files/jobs/jobs_bad_resp.php deleted file mode 100644 index 6233246..0000000 --- a/tests/php_test_files/jobs/jobs_bad_resp.php +++ /dev/null @@ -1,22 +0,0 @@ -waitPayload()) { - try { - $rr->respond(new RoadRunner\Payload('foo')); - } catch (\Throwable $e) { - $rr->error((string)$e); - } -} diff --git a/tests/php_test_files/jobs/jobs_create_memory.php b/tests/php_test_files/jobs/jobs_create_memory.php deleted file mode 100644 index 3b56d69..0000000 --- a/tests/php_test_files/jobs/jobs_create_memory.php +++ /dev/null @@ -1,47 +0,0 @@ -create(new MemoryCreateInfo( - name: 'example', - priority: 10, - prefetch: 100, -)); - -$queue1->resume(); - -// Create task prototype with default headers -$task1 = $queue1->create('ping', '{"site": "https://example.com"}') // Create task with "echo" name - ->withHeader('attempts', 4) // Number of attempts to execute the task - ->withHeader('retry-delay', 10); // Delay between attempts - -// Push "echo" task to the queue -$task1 = $queue1->dispatch($task1); - -// Select "local" pipeline from jobs -$queue2 = $jobs->connect('local'); -$queue2->resume(); - -// Create task prototype with default headers -$task = $queue2->create('ping', '{"site": "https://example.com"}') // Create task with "echo" name - ->withHeader('attempts', 4) // Number of attempts to execute the task - ->withHeader('retry-delay', 10); // Delay between attempts - -// Push "echo" task to the queue -$task = $queue2->dispatch($task); - -$consumer = new Spiral\RoadRunner\Jobs\Consumer(); - -while ($task = $consumer->waitTask()) { - $task->complete(); -} diff --git a/tests/php_test_files/jobs/jobs_err.php b/tests/php_test_files/jobs/jobs_err.php index 73509dc..6f5420c 100644 --- a/tests/php_test_files/jobs/jobs_err.php +++ b/tests/php_test_files/jobs/jobs_err.php @@ -1,9 +1,4 @@ waitTask()) { try { - sleep(15); + sleep(15); $task->complete(); } catch (\Throwable $e) { $rr->error((string)$e); diff --git a/tests/php_test_files/jobs/jobs_ok_queue_name_exist.php b/tests/php_test_files/jobs/jobs_ok_queue_name_exist.php deleted file mode 100644 index a51c488..0000000 --- a/tests/php_test_files/jobs/jobs_ok_queue_name_exist.php +++ /dev/null @@ -1,28 +0,0 @@ -waitTask()) { - try { - if ('unknown' === $task->getQueue()) { - throw new RuntimeException('Queue name was not found'); - } - - $task->complete(); - } catch (\Throwable $e) { - $rr->error((string)$e); - } -} diff --git a/tests/php_test_files/jobs/jobs_ok_sleep1.php b/tests/php_test_files/jobs/jobs_ok_sleep1.php deleted file mode 100644 index 2894b6e..0000000 --- a/tests/php_test_files/jobs/jobs_ok_sleep1.php +++ /dev/null @@ -1,25 +0,0 @@ -waitTask()) { - try { - sleep(1); - $task->complete(); - } catch (\Throwable $e) { - $rr->error((string)$e); - } -} diff --git a/tests/php_test_files/jobs/jobs_ok_slow.php b/tests/php_test_files/jobs/jobs_ok_slow.php deleted file mode 100644 index cc06dab..0000000 --- a/tests/php_test_files/jobs/jobs_ok_slow.php +++ /dev/null @@ -1,25 +0,0 @@ -waitTask()) { - try { - sleep(60); - $task->complete(); - } catch (\Throwable $e) { - $rr->error((string)$e); - } -} diff --git a/tests/php_test_files/jobs/jobs_ok_slow_rand.php b/tests/php_test_files/jobs/jobs_ok_slow_rand.php deleted file mode 100644 index 52a260c..0000000 --- a/tests/php_test_files/jobs/jobs_ok_slow_rand.php +++ /dev/null @@ -1,29 +0,0 @@ -waitTask()) { - try { - $val = random_int(0, 1000); - if ($val > 995) { - sleep(60); - } - - $task->complete(); - } catch (\Throwable $e) { - $rr->error((string)$e); - } -} diff --git a/tests/php_test_files/jobs/jobs_send.php b/tests/php_test_files/jobs/jobs_send.php deleted file mode 100644 index 10f971c..0000000 --- a/tests/php_test_files/jobs/jobs_send.php +++ /dev/null @@ -1,23 +0,0 @@ -connect('test-1'); - -$queue->dispatch( - $queue->create( - 'my-name', - ['foo' => 'bar'], - new KafkaOptions( - topic: 'mytopic', - offset: PartitionOffset::OFFSET_NEWEST - ) - ) -); From 643568993b57fb21c961efe84f9f4da0989afb9b Mon Sep 17 00:00:00 2001 From: Kajetan Date: Sat, 10 Feb 2024 15:55:17 +0100 Subject: [PATCH 8/9] Clean up --- tests/configs/.rr-init.yaml | 13 ---------- tests/configs/.rr-pq.yaml | 12 --------- tests/jobs_test.go | 50 ++++++++++++++++++------------------- 3 files changed, 25 insertions(+), 50 deletions(-) diff --git a/tests/configs/.rr-init.yaml b/tests/configs/.rr-init.yaml index 6ba9a26..3f878ec 100644 --- a/tests/configs/.rr-init.yaml +++ b/tests/configs/.rr-init.yaml @@ -33,25 +33,12 @@ jobs: driver: google-pub-sub config: prefetch: 1000 - visibility_timeout: 0 - wait_time_seconds: 0 - queue: default - attributes: - DelaySeconds: 0 - MaximumMessageSize: 262144 - MessageRetentionPeriod: 345600 - ReceiveMessageWaitTimeSeconds: 0 - VisibilityTimeout: 30 tags: test: "tag" test-2: driver: google-pub-sub config: - prefetch: 1000 - queue: default-2 - attributes: - MessageRetentionPeriod: 86400 tags: test: "tag" consume: [ "test-1", "test-2" ] diff --git a/tests/configs/.rr-pq.yaml b/tests/configs/.rr-pq.yaml index 4318edb..701c8d3 100644 --- a/tests/configs/.rr-pq.yaml +++ b/tests/configs/.rr-pq.yaml @@ -31,15 +31,6 @@ jobs: driver: google-pub-sub config: prefetch: 1000 - visibility_timeout: 0 - wait_time_seconds: 0 - queue: default-1-pq - attributes: - DelaySeconds: 0 - MaximumMessageSize: 262144 - MessageRetentionPeriod: 345600 - ReceiveMessageWaitTimeSeconds: 0 - VisibilityTimeout: 30 tags: test: "tag-pq" @@ -47,9 +38,6 @@ jobs: driver: google-pub-sub config: prefetch: 1000 - queue: default-2-pq - attributes: - MessageRetentionPeriod: 86400 tags: test: "tag" consume: [ "test-1-pq", "test-2-pq" ] diff --git a/tests/jobs_test.go b/tests/jobs_test.go index ca28daa..3946ab8 100644 --- a/tests/jobs_test.go +++ b/tests/jobs_test.go @@ -282,7 +282,7 @@ func TestJobsError(t *testing.T) { time.Sleep(time.Second * 5) } -func TestRemovePQ(t *testing.T) { +func TestAutoAck(t *testing.T) { err := helpers.CleanEmulator() if err != nil { assert.FailNow(t, "error", err.Error()) @@ -291,8 +291,8 @@ func TestRemovePQ(t *testing.T) { cont := endure.New(slog.LevelDebug) cfg := &config.Plugin{ - Version: "2023.2.0", - Path: "configs/.rr-pq.yaml", + Version: "2023.3.0", + Path: "configs/.rr-init.yaml", Prefix: "rr", } @@ -355,27 +355,18 @@ func TestRemovePQ(t *testing.T) { }() time.Sleep(time.Second * 3) - - for i := 0; i < 10; i++ { - t.Run("PushPipeline", helpers.PushToPipe("test-1-pq", false, "127.0.0.1:6601")) - t.Run("PushPipeline", helpers.PushToPipe("test-2-pq", false, "127.0.0.1:6601")) - } - time.Sleep(time.Second * 3) - - t.Run("DestroyPipeline", helpers.DestroyPipelines("127.0.0.1:6601", "test-1-pq", "test-2-pq")) + t.Run("PushPipeline", helpers.PushToPipe("test-1", true, "127.0.0.1:6001")) + t.Run("PushPipeline", helpers.PushToPipe("test-2", true, "127.0.0.1:6001")) + time.Sleep(time.Second * 2) + t.Run("DestroyPipeline", helpers.DestroyPipelines("127.0.0.1:6001", "test-1", "test-2")) stopCh <- struct{}{} wg.Wait() - assert.Equal(t, 0, oLogger.FilterMessageSnippet("job was processed successfully").Len()) - assert.Equal(t, 2, oLogger.FilterMessageSnippet("pipeline was started").Len()) - assert.Equal(t, 2, oLogger.FilterMessageSnippet("pipeline was stopped").Len()) - assert.Equal(t, 20, oLogger.FilterMessageSnippet("job was pushed successfully").Len()) - assert.Equal(t, 2, oLogger.FilterMessageSnippet("job processing was started").Len()) - assert.Equal(t, 2, oLogger.FilterMessageSnippet("listener was stopped").Len()) + require.Equal(t, 4, oLogger.FilterMessageSnippet("auto ack is turned on, message acknowledged").Len()) } -func TestAutoAck(t *testing.T) { +func TestRemovePQ(t *testing.T) { err := helpers.CleanEmulator() if err != nil { assert.FailNow(t, "error", err.Error()) @@ -384,8 +375,8 @@ func TestAutoAck(t *testing.T) { cont := endure.New(slog.LevelDebug) cfg := &config.Plugin{ - Version: "2023.3.0", - Path: "configs/.rr-init.yaml", + Version: "2023.2.0", + Path: "configs/.rr-pq.yaml", Prefix: "rr", } @@ -448,13 +439,22 @@ func TestAutoAck(t *testing.T) { }() time.Sleep(time.Second * 3) - t.Run("PushPipeline", helpers.PushToPipe("test-1", true, "127.0.0.1:6001")) - t.Run("PushPipeline", helpers.PushToPipe("test-2", true, "127.0.0.1:6001")) - time.Sleep(time.Second * 2) - t.Run("DestroyPipeline", helpers.DestroyPipelines("127.0.0.1:6001", "test-1", "test-2")) + + for i := 0; i < 10; i++ { + t.Run("PushPipeline", helpers.PushToPipe("test-1-pq", false, "127.0.0.1:6601")) + t.Run("PushPipeline", helpers.PushToPipe("test-2-pq", false, "127.0.0.1:6601")) + } + time.Sleep(time.Second * 3) + + t.Run("DestroyPipeline", helpers.DestroyPipelines("127.0.0.1:6601", "test-1-pq", "test-2-pq")) stopCh <- struct{}{} wg.Wait() - require.Equal(t, 4, oLogger.FilterMessageSnippet("auto ack is turned on, message acknowledged").Len()) + assert.Equal(t, 0, oLogger.FilterMessageSnippet("job was processed successfully").Len()) + assert.Equal(t, 2, oLogger.FilterMessageSnippet("pipeline was started").Len()) + assert.Equal(t, 2, oLogger.FilterMessageSnippet("pipeline was stopped").Len()) + assert.Equal(t, 20, oLogger.FilterMessageSnippet("job was pushed successfully").Len()) + assert.Equal(t, 2, oLogger.FilterMessageSnippet("job processing was started").Len()) + assert.Equal(t, 2, oLogger.FilterMessageSnippet("listener was stopped").Len()) } From d57467abd28bcbcbf36aa80bbcc4967779854fa7 Mon Sep 17 00:00:00 2001 From: Kajetan Date: Wed, 14 Feb 2024 15:56:23 +0100 Subject: [PATCH 9/9] Remove unused credentials --- .github/workflows/linux.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 6e33100..e905e56 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -65,9 +65,6 @@ jobs: env: RR_TEST_ENV: ${{ secrets.RR_TEST_ENV }} RR_GOOGLE_PUB_SUB_TEST_ENDPOINT: ${{ secrets.RR_GOOGLE_PUB_SUB__TEST_ENDPOINT }} - RR_GOOGLE_PUB_SUB__TEST_REGION: ${{ secrets.RR_GOOGLE_PUB_SUB__TEST_REGION }} - RR_GOOGLE_PUB_SUB__TEST_KEY: ${{ secrets.RR_GOOGLE_PUB_SUB__TEST_KEY }} - RR_GOOGLE_PUB_SUB__TEST_SECRET: ${{ secrets.RR_GOOGLE_PUB_SUB__TEST_SECRET }} run: | cd tests mkdir ./coverage-ci