diff --git a/console-webapp/package-lock.json b/console-webapp/package-lock.json index d14e459c809..5b0bec4b47b 100644 --- a/console-webapp/package-lock.json +++ b/console-webapp/package-lock.json @@ -628,18 +628,6 @@ } } }, - "node_modules/@angular/build/node_modules/@types/node": { - "version": "25.9.1", - "resolved": "https://us-npm.pkg.dev/artifact-foundry-prod/ah-3p-staging-npm/@types/node/-/node-25.9.1.tgz", - "integrity": "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "undici-types": ">=7.24.0 <7.24.7" - } - }, "node_modules/@angular/build/node_modules/@vitejs/plugin-basic-ssl": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-2.1.0.tgz", @@ -653,24 +641,6 @@ "vite": "^6.0.0 || ^7.0.0" } }, - "node_modules/@angular/build/node_modules/chokidar": { - "version": "5.0.0", - "resolved": "https://us-npm.pkg.dev/artifact-foundry-prod/ah-3p-staging-npm/chokidar/-/chokidar-5.0.0.tgz", - "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "readdirp": "^5.0.0" - }, - "engines": { - "node": ">= 20.19.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/@angular/build/node_modules/magic-string": { "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", @@ -694,22 +664,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/@angular/build/node_modules/readdirp": { - "version": "5.0.0", - "resolved": "https://us-npm.pkg.dev/artifact-foundry-prod/ah-3p-staging-npm/readdirp/-/readdirp-5.0.0.tgz", - "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">= 20.19.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/@angular/build/node_modules/rxjs": { "version": "7.8.2", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", @@ -743,15 +697,6 @@ "node": ">= 12" } }, - "node_modules/@angular/build/node_modules/undici-types": { - "version": "7.24.6", - "resolved": "https://us-npm.pkg.dev/artifact-foundry-prod/ah-3p-staging-npm/undici-types/-/undici-types-7.24.6.tgz", - "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/@angular/build/node_modules/vite": { "version": "7.3.0", "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.0.tgz", @@ -971,24 +916,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@angular/cli/node_modules/chokidar": { - "version": "5.0.0", - "resolved": "https://us-npm.pkg.dev/artifact-foundry-prod/ah-3p-staging-npm/chokidar/-/chokidar-5.0.0.tgz", - "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "readdirp": "^5.0.0" - }, - "engines": { - "node": ">= 20.19.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/@angular/cli/node_modules/cli-spinners": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-3.4.0.tgz", @@ -1092,22 +1019,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/@angular/cli/node_modules/readdirp": { - "version": "5.0.0", - "resolved": "https://us-npm.pkg.dev/artifact-foundry-prod/ah-3p-staging-npm/readdirp/-/readdirp-5.0.0.tgz", - "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">= 20.19.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/@angular/cli/node_modules/rxjs": { "version": "7.8.2", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", @@ -4695,24 +4606,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@schematics/angular/node_modules/chokidar": { - "version": "5.0.0", - "resolved": "https://us-npm.pkg.dev/artifact-foundry-prod/ah-3p-staging-npm/chokidar/-/chokidar-5.0.0.tgz", - "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "readdirp": "^5.0.0" - }, - "engines": { - "node": ">= 20.19.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/@schematics/angular/node_modules/cli-spinners": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-3.4.0.tgz", @@ -4816,22 +4709,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/@schematics/angular/node_modules/readdirp": { - "version": "5.0.0", - "resolved": "https://us-npm.pkg.dev/artifact-foundry-prod/ah-3p-staging-npm/readdirp/-/readdirp-5.0.0.tgz", - "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">= 20.19.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/@schematics/angular/node_modules/rxjs": { "version": "7.8.2", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", diff --git a/core/build.gradle b/core/build.gradle index 71cb8c79a6b..532255ef484 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -126,6 +126,7 @@ dependencies { implementation deps['com.google.api:gax'] implementation deps['com.google.api.grpc:proto-google-common-protos'] implementation deps['com.google.api.grpc:proto-google-cloud-secretmanager-v1'] + implementation deps['com.google.apis:google-api-services-cloudkms'] implementation deps['com.google.api-client:google-api-client'] implementation deps['com.google.api-client:google-api-client-servlet'] implementation deps['com.google.monitoring-client:metrics'] @@ -244,6 +245,7 @@ dependencies { implementation project(':common') testImplementation project(path: ':common', configuration: 'testing') implementation project(':util') + implementation project(':networking') // Import NomulusPostreSql from ':db' for implementation but exclude dependencies. implementation project(path: ':db', configuration: 'implementationApi') nonprodRuntimeOnly project(':db') @@ -277,6 +279,16 @@ dependencies { testImplementation deps['org.mockito:mockito-core'] testImplementation deps['org.mockito:mockito-junit-jupiter'] + implementation deps['io.netty:netty-buffer'] + implementation deps['io.netty:netty-codec-http'] + implementation deps['io.netty:netty-codec'] + implementation deps['io.netty:netty-common'] + implementation deps['io.netty:netty-handler'] + implementation deps['io.netty:netty-transport'] + implementation deps['redis.clients:jedis'] + runtimeOnly deps['io.netty:netty-tcnative-boringssl-static'] + implementation deps['org.yaml:snakeyaml'] + // Indirect dependency found by undeclared-dependency check. Such // dependencies should go after all other implementation and testImplementation // dependencies to avoid overriding them accidentally. diff --git a/core/gradle.lockfile b/core/gradle.lockfile index 9b854803129..f6ece071e2b 100644 --- a/core/gradle.lockfile +++ b/core/gradle.lockfile @@ -3,14 +3,14 @@ # This file is expected to be part of source control. args4j:args4j:2.33=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.charleskorn.kaml:kaml:0.20.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -com.fasterxml.jackson.core:jackson-annotations:2.21=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.fasterxml.jackson.core:jackson-core:2.21.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.fasterxml.jackson.core:jackson-databind:2.21.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.21.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.21.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.fasterxml.jackson.datatype:jackson-datatype-joda:2.21.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.21.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.fasterxml.jackson:jackson-bom:2.21.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.fasterxml.jackson.core:jackson-annotations:2.22=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.fasterxml.jackson.core:jackson-core:2.22.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.fasterxml.jackson.core:jackson-databind:2.22.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.22.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.22.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.fasterxml.jackson.datatype:jackson-datatype-joda:2.22.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.22.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.fasterxml.jackson:jackson-bom:2.22.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.fasterxml.woodstox:woodstox-core:7.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.github.ben-manes.caffeine:caffeine:3.0.5=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor com.github.ben-manes.caffeine:caffeine:3.2.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -26,67 +26,68 @@ com.github.jnr:jnr-posix:3.1.22=compileClasspath,deploy_jar,nonprodCompileClassp com.github.jnr:jnr-unixsocket:0.38.25=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.github.jnr:jnr-x86asm:1.0.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.github.kevinstern:software-and-algorithms:1.0=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor -com.github.mwiede:jsch:2.28.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.github.mwiede:jsch:2.28.2=deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.github.mwiede:jsch:2.28.3=compileClasspath com.google.android:annotations:4.1.1.4=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -com.google.api-client:google-api-client-jackson2:2.0.1=compileClasspath,nonprodCompileClasspath,testCompileClasspath -com.google.api-client:google-api-client-jackson2:2.7.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +com.google.api-client:google-api-client-gson:2.9.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.api-client:google-api-client-java6:2.1.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.api-client:google-api-client-servlet:2.7.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath com.google.api-client:google-api-client-servlet:2.9.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath com.google.api-client:google-api-client:2.9.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.api.grpc:gapic-google-cloud-storage-v2:2.64.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -com.google.api.grpc:gapic-google-cloud-storage-v2:2.68.0=testCompileClasspath -com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1:3.24.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta1:0.196.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta2:0.196.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.api.grpc:gapic-google-cloud-storage-v2:2.67.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +com.google.api.grpc:gapic-google-cloud-storage-v2:2.69.0=testCompileClasspath +com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1:3.27.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta1:0.199.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta2:0.199.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.api.grpc:grpc-google-cloud-bigtable-v2:2.73.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.api.grpc:grpc-google-cloud-pubsub-v1:1.132.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.api.grpc:grpc-google-cloud-spanner-admin-database-v1:6.113.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.api.grpc:grpc-google-cloud-spanner-admin-instance-v1:6.113.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.api.grpc:grpc-google-cloud-spanner-v1:6.113.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.api.grpc:grpc-google-cloud-storage-control-v2:2.44.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.api.grpc:grpc-google-cloud-storage-v2:2.64.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -com.google.api.grpc:grpc-google-cloud-storage-v2:2.68.0=testCompileClasspath -com.google.api.grpc:grpc-google-common-protos:2.67.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.api.grpc:proto-google-cloud-bigquerystorage-v1:3.24.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.api.grpc:proto-google-cloud-bigquerystorage-v1alpha:3.24.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.api.grpc:proto-google-cloud-bigquerystorage-v1beta1:0.196.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.api.grpc:proto-google-cloud-bigquerystorage-v1beta2:0.196.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.api.grpc:proto-google-cloud-bigquerystorage-v1beta:3.24.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.api.grpc:grpc-google-cloud-pubsub-v1:1.132.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.api.grpc:grpc-google-cloud-spanner-admin-database-v1:6.116.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.api.grpc:grpc-google-cloud-spanner-admin-instance-v1:6.116.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.api.grpc:grpc-google-cloud-spanner-v1:6.116.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.api.grpc:grpc-google-cloud-storage-control-v2:2.49.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.api.grpc:grpc-google-cloud-storage-v2:2.67.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +com.google.api.grpc:grpc-google-cloud-storage-v2:2.69.0=testCompileClasspath +com.google.api.grpc:grpc-google-common-protos:2.70.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.api.grpc:proto-google-cloud-bigquerystorage-v1:3.27.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.api.grpc:proto-google-cloud-bigquerystorage-v1alpha:3.27.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.api.grpc:proto-google-cloud-bigquerystorage-v1beta1:0.199.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.api.grpc:proto-google-cloud-bigquerystorage-v1beta2:0.199.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.api.grpc:proto-google-cloud-bigquerystorage-v1beta:3.27.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.api.grpc:proto-google-cloud-bigtable-admin-v2:2.73.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.api.grpc:proto-google-cloud-bigtable-v2:2.75.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.api.grpc:proto-google-cloud-compute-v1:1.102.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.api.grpc:proto-google-cloud-datastore-v1:0.128.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.api.grpc:proto-google-cloud-firestore-v1:3.39.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.api.grpc:proto-google-cloud-logging-v2:0.118.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.api.grpc:proto-google-cloud-monitoring-v3:3.85.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.api.grpc:proto-google-cloud-pubsub-v1:1.132.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.api.grpc:proto-google-cloud-bigtable-v2:2.77.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.api.grpc:proto-google-cloud-compute-v1:1.103.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.api.grpc:proto-google-cloud-datastore-v1:0.131.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.api.grpc:proto-google-cloud-firestore-v1:3.41.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.api.grpc:proto-google-cloud-logging-v2:0.121.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.api.grpc:proto-google-cloud-monitoring-v3:3.92.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.api.grpc:proto-google-cloud-pubsub-v1:1.132.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.api.grpc:proto-google-cloud-secretmanager-v1:2.51.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -com.google.api.grpc:proto-google-cloud-secretmanager-v1:2.92.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath -com.google.api.grpc:proto-google-cloud-secretmanager-v1beta1:2.92.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath +com.google.api.grpc:proto-google-cloud-secretmanager-v1:2.93.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath +com.google.api.grpc:proto-google-cloud-secretmanager-v1beta1:2.93.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath com.google.api.grpc:proto-google-cloud-secretmanager-v1beta2:2.51.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -com.google.api.grpc:proto-google-cloud-secretmanager-v1beta2:2.92.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath -com.google.api.grpc:proto-google-cloud-spanner-admin-database-v1:6.113.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.api.grpc:proto-google-cloud-spanner-admin-instance-v1:6.113.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.api.grpc:proto-google-cloud-spanner-v1:6.113.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.api.grpc:proto-google-cloud-storage-control-v2:2.44.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.api.grpc:proto-google-cloud-storage-v2:2.64.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath -com.google.api.grpc:proto-google-cloud-storage-v2:2.68.0=testCompileClasspath,testRuntimeClasspath +com.google.api.grpc:proto-google-cloud-secretmanager-v1beta2:2.93.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath +com.google.api.grpc:proto-google-cloud-spanner-admin-database-v1:6.116.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.api.grpc:proto-google-cloud-spanner-admin-instance-v1:6.116.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.api.grpc:proto-google-cloud-spanner-v1:6.116.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.api.grpc:proto-google-cloud-storage-control-v2:2.49.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.api.grpc:proto-google-cloud-storage-v2:2.67.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath +com.google.api.grpc:proto-google-cloud-storage-v2:2.69.0=testCompileClasspath,testRuntimeClasspath com.google.api.grpc:proto-google-cloud-tasks-v2:2.51.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -com.google.api.grpc:proto-google-cloud-tasks-v2:2.92.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath +com.google.api.grpc:proto-google-cloud-tasks-v2:2.93.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath com.google.api.grpc:proto-google-cloud-tasks-v2beta2:0.141.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -com.google.api.grpc:proto-google-cloud-tasks-v2beta2:0.182.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath +com.google.api.grpc:proto-google-cloud-tasks-v2beta2:2.93.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath com.google.api.grpc:proto-google-cloud-tasks-v2beta3:0.141.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -com.google.api.grpc:proto-google-cloud-tasks-v2beta3:0.182.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath -com.google.api.grpc:proto-google-common-protos:2.71.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.api.grpc:proto-google-iam-v1:1.62.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath -com.google.api.grpc:proto-google-iam-v1:1.66.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath,testRuntimeClasspath -com.google.api:api-common:2.63.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.api:gax-grpc:2.80.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.api:gax-httpjson:2.80.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.api:gax:2.80.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.apis:google-api-services-admin-directory:directory_v1-rev20260522-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.api.grpc:proto-google-cloud-tasks-v2beta3:2.93.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath +com.google.api.grpc:proto-google-common-protos:2.72.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.api.grpc:proto-google-iam-v1:1.65.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath +com.google.api.grpc:proto-google-iam-v1:1.67.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath,testRuntimeClasspath +com.google.api:api-common:2.64.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.api:gax-grpc:2.81.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.api:gax-httpjson:2.81.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.api:gax:2.81.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.apis:google-api-services-admin-directory:directory_v1-rev20260531-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.apis:google-api-services-bigquery:v2-rev20251012-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.apis:google-api-services-cloudkms:v1-rev20260522-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.apis:google-api-services-cloudresourcemanager:v1-rev20250606-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.apis:google-api-services-dataflow:v1b3-rev20260503-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.apis:google-api-services-dns:v1-rev20260421-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -101,8 +102,8 @@ com.google.apis:google-api-services-pubsub:v1-rev20220904-2.0.0=compileClasspath com.google.apis:google-api-services-sheets:v4-rev20260213-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.apis:google-api-services-sqladmin:v1beta4-rev20260510-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.apis:google-api-services-storage:v1-rev20260204-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.auth:google-auth-library-credentials:1.47.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.auth:google-auth-library-oauth2-http:1.47.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.auth:google-auth-library-credentials:1.48.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.auth:google-auth-library-oauth2-http:1.48.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.auto.service:auto-service-annotations:1.0.1=nonprodAnnotationProcessor,testAnnotationProcessor com.google.auto.service:auto-service-annotations:1.1.1=annotationProcessor,compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.auto.service:auto-service:1.1.1=annotationProcessor @@ -110,41 +111,41 @@ com.google.auto.value:auto-value-annotations:1.11.0=compileClasspath,deploy_jar, com.google.auto.value:auto-value-annotations:1.9=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor com.google.auto.value:auto-value:1.11.1=annotationProcessor,deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testRuntimeClasspath com.google.auto:auto-common:1.2.2=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor -com.google.cloud.bigdataoss:gcsio:2.2.26=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.cloud.bigdataoss:util:2.2.26=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.cloud.bigdataoss:gcsio:3.1.16=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.cloud.bigdataoss:util:3.1.16=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.cloud.bigtable:bigtable-client-core-config:1.28.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.cloud.datastore:datastore-v1-proto-client:2.37.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.cloud.datastore:datastore-v1-proto-client:2.40.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.cloud.opentelemetry:detector-resources-support:0.33.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.cloud.opentelemetry:exporter-metrics:0.33.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.cloud.opentelemetry:shared-resourcemapping:0.33.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath com.google.cloud.sql:jdbc-socket-factory-core:1.28.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.cloud.sql:postgres-socket-factory:1.28.4=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -com.google.cloud:google-cloud-bigquerystorage:3.24.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.cloud:google-cloud-bigquerystorage:3.27.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.cloud:google-cloud-bigtable:2.73.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.cloud:google-cloud-compute:1.102.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.cloud:google-cloud-core-grpc:2.66.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath -com.google.cloud:google-cloud-core-grpc:2.70.0=testCompileClasspath,testRuntimeClasspath -com.google.cloud:google-cloud-core-http:2.66.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -com.google.cloud:google-cloud-core-http:2.70.0=testCompileClasspath -com.google.cloud:google-cloud-core:2.66.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath -com.google.cloud:google-cloud-core:2.70.0=testCompileClasspath,testRuntimeClasspath -com.google.cloud:google-cloud-firestore:3.39.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.cloud:google-cloud-logging:3.29.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.cloud:google-cloud-monitoring:3.85.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.cloud:google-cloud-compute:1.103.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.cloud:google-cloud-core-grpc:2.69.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath +com.google.cloud:google-cloud-core-grpc:2.71.0=testCompileClasspath,testRuntimeClasspath +com.google.cloud:google-cloud-core-http:2.69.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +com.google.cloud:google-cloud-core-http:2.71.0=testCompileClasspath +com.google.cloud:google-cloud-core:2.69.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath +com.google.cloud:google-cloud-core:2.71.0=testCompileClasspath,testRuntimeClasspath +com.google.cloud:google-cloud-firestore:3.41.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.cloud:google-cloud-logging:3.32.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.cloud:google-cloud-monitoring:3.92.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.cloud:google-cloud-nio:0.127.24=testRuntimeClasspath -com.google.cloud:google-cloud-nio:0.132.0=testCompileClasspath -com.google.cloud:google-cloud-pubsub:1.150.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.cloud:google-cloud-nio:0.133.0=testCompileClasspath +com.google.cloud:google-cloud-pubsub:1.150.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.cloud:google-cloud-secretmanager:2.51.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -com.google.cloud:google-cloud-secretmanager:2.92.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath -com.google.cloud:google-cloud-spanner:6.113.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.cloud:google-cloud-storage-control:2.44.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.cloud:google-cloud-storage:2.64.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -com.google.cloud:google-cloud-storage:2.68.0=testCompileClasspath +com.google.cloud:google-cloud-secretmanager:2.93.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath +com.google.cloud:google-cloud-spanner:6.116.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.cloud:google-cloud-storage-control:2.49.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.cloud:google-cloud-storage:2.67.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +com.google.cloud:google-cloud-storage:2.69.0=testCompileClasspath com.google.cloud:google-cloud-tasks:2.51.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -com.google.cloud:google-cloud-tasks:2.92.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath -com.google.cloud:grpc-gcp:1.9.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.cloud:google-cloud-tasks:2.93.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath +com.google.cloud:grpc-gcp:1.10.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.cloud:libraries-bom:26.48.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -com.google.cloud:proto-google-cloud-firestore-bundle-v1:3.39.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.cloud:proto-google-cloud-firestore-bundle-v1:3.41.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.code.findbugs:jsr305:3.0.2=annotationProcessor,checkstyle,compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath com.google.code.gson:gson:2.14.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.dagger:dagger-compiler:2.59.2=annotationProcessor,testAnnotationProcessor @@ -160,7 +161,7 @@ com.google.errorprone:error_prone_core:2.48.0=annotationProcessor,nonprodAnnotat com.google.flatbuffers:flatbuffers-java:24.3.25=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.flogger:flogger-system-backend:0.7.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.flogger:flogger:0.7.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.flogger:google-extensions:0.7.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.flogger:google-extensions:0.7.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.googlejavaformat:google-java-format:1.34.1=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor com.google.guava:failureaccess:1.0.3=annotationProcessor,checkstyle,compileClasspath,deploy_jar,nonprodAnnotationProcessor,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath com.google.guava:guava-testlib:33.3.0-jre=testRuntimeClasspath @@ -195,13 +196,17 @@ com.google.truth:truth:1.4.5=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath com.ibm.icu:icu4j:73.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.lmax:disruptor:3.4.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.puppycrawl.tools:checkstyle:10.24.0=checkstyle -com.squareup.okhttp3:okhttp-jvm:5.3.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.squareup.okhttp3:okhttp:5.3.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.squareup.okhttp3:okhttp-jvm:5.3.2=deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.squareup.okhttp3:okhttp-jvm:5.4.0=compileClasspath +com.squareup.okhttp3:okhttp:5.3.2=deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.squareup.okhttp3:okhttp:5.4.0=compileClasspath com.squareup.okio:okio-bom:3.0.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath com.squareup.okio:okio-fakefilesystem-jvm:3.4.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.squareup.okio:okio-fakefilesystem:3.4.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.squareup.okio:okio-jvm:3.16.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.squareup.okio:okio:3.16.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.squareup.okio:okio-jvm:3.16.4=deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.squareup.okio:okio-jvm:3.17.0=compileClasspath +com.squareup.okio:okio:3.16.4=deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.squareup.okio:okio:3.17.0=compileClasspath com.squareup.wire:wire-compiler:4.5.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.squareup.wire:wire-grpc-server-generator:4.5.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath com.squareup.wire:wire-grpc-server:4.5.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath @@ -246,63 +251,68 @@ io.github.ss-bhatt:testcontainers-valkey:1.0.0=testCompileClasspath,testRuntimeC io.grpc:grpc-alts:1.81.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.grpc:grpc-api:1.81.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.grpc:grpc-auth:1.81.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.grpc:grpc-census:1.76.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.grpc:grpc-census:1.80.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.grpc:grpc-context:1.81.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.grpc:grpc-core:1.81.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.grpc:grpc-googleapis:1.81.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.grpc:grpc-grpclb:1.76.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath +io.grpc:grpc-grpclb:1.80.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath io.grpc:grpc-grpclb:1.81.0=testCompileClasspath,testRuntimeClasspath io.grpc:grpc-inprocess:1.81.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.grpc:grpc-netty-shaded:1.81.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.grpc:grpc-netty:1.76.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.grpc:grpc-opentelemetry:1.76.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath +io.grpc:grpc-netty:1.80.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.grpc:grpc-opentelemetry:1.80.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath io.grpc:grpc-opentelemetry:1.81.0=testCompileClasspath,testRuntimeClasspath io.grpc:grpc-protobuf-lite:1.81.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath io.grpc:grpc-protobuf:1.81.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.grpc:grpc-rls:1.76.3=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath +io.grpc:grpc-rls:1.80.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath io.grpc:grpc-rls:1.81.0=testRuntimeClasspath -io.grpc:grpc-services:1.76.3=compileClasspath,nonprodCompileClasspath,testCompileClasspath +io.grpc:grpc-services:1.80.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath io.grpc:grpc-services:1.81.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath io.grpc:grpc-stub:1.81.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.grpc:grpc-util:1.76.3=compileClasspath,nonprodCompileClasspath,testCompileClasspath +io.grpc:grpc-testing:1.70.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.grpc:grpc-util:1.80.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath io.grpc:grpc-util:1.81.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.grpc:grpc-xds:1.76.3=compileClasspath,nonprodCompileClasspath,testCompileClasspath +io.grpc:grpc-xds:1.80.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath io.grpc:grpc-xds:1.81.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.netty:netty-buffer:4.1.124.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-codec-http2:4.1.124.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-codec-http:4.1.124.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-codec-socks:4.1.124.Final=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.netty:netty-codec:4.1.124.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-common:4.1.124.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-handler-proxy:4.1.124.Final=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.netty:netty-handler:4.1.124.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-resolver:4.1.124.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-buffer:4.2.15.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-codec-base:4.2.15.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-codec-compression:4.2.15.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-codec-http2:4.1.130.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-codec-http:4.2.15.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-codec-marshalling:4.2.15.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-codec-protobuf:4.2.15.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-codec-socks:4.1.130.Final=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.netty:netty-codec:4.2.15.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-common:4.2.15.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-handler-proxy:4.1.130.Final=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.netty:netty-handler:4.2.15.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-resolver:4.2.15.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.netty:netty-tcnative-boringssl-static:2.0.52.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.netty:netty-tcnative-classes:2.0.52.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-transport-native-unix-common:4.1.124.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-transport:4.1.124.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-transport-native-unix-common:4.2.15.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-transport:4.2.15.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.opencensus:opencensus-api:0.31.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.opencensus:opencensus-contrib-exemplar-util:0.31.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.opencensus:opencensus-contrib-grpc-metrics:0.31.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath -io.opencensus:opencensus-contrib-grpc-metrics:0.31.1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.opencensus:opencensus-contrib-exemplar-util:0.31.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.opencensus:opencensus-contrib-grpc-metrics:0.31.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.opencensus:opencensus-contrib-grpc-util:0.31.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.opencensus:opencensus-contrib-http-util:0.31.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.opencensus:opencensus-contrib-resource-util:0.31.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.opencensus:opencensus-exporter-metrics-util:0.31.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.opencensus:opencensus-exporter-stats-stackdriver:0.31.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.opencensus:opencensus-impl-core:0.31.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.opencensus:opencensus-impl:0.31.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.opencensus:opencensus-contrib-resource-util:0.31.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.opencensus:opencensus-exporter-metrics-util:0.31.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.opencensus:opencensus-exporter-stats-stackdriver:0.31.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.opencensus:opencensus-impl-core:0.31.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.opencensus:opencensus-impl:0.31.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.opentelemetry.contrib:opentelemetry-gcp-resources:1.37.0-alpha=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath io.opentelemetry.contrib:opentelemetry-gcp-resources:1.45.0-alpha=testCompileClasspath io.opentelemetry.instrumentation:opentelemetry-grpc-1.6:2.1.0-alpha=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.opentelemetry.instrumentation:opentelemetry-instrumentation-api-incubator:2.1.0-alpha=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.opentelemetry.instrumentation:opentelemetry-instrumentation-api:2.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.opentelemetry.semconv:opentelemetry-semconv:1.29.0-alpha=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.opentelemetry:opentelemetry-api:1.51.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath +io.opentelemetry:opentelemetry-api:1.56.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath io.opentelemetry:opentelemetry-api:1.62.0=testCompileClasspath,testRuntimeClasspath io.opentelemetry:opentelemetry-bom:1.42.1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.opentelemetry:opentelemetry-common:1.56.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath io.opentelemetry:opentelemetry-common:1.62.0=testCompileClasspath,testRuntimeClasspath -io.opentelemetry:opentelemetry-context:1.51.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath +io.opentelemetry:opentelemetry-context:1.56.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath io.opentelemetry:opentelemetry-context:1.62.0=testCompileClasspath,testRuntimeClasspath io.opentelemetry:opentelemetry-exporter-logging:1.62.0=testCompileClasspath,testRuntimeClasspath io.opentelemetry:opentelemetry-extension-incubator:1.35.0-alpha=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath @@ -359,25 +369,25 @@ org.antlr:antlr4:4.13.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonp org.apache.arrow:arrow-format:17.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.apache.arrow:arrow-memory-core:17.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.apache.arrow:arrow-vector:17.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.apache.avro:avro:1.11.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.apache.beam:beam-model-fn-execution:2.73.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.apache.beam:beam-model-job-management:2.73.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.apache.beam:beam-model-pipeline:2.73.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.apache.avro:avro:1.12.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.apache.beam:beam-model-fn-execution:2.74.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.apache.beam:beam-model-job-management:2.74.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.apache.beam:beam-model-pipeline:2.74.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.apache.beam:beam-runners-core-construction-java:2.54.0=testCompileClasspath,testRuntimeClasspath -org.apache.beam:beam-runners-core-java:2.73.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.apache.beam:beam-runners-direct-java:2.73.0=testCompileClasspath,testRuntimeClasspath -org.apache.beam:beam-runners-google-cloud-dataflow-java:2.73.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.apache.beam:beam-runners-java-fn-execution:2.73.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.apache.beam:beam-sdks-java-core:2.73.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.apache.beam:beam-sdks-java-expansion-service:2.73.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.apache.beam:beam-sdks-java-extensions-arrow:2.73.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.apache.beam:beam-sdks-java-extensions-avro:2.73.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.apache.beam:beam-sdks-java-extensions-google-cloud-platform-core:2.73.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.apache.beam:beam-sdks-java-extensions-protobuf:2.73.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.apache.beam:beam-runners-core-java:2.74.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.apache.beam:beam-runners-direct-java:2.74.0=testCompileClasspath,testRuntimeClasspath +org.apache.beam:beam-runners-google-cloud-dataflow-java:2.74.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.apache.beam:beam-runners-java-fn-execution:2.74.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.apache.beam:beam-sdks-java-core:2.74.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.apache.beam:beam-sdks-java-expansion-service:2.74.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.apache.beam:beam-sdks-java-extensions-arrow:2.74.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.apache.beam:beam-sdks-java-extensions-avro:2.74.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.apache.beam:beam-sdks-java-extensions-google-cloud-platform-core:2.74.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.apache.beam:beam-sdks-java-extensions-protobuf:2.74.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.apache.beam:beam-sdks-java-fn-execution:2.54.0=testCompileClasspath,testRuntimeClasspath -org.apache.beam:beam-sdks-java-harness:2.73.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.apache.beam:beam-sdks-java-io-google-cloud-platform:2.73.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.apache.beam:beam-sdks-java-transform-service-launcher:2.73.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.apache.beam:beam-sdks-java-harness:2.74.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.apache.beam:beam-sdks-java-io-google-cloud-platform:2.74.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.apache.beam:beam-sdks-java-transform-service-launcher:2.74.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.apache.beam:beam-vendor-grpc-1_60_1:0.1=testCompileClasspath,testRuntimeClasspath org.apache.beam:beam-vendor-grpc-1_69_0:0.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.apache.beam:beam-vendor-guava-32_1_2-jre:0.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -430,18 +440,20 @@ org.conscrypt:conscrypt-openjdk-uber:2.5.2=compileClasspath,deploy_jar,nonprodCo org.eclipse.angus:angus-activation:2.0.3=jaxb org.eclipse.angus:angus-activation:2.1.0-M1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.eclipse.angus:jakarta.mail:2.1.0-M1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -org.eclipse.jetty.ee10:jetty-ee10-servlet:12.1.9=testCompileClasspath,testRuntimeClasspath -org.eclipse.jetty.ee10:jetty-ee10-webapp:12.1.9=testCompileClasspath,testRuntimeClasspath -org.eclipse.jetty.ee:jetty-ee-webapp:12.1.9=testCompileClasspath,testRuntimeClasspath -org.eclipse.jetty:jetty-http:12.1.9=testCompileClasspath,testRuntimeClasspath -org.eclipse.jetty:jetty-io:12.1.9=testCompileClasspath,testRuntimeClasspath -org.eclipse.jetty:jetty-security:12.1.9=testCompileClasspath,testRuntimeClasspath -org.eclipse.jetty:jetty-server:12.1.9=testCompileClasspath,testRuntimeClasspath -org.eclipse.jetty:jetty-session:12.1.9=testCompileClasspath,testRuntimeClasspath -org.eclipse.jetty:jetty-util:12.1.9=testCompileClasspath,testRuntimeClasspath -org.eclipse.jetty:jetty-xml:12.1.9=testCompileClasspath,testRuntimeClasspath -org.flywaydb:flyway-core:12.7.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.flywaydb:flyway-database-postgresql:12.7.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.eclipse.jetty.ee10:jetty-ee10-servlet:12.1.10=testCompileClasspath,testRuntimeClasspath +org.eclipse.jetty.ee10:jetty-ee10-webapp:12.1.10=testCompileClasspath,testRuntimeClasspath +org.eclipse.jetty.ee:jetty-ee-webapp:12.1.10=testCompileClasspath,testRuntimeClasspath +org.eclipse.jetty:jetty-http:12.1.10=testCompileClasspath,testRuntimeClasspath +org.eclipse.jetty:jetty-io:12.1.10=testCompileClasspath,testRuntimeClasspath +org.eclipse.jetty:jetty-security:12.1.10=testCompileClasspath,testRuntimeClasspath +org.eclipse.jetty:jetty-server:12.1.10=testCompileClasspath,testRuntimeClasspath +org.eclipse.jetty:jetty-session:12.1.10=testCompileClasspath,testRuntimeClasspath +org.eclipse.jetty:jetty-util:12.1.10=testCompileClasspath,testRuntimeClasspath +org.eclipse.jetty:jetty-xml:12.1.10=testCompileClasspath,testRuntimeClasspath +org.flywaydb:flyway-core:12.8.0=deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.flywaydb:flyway-core:12.8.1=compileClasspath +org.flywaydb:flyway-database-postgresql:12.8.0=deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.flywaydb:flyway-database-postgresql:12.8.1=compileClasspath org.freemarker:freemarker:2.3.34=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.glassfish.jaxb:codemodel:4.0.9=jaxb org.glassfish.jaxb:jaxb-core:4.0.6=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath @@ -563,12 +575,12 @@ tools.jackson.core:jackson-core:3.1.1=compileClasspath,deploy_jar,nonprodCompile tools.jackson.core:jackson-databind:3.1.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath tools.jackson:jackson-bom:3.1.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath us.fatehi:schemacrawler-api:17.1.7=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -us.fatehi:schemacrawler-diagram:17.11.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -us.fatehi:schemacrawler-operations:17.11.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -us.fatehi:schemacrawler-postgresql:17.11.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -us.fatehi:schemacrawler-text:17.11.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +us.fatehi:schemacrawler-diagram:17.11.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +us.fatehi:schemacrawler-operations:17.11.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +us.fatehi:schemacrawler-postgresql:17.11.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +us.fatehi:schemacrawler-text:17.11.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath us.fatehi:schemacrawler-tools:17.1.7=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath us.fatehi:schemacrawler-utility:17.1.7=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -us.fatehi:schemacrawler:17.11.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +us.fatehi:schemacrawler:17.11.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath xerces:xmlParserAPIs:2.6.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath empty=devtool,shadow diff --git a/core/src/main/java/google/registry/config/RegistryConfig.java b/core/src/main/java/google/registry/config/RegistryConfig.java index cee88303343..a865778f3e6 100644 --- a/core/src/main/java/google/registry/config/RegistryConfig.java +++ b/core/src/main/java/google/registry/config/RegistryConfig.java @@ -1483,6 +1483,91 @@ public static Optional> provideValkeyHostsAndPorts( .map(valkey -> ImmutableList.copyOf(valkey.hostsAndPorts)); } + @Provides + @Config("eppServerPort") + public static int provideEppServerPort(RegistryConfigSettings config) { + return config.eppServer.port; + } + + @Provides + @Config("eppServerHealthCheckPort") + public static int provideEppServerHealthCheckPort(RegistryConfigSettings config) { + return config.eppServer.healthCheckPort; + } + + @Provides + @Config("eppServerSslPemBucket") + public static String provideEppServerSslPemBucket(RegistryConfigSettings config) { + return config.eppServer.sslPemBucket; + } + + @Provides + @Config("eppServerSslPemFilename") + public static String provideEppServerSslPemFilename(RegistryConfigSettings config) { + return config.eppServer.sslPemFilename; + } + + @Provides + @Config("eppServerKmsLocation") + public static String provideEppServerKmsLocation(RegistryConfigSettings config) { + return config.eppServer.kmsLocation; + } + + @Provides + @Config("eppServerKmsKeyRing") + public static String provideEppServerKmsKeyRing(RegistryConfigSettings config) { + return config.eppServer.kmsKeyRing; + } + + @Provides + @Config("eppServerKmsCryptoKey") + public static String provideEppServerKmsCryptoKey(RegistryConfigSettings config) { + return config.eppServer.kmsCryptoKey; + } + + @Provides + @Config("eppServerMaxMessageLengthBytes") + public static int provideEppServerMaxMessageLengthBytes(RegistryConfigSettings config) { + return config.eppServer.maxMessageLengthBytes; + } + + @Provides + @Config("eppServerHeaderLengthBytes") + public static int provideEppServerHeaderLengthBytes(RegistryConfigSettings config) { + return config.eppServer.headerLengthBytes; + } + + @Provides + @Config("eppServerReadTimeoutSeconds") + public static int provideEppServerReadTimeoutSeconds(RegistryConfigSettings config) { + return config.eppServer.readTimeoutSeconds; + } + + @Provides + @Config("eppServerCertificateCacheSeconds") + public static int provideEppServerCertificateCacheSeconds(RegistryConfigSettings config) { + return config.eppServer.serverCertificateCacheSeconds; + } + + @Provides + @Config("eppServerQuota") + public static RegistryConfigSettings.Quota provideEppServerQuota( + RegistryConfigSettings config) { + return config.eppServer.quota; + } + + @Provides + @Config("eppServerFrontendMetricsRatio") + public static double provideEppServerFrontendMetricsRatio(RegistryConfigSettings config) { + return config.monitoring.frontendMetricsRatio; + } + + @Provides + @Config("writeIntervalSeconds") + public static int provideWriteIntervalSeconds(RegistryConfigSettings config) { + return config.monitoring.writeIntervalSeconds; + } + private static String formatComments(String text) { return Splitter.on('\n').omitEmptyStrings().trimResults().splitToList(text).stream() .map(s -> "# " + s) diff --git a/core/src/main/java/google/registry/config/RegistryConfigSettings.java b/core/src/main/java/google/registry/config/RegistryConfigSettings.java index 5b8ddedadcd..8f5c93f173c 100644 --- a/core/src/main/java/google/registry/config/RegistryConfigSettings.java +++ b/core/src/main/java/google/registry/config/RegistryConfigSettings.java @@ -44,6 +44,7 @@ public class RegistryConfigSettings { public Bsa bsa; public MosApi mosapi; public Valkey valkey; + public EppServer eppServer; /** Configuration options that apply to the entire GCP project. */ public static class GcpProject { @@ -190,6 +191,40 @@ public static class Monitoring { public int stackdriverMaxQps; public int stackdriverMaxPointsPerRequest; public int writeIntervalSeconds; + public double frontendMetricsRatio; + public double backendMetricsRatio; + } + + /** Configuration for EppServer. */ + public static class EppServer { + public int port; + public int healthCheckPort; + public String sslPemFilename; + public String sslPemBucket; + public String kmsLocation; + public String kmsKeyRing; + public String kmsCryptoKey; + public int maxMessageLengthBytes; + public int headerLengthBytes; + public int readTimeoutSeconds; + public int serverCertificateCacheSeconds; + public Quota quota; + } + + /** Configuration options that apply to quota management. */ + public static class Quota { + + /** Quota configuration for a specific set of users. */ + public static class QuotaGroup { + public List userId; + public int tokenAmount; + public int refillSeconds; + public int batchSize; + } + + public int refreshSeconds; + public QuotaGroup defaultQuota; + public List customQuota; } /** Miscellaneous configuration that doesn't quite fit in anywhere else. */ diff --git a/core/src/main/java/google/registry/config/files/default-config.yaml b/core/src/main/java/google/registry/config/files/default-config.yaml index 9b8719d7511..d8996b84e1f 100644 --- a/core/src/main/java/google/registry/config/files/default-config.yaml +++ b/core/src/main/java/google/registry/config/files/default-config.yaml @@ -398,6 +398,53 @@ monitoring: # How often metrics are exported to BigQuery. writeIntervalSeconds: 60 + # What ratio of frontend request metrics should be stochastically recorded + # (0.0 means none, 1.0 means all). This is useful for reducing metrics volume, + # and thus cost, while still recording some information for performance + # monitoring purposes. + frontendMetricsRatio: 1.0 + + # What ratio of backend request metrics should be stochastically recorded + # (0.0 means none, 1.0 means all). This is useful for reducing metrics volume, + # and thus cost, while still recording some information for performance + # monitoring purposes. + backendMetricsRatio: 1.0 + +eppServer: + # The integrated EPP server listens on this port. + port: 30002 + # Health check port used by GCP load balancer. + healthCheckPort: 30000 + # GCS bucket that stores the encrypted PEM file. + sslPemBucket: your-gcs-bucket + # Name of the encrypted PEM file. + sslPemFilename: your-pem-filename + # Cloud KMS details for certificate decryption. + kmsLocation: your-kms-location + kmsKeyRing: your-kms-keyRing + kmsCryptoKey: your-kms-cryptoKey + + # Maximum input message length in bytes. + maxMessageLengthBytes: 1073741824 + # Length of the header field in bytes (RFC 5734). + headerLengthBytes: 4 + # Time after which an idle connection will be closed. + readTimeoutSeconds: 3600 + # Server certificate cache duration. + serverCertificateCacheSeconds: 1800 + + # Quota configuration for EPP + quota: + refreshSeconds: 0 + defaultQuota: + userId: [] + tokenAmount: 100 + refillSeconds: 0 + # To implement a shared quota group across multiple registrars, place a virtual + # group name as the FIRST element of the userId list. + # e.g., userId: ["my_group", "registrar1", "registrar2"] + customQuota: [] + misc: # The ID of the Google Sheet (as found in the URL) to export registrar details # to. Leave this null to disable syncing. diff --git a/core/src/main/java/google/registry/eppserver/EppProtocolModule.java b/core/src/main/java/google/registry/eppserver/EppProtocolModule.java new file mode 100644 index 00000000000..f9274a20e99 --- /dev/null +++ b/core/src/main/java/google/registry/eppserver/EppProtocolModule.java @@ -0,0 +1,163 @@ +// Copyright 2017 The Nomulus Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package google.registry.eppserver; + +import static google.registry.util.ResourceUtils.readResourceBytes; + +import com.google.common.collect.ImmutableList; +import dagger.Module; +import dagger.Provides; +import dagger.multibindings.IntoSet; +import google.registry.config.RegistryConfig.Config; +import google.registry.config.RegistryConfigSettings; +import google.registry.eppserver.Protocol.FrontendProtocol; +import google.registry.eppserver.handler.EppProxyProtocolHandler; +import google.registry.eppserver.handler.EppServiceHandler; +import google.registry.eppserver.quota.QuotaManager; +import google.registry.networking.handler.SslServerInitializer; +import io.netty.channel.ChannelHandler; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.handler.codec.LengthFieldBasedFrameDecoder; +import io.netty.handler.codec.LengthFieldPrepender; +import io.netty.handler.ssl.SslProvider; +import io.netty.handler.timeout.ReadTimeoutHandler; +import jakarta.inject.Named; +import jakarta.inject.Provider; +import jakarta.inject.Qualifier; +import jakarta.inject.Singleton; +import java.io.IOException; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; +import java.util.Optional; +import java.util.function.Supplier; +import redis.clients.jedis.UnifiedJedis; + +/** A module that provides the {@link FrontendProtocol} used for epp protocol. */ +@Module +public final class EppProtocolModule { + + private EppProtocolModule() {} + + @Qualifier + public @interface EppProtocol {} + + @Qualifier + public @interface IpQuota {} + + @Qualifier + public @interface CommandQuota {} + + private static final String PROTOCOL_NAME = "epp"; + + @Singleton + @Provides + @IntoSet + static FrontendProtocol provideProtocol( + @EppProtocol int eppPort, + @EppProtocol ImmutableList> handlerProviders) { + return Protocol.frontendBuilder() + .name(PROTOCOL_NAME) + .port(eppPort) + .handlerProviders(handlerProviders) + .hasBackend(false) + .build(); + } + + @Provides + @EppProtocol + static ImmutableList> provideHandlerProviders( + Provider proxyProtocolHandlerProvider, + @EppProtocol Provider> sslServerInitializerProvider, + @EppProtocol Provider readTimeoutHandlerProvider, + Provider lengthFieldBasedFrameDecoderProvider, + Provider lengthFieldPrependerProvider, + Provider eppServiceHandlerProvider) { + return ImmutableList.of( + proxyProtocolHandlerProvider, + sslServerInitializerProvider, + readTimeoutHandlerProvider, + lengthFieldBasedFrameDecoderProvider, + lengthFieldPrependerProvider, + eppServiceHandlerProvider); + } + + @Provides + static LengthFieldBasedFrameDecoder provideLengthFieldBasedFrameDecoder( + @Config("eppServerMaxMessageLengthBytes") int maxMessageLengthBytes, + @Config("eppServerHeaderLengthBytes") int headerLengthBytes) { + return new LengthFieldBasedFrameDecoder( + maxMessageLengthBytes, 0, headerLengthBytes, -headerLengthBytes, headerLengthBytes); + } + + @Singleton + @Provides + static LengthFieldPrepender provideLengthFieldPrepender( + @Config("eppServerHeaderLengthBytes") int headerLengthBytes) { + return new LengthFieldPrepender(headerLengthBytes, true); + } + + @Provides + @EppProtocol + static ReadTimeoutHandler provideReadTimeoutHandler( + @Config("eppServerReadTimeoutSeconds") int readTimeoutSeconds) { + return new ReadTimeoutHandler(readTimeoutSeconds); + } + + @Singleton + @Provides + @Named("hello") + static byte[] provideHelloBytes() { + try { + return readResourceBytes(EppProtocolModule.class, "hello.xml").read(); + } catch (IOException e) { + throw new RuntimeException("Cannot read EPP message file.", e); + } + } + + @Singleton + @Provides + @EppProtocol + static SslServerInitializer provideSslServerInitializer( + SslProvider sslProvider, + Supplier privateKeySupplier, + Supplier> certificatesSupplier) { + return new SslServerInitializer<>( + true, false, sslProvider, privateKeySupplier, certificatesSupplier); + } + + @Provides + @Singleton + @EppProtocol + static QuotaManager provideConnectionQuotaManager( + @Config("eppServerQuota") RegistryConfigSettings.Quota quota, Optional jedis) { + return new QuotaManager(quota, jedis.orElse(null), "connection"); + } + + @Provides + @Singleton + @CommandQuota + static QuotaManager provideCommandQuotaManager( + @Config("eppServerQuota") RegistryConfigSettings.Quota quota, Optional jedis) { + return new QuotaManager(quota, jedis.orElse(null), "command"); + } + + @Provides + @Singleton + @IpQuota + static QuotaManager provideIpQuotaManager( + @Config("eppServerQuota") RegistryConfigSettings.Quota quota, Optional jedis) { + return new QuotaManager(quota, jedis.orElse(null), "ip"); + } +} diff --git a/core/src/main/java/google/registry/eppserver/EppServer.java b/core/src/main/java/google/registry/eppserver/EppServer.java new file mode 100644 index 00000000000..b0cd414abef --- /dev/null +++ b/core/src/main/java/google/registry/eppserver/EppServer.java @@ -0,0 +1,126 @@ +// Copyright 2024 The Nomulus Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package google.registry.eppserver; + +import static google.registry.eppserver.Protocol.PROTOCOL_KEY; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.flogger.FluentLogger; +import google.registry.eppserver.EppServerModule.EppServerComponent; +import google.registry.eppserver.Protocol.FrontendProtocol; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.util.concurrent.DefaultEventExecutorGroup; +import io.netty.util.concurrent.EventExecutorGroup; +import io.netty.util.concurrent.Future; +import jakarta.inject.Provider; +import java.util.HashMap; + +/** An integrated EPP server that listens for EPP traffic and processes it in-process. */ +public class EppServer { + private static final FluentLogger logger = FluentLogger.forEnclosingClass(); + + /** Maximum length of the queue of incoming connections. */ + private static final int MAX_SOCKET_BACKLOG = 128; + + private final ImmutableSet protocols; + private final HashMap portToChannelMap = new HashMap<>(); + private final EventLoopGroup eventGroup = new NioEventLoopGroup(); + private final EventExecutorGroup businessGroup = + new DefaultEventExecutorGroup(Math.max(4, Runtime.getRuntime().availableProcessors() * 4)); + + public EppServer(EppServerComponent eppServerComponent) { + this.protocols = ImmutableSet.copyOf(eppServerComponent.protocols()); + } + + private class ServerChannelInitializer extends ChannelInitializer { + @Override + protected void initChannel(NioSocketChannel inboundChannel) { + FrontendProtocol inboundProtocol = + (FrontendProtocol) inboundChannel.parent().attr(PROTOCOL_KEY).get(); + inboundChannel.attr(PROTOCOL_KEY).set(inboundProtocol); + + addHandlers(inboundChannel.pipeline(), inboundProtocol.handlerProviders()); + + // Start reading immediately since we don't have a backend relay. + inboundChannel.config().setAutoRead(true); + + logger.atInfo().log("Connection established: %s %s", inboundProtocol.name(), inboundChannel); + } + + private void addHandlers( + ChannelPipeline channelPipeline, + ImmutableList> handlerProviders) { + for (Provider handlerProvider : handlerProviders) { + ChannelHandler handler = handlerProvider.get(); + if (handler.getClass().getSimpleName().equals("EppServiceHandler")) { + channelPipeline.addLast(businessGroup, handler); + } else { + channelPipeline.addLast(handler); + } + } + } + } + + public void start() { + ServerBootstrap serverBootstrap = + new ServerBootstrap() + .group(eventGroup) + .channel(NioServerSocketChannel.class) + .childHandler(new ServerChannelInitializer()) + .option(ChannelOption.SO_BACKLOG, MAX_SOCKET_BACKLOG) + .childOption(ChannelOption.SO_KEEPALIVE, true); + + protocols.forEach( + protocol -> { + int port = protocol.port(); + try { + ChannelFuture serverChannelFuture = serverBootstrap.bind(port).sync(); + if (serverChannelFuture.isSuccess()) { + logger.atInfo().log( + "Start listening on port %s for %s protocol.", port, protocol.name()); + Channel serverChannel = serverChannelFuture.channel(); + serverChannel.attr(PROTOCOL_KEY).set(protocol); + portToChannelMap.put(port, serverChannel); + } + } catch (InterruptedException e) { + logger.atSevere().withCause(e).log( + "Cannot listen on port %d for %s protocol.", port, protocol.name()); + } + }); + } + + public void stop() { + logger.atInfo().log("Shutting down EPP server..."); + portToChannelMap + .values() + .forEach( + channel -> { + Future unusedFuture = channel.close(); + }); + Future unusedFuture = eventGroup.shutdownGracefully(); + Future unusedFutureBusiness = businessGroup.shutdownGracefully(); + } +} diff --git a/core/src/main/java/google/registry/eppserver/EppServerModule.java b/core/src/main/java/google/registry/eppserver/EppServerModule.java new file mode 100644 index 00000000000..b59e2d7f806 --- /dev/null +++ b/core/src/main/java/google/registry/eppserver/EppServerModule.java @@ -0,0 +1,259 @@ +// Copyright 2017 The Nomulus Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package google.registry.eppserver; + +import com.google.api.services.cloudkms.v1.CloudKMS; +import com.google.api.services.cloudkms.v1.model.DecryptRequest; +import com.google.auth.oauth2.GoogleCredentials; +import com.google.cloud.http.HttpTransportOptions; +import com.google.cloud.storage.BlobId; +import com.google.cloud.storage.Storage; +import com.google.cloud.storage.StorageException; +import com.google.cloud.storage.StorageOptions; +import com.google.common.base.Suppliers; +import com.google.monitoring.metrics.MetricReporter; +import dagger.Component; +import dagger.Module; +import dagger.Provides; +import google.registry.cache.CacheModule; +import google.registry.config.CredentialModule; +import google.registry.config.CredentialModule.ApplicationDefaultCredential; +import google.registry.config.RegistryConfig.Config; +import google.registry.config.RegistryConfig.ConfigModule; +import google.registry.eppserver.EppProtocolModule.EppProtocol; +import google.registry.eppserver.HealthCheckProtocolModule.HealthCheckProtocol; +import google.registry.eppserver.Protocol.FrontendProtocol; +import google.registry.keyring.KeyringModule; +import google.registry.keyring.api.KeyModule; +import google.registry.networking.module.CertificateSupplierModule; +import google.registry.networking.module.CertificateSupplierModule.Mode; +import google.registry.privileges.secretmanager.SecretManagerModule; +import google.registry.util.GoogleCredentialsBundle; +import google.registry.util.OidcTokenUtils; +import google.registry.util.RegistryEnvironment; +import google.registry.util.UtilsModule; +import io.netty.handler.logging.LogLevel; +import io.netty.handler.logging.LoggingHandler; +import io.netty.handler.ssl.OpenSsl; +import io.netty.handler.ssl.SslProvider; +import jakarta.inject.Named; +import jakarta.inject.Singleton; +import java.io.IOException; +import java.time.Duration; +import java.util.Base64; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; +import redis.clients.jedis.UnifiedJedis; + +/** + * A module that provides the port-to-protocol map and other configs that are used to bootstrap the + * server. + */ +@Module +public class EppServerModule { + + @Provides + @EppProtocol + static int provideEppPort(@Config("eppServerPort") int eppPort) { + return eppPort; + } + + @Provides + @HealthCheckProtocol + static int provideHealthCheckPort(@Config("eppServerHealthCheckPort") int healthCheckPort) { + return healthCheckPort; + } + + @Singleton + @Provides + LoggingHandler provideLoggingHandler() { + return new LoggingHandler(LogLevel.DEBUG); + } + + @Singleton + @Provides + static Supplier provideRefreshedCredentialsSupplier( + @ApplicationDefaultCredential GoogleCredentialsBundle credentialsBundle) { + return () -> { + GoogleCredentials credentials = credentialsBundle.getGoogleCredentials(); + try { + credentials.refreshIfExpired(); + } catch (IOException e) { + throw new RuntimeException("Cannot refresh credentials.", e); + } + return credentials; + }; + } + + @Singleton + @Provides + @Named("idToken") + static Supplier provideOidcToken( + @ApplicationDefaultCredential GoogleCredentialsBundle credentialsBundle, + @Config("oauthClientId") String clientId) { + return Suppliers.memoizeWithExpiration( + () -> OidcTokenUtils.createOidcToken(credentialsBundle, clientId), 1, TimeUnit.HOURS); + } + + @Singleton + @Provides + @Named("canary") + static boolean provideIsCanary() { + return RegistryEnvironment.get().name().endsWith("_CANARY"); + } + + @Singleton + @Provides + static CloudKMS provideCloudKms( + @ApplicationDefaultCredential GoogleCredentialsBundle credentialsBundle, + @Config("projectId") String projectId) { + return new CloudKMS.Builder( + credentialsBundle.getHttpTransport(), + credentialsBundle.getJsonFactory(), + credentialsBundle.getHttpRequestInitializer()) + .setApplicationName(projectId) + .build(); + } + + @Singleton + @Provides + static Storage provideStorage( + @ApplicationDefaultCredential GoogleCredentialsBundle credentialsBundle) { + return StorageOptions.newBuilder() + .setTransportOptions( + HttpTransportOptions.newBuilder() + .setHttpTransportFactory(credentialsBundle::getHttpTransport) + .build()) + .setCredentials(credentialsBundle.getGoogleCredentials()) + .build() + .getService(); + } + + @Provides + @Named("encryptedPemBytes") + static byte[] provideEncryptedPemBytes( + Storage storage, + @Config("eppServerSslPemBucket") String bucket, + @Config("eppServerSslPemFilename") String sslPemFilename) { + try { + return Base64.getMimeDecoder() + .decode(storage.readAllBytes(BlobId.of(bucket, sslPemFilename))); + } catch (StorageException e) { + throw new RuntimeException( + String.format( + "Error reading encrypted PEM file %s from GCS bucket %s", sslPemFilename, bucket), + e); + } + } + + @Provides + @Named("pemBytes") + static byte[] providePemBytes( + CloudKMS cloudKms, + @Named("encryptedPemBytes") byte[] encryptedPemBytes, + @Config("projectId") String projectId, + @Config("eppServerKmsLocation") String location, + @Config("eppServerKmsKeyRing") String keyRing, + @Config("eppServerKmsCryptoKey") String cryptoKey) { + String cryptoKeyUrl = + String.format( + "projects/%s/locations/%s/keyRings/%s/cryptoKeys/%s", + projectId, location, keyRing, cryptoKey); + try { + DecryptRequest decryptRequest = new DecryptRequest().encodeCiphertext(encryptedPemBytes); + return cloudKms + .projects() + .locations() + .keyRings() + .cryptoKeys() + .decrypt(cryptoKeyUrl, decryptRequest) + .execute() + .decodePlaintext(); + } catch (IOException e) { + throw new RuntimeException( + String.format("PEM file decryption failed using CryptoKey: %s", cryptoKeyUrl), e); + } + } + + @Provides + static SslProvider provideSslProvider() { + return OpenSsl.isAvailable() ? SslProvider.OPENSSL : SslProvider.JDK; + } + + @Provides + static ExecutorService provideExecutorService() { + return Executors.newWorkStealingPool(); + } + + @Provides + static ScheduledExecutorService provideScheduledExecutorService() { + return Executors.newSingleThreadScheduledExecutor(); + } + + @Singleton + @Provides + static Mode provideMode() { + if (RegistryEnvironment.get() == RegistryEnvironment.LOCAL) { + return Mode.SELF_SIGNED; + } else { + return Mode.PEM_FILE; + } + } + + @Singleton + @Provides + @Named("remoteCertCachingDuration") + static Duration provideCertCachingDuration( + @Config("eppServerCertificateCacheSeconds") int cacheSeconds) { + return Duration.ofSeconds(cacheSeconds); + } + + @Singleton + @Provides + @Named("frontendMetricsRatio") + static double provideFrontendMetricsRatio(@Config("eppServerFrontendMetricsRatio") double ratio) { + return ratio; + } + + /** Root level component that exposes the port-to-protocol map. */ + @Singleton + @Component( + modules = { + EppServerModule.class, + CacheModule.class, + CertificateSupplierModule.class, + ConfigModule.class, + EppProtocolModule.class, + HealthCheckProtocolModule.class, + MetricsModule.class, + CredentialModule.class, + KeyModule.class, + KeyringModule.class, + SecretManagerModule.class, + UtilsModule.class + }) + public interface EppServerComponent { + Set protocols(); + + MetricReporter metricReporter(); + + Optional jedis(); + } +} diff --git a/core/src/main/java/google/registry/eppserver/HealthCheckProtocolModule.java b/core/src/main/java/google/registry/eppserver/HealthCheckProtocolModule.java new file mode 100644 index 00000000000..fc4204ab1f9 --- /dev/null +++ b/core/src/main/java/google/registry/eppserver/HealthCheckProtocolModule.java @@ -0,0 +1,66 @@ +// Copyright 2017 The Nomulus Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package google.registry.eppserver; + +import com.google.common.collect.ImmutableList; +import dagger.Module; +import dagger.Provides; +import dagger.multibindings.IntoSet; +import google.registry.eppserver.Protocol.FrontendProtocol; +import google.registry.eppserver.handler.HealthCheckHandler; +import io.netty.channel.ChannelHandler; +import jakarta.inject.Provider; +import jakarta.inject.Qualifier; +import jakarta.inject.Singleton; + +/** A module that provides the {@link FrontendProtocol} used for health check protocol. */ +@Module +public final class HealthCheckProtocolModule { + + /** Dagger qualifier to provide health check protocol related handlers and other bindings. */ + @Qualifier + public @interface HealthCheckProtocol {} + + private static final String PROTOCOL_NAME = "health_check"; + + @Singleton + @Provides + @IntoSet + static FrontendProtocol provideProtocol( + @HealthCheckProtocol int healthCheckPort, + @HealthCheckProtocol ImmutableList> handlerProviders) { + return Protocol.frontendBuilder() + .name(PROTOCOL_NAME) + .port(healthCheckPort) + .handlerProviders(handlerProviders) + .hasBackend(false) + .build(); + } + + @Provides + @HealthCheckProtocol + static ImmutableList> provideHandlerProviders( + @HealthCheckProtocol Provider healthCheckHandlerProvider) { + return ImmutableList.of(healthCheckHandlerProvider); + } + + @Provides + @HealthCheckProtocol + static HealthCheckHandler provideHealthCheckHandler() { + // These are currently hardcoded in the handler or defaulted in RegistryConfig. + // For now, we use the standard GCP health check strings. + return new HealthCheckHandler("HEALTH_CHECK_REQUEST", "HEALTH_CHECK_RESPONSE"); + } +} diff --git a/core/src/main/java/google/registry/eppserver/MetricsModule.java b/core/src/main/java/google/registry/eppserver/MetricsModule.java new file mode 100644 index 00000000000..e5bada6baf9 --- /dev/null +++ b/core/src/main/java/google/registry/eppserver/MetricsModule.java @@ -0,0 +1,115 @@ +// Copyright 2017 The Nomulus Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package google.registry.eppserver; + +import com.google.api.services.monitoring.v3.Monitoring; +import com.google.api.services.monitoring.v3.model.MonitoredResource; +import com.google.common.collect.ImmutableMap; +import com.google.common.flogger.FluentLogger; +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import com.google.monitoring.metrics.MetricReporter; +import com.google.monitoring.metrics.MetricWriter; +import com.google.monitoring.metrics.stackdriver.StackdriverWriter; +import dagger.Component; +import dagger.Module; +import dagger.Provides; +import google.registry.config.CredentialModule; +import google.registry.config.CredentialModule.ApplicationDefaultCredential; +import google.registry.config.RegistryConfig.Config; +import google.registry.config.RegistryConfig.ConfigModule; +import google.registry.util.GoogleCredentialsBundle; +import google.registry.util.MetricParameters; +import google.registry.util.RegistryEnvironment; +import jakarta.inject.Singleton; + +/** Module that provides necessary bindings to instantiate a {@link MetricReporter} */ +@Module +public class MetricsModule { + + private static final FluentLogger logger = FluentLogger.forEnclosingClass(); + + @Singleton + @Provides + static Monitoring provideMonitoring( + @ApplicationDefaultCredential GoogleCredentialsBundle credentialsBundle, + @Config("projectId") String projectId) { + return new Monitoring.Builder( + credentialsBundle.getHttpTransport(), + credentialsBundle.getJsonFactory(), + credentialsBundle.getHttpRequestInitializer()) + .setApplicationName(projectId) + .build(); + } + + @Singleton + @Provides + static MetricWriter provideMetricWriter( + Monitoring monitoringClient, + MonitoredResource monitoredResource, + @Config("projectId") String projectId, + @Config("stackdriverMaxQps") int stackdriverMaxQps, + @Config("stackdriverMaxPointsPerRequest") int stackdriverMaxPointsPerRequest) { + return new StackdriverWriter( + monitoringClient, + projectId, + monitoredResource, + stackdriverMaxQps, + stackdriverMaxPointsPerRequest); + } + + @Singleton + @Provides + static MetricReporter provideMetricReporter( + MetricWriter metricWriter, @Config("writeIntervalSeconds") int writeIntervalSeconds) { + return new MetricReporter( + metricWriter, writeIntervalSeconds, new ThreadFactoryBuilder().setDaemon(true).build()); + } + + /** + * Provides a {@link MonitoredResource} appropriate for environment the EPP server runs in. + * + *

When running locally, the type of the monitored resource is set to {@code global}, otherwise + * it is {@code gke_container}. + * + * @see + * Choosing a monitored resource type + */ + @Singleton + @Provides + static MonitoredResource provideMonitoredResource( + @Config("projectId") String projectId, MetricParameters metricParameters) { + MonitoredResource monitoredResource = new MonitoredResource(); + if (RegistryEnvironment.get() == RegistryEnvironment.LOCAL) { + monitoredResource.setType("global").setLabels(ImmutableMap.of("project_id", projectId)); + } else { + monitoredResource.setType("gke_container").setLabels(metricParameters.makeLabelsMap()); + } + logger.atInfo().log("Monitored resource: %s", monitoredResource); + return monitoredResource; + } + + @Singleton + @Component( + modules = { + MetricsModule.class, + EppServerModule.class, + CredentialModule.class, + ConfigModule.class + }) + interface MetricsComponent { + MetricReporter metricReporter(); + } +} diff --git a/core/src/main/java/google/registry/eppserver/Protocol.java b/core/src/main/java/google/registry/eppserver/Protocol.java new file mode 100644 index 00000000000..722b0b941cc --- /dev/null +++ b/core/src/main/java/google/registry/eppserver/Protocol.java @@ -0,0 +1,147 @@ +// Copyright 2017 The Nomulus Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package google.registry.eppserver; + +import com.google.auto.value.AutoValue; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandler; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.util.Attribute; +import io.netty.util.AttributeKey; +import jakarta.inject.Provider; +import javax.annotation.Nullable; + +/** Value class that encapsulates parameters of a specific connection. */ +public interface Protocol { + + /** Key used to retrieve the {@link Protocol} from a {@link Channel}'s {@link Attribute}. */ + AttributeKey PROTOCOL_KEY = AttributeKey.valueOf("PROTOCOL_KEY"); + + /** Protocol name. */ + String name(); + + /** + * Port to bind to (for {@link FrontendProtocol}) or to connect to (for {@link BackendProtocol}). + */ + int port(); + + /** The {@link ChannelHandler} providers to use for the protocol, in order. */ + ImmutableList> handlerProviders(); + + /** A builder for {@link FrontendProtocol}, by default there is a backend associated with it. */ + static FrontendProtocol.Builder frontendBuilder() { + return new AutoValue_Protocol_FrontendProtocol.Builder().hasBackend(true); + } + + /** A builder for {@link FrontendProtocol}, by default it connects to a remote host. */ + static BackendProtocol.Builder backendBuilder() { + return new AutoValue_Protocol_BackendProtocol.Builder().isLocal(false); + } + + /** + * Generic builder enabling chaining for concrete implementations. + * + * @param builder of the concrete subtype of {@link Protocol}. + * @param

type of the concrete subtype of {@link Protocol}. + */ + abstract class Builder, P extends Protocol> { + + public abstract B name(String value); + + public abstract B port(int port); + + public abstract B handlerProviders(ImmutableList> value); + + public abstract P build(); + } + + /** + * Connection parameters for a connection from the client to the proxy. + * + *

This protocol is associated to a {@link NioSocketChannel} established by remote peer + * connecting to the given {@code port} that the proxy is listening on. + */ + @AutoValue + abstract class FrontendProtocol implements Protocol { + + /** + * The {@link BackendProtocol} used to establish a relay channel and relay the traffic to. Not + * required for health check protocol or HTTP(S) redirect. + */ + @Nullable + public abstract BackendProtocol relayProtocol(); + + /** + * Whether this {@code FrontendProtocol} relays to a {@code BackendProtocol}. All proxied + * traffic must be represented by a protocol that has a backend. + */ + public abstract boolean hasBackend(); + + @AutoValue.Builder + public abstract static class Builder extends Protocol.Builder { + public abstract Builder relayProtocol(BackendProtocol value); + + public abstract Builder hasBackend(boolean value); + + abstract FrontendProtocol autoBuild(); + + @Override + public FrontendProtocol build() { + FrontendProtocol frontendProtocol = autoBuild(); + Preconditions.checkState( + !frontendProtocol.hasBackend() || frontendProtocol.relayProtocol() != null, + "Frontend protocol %s must define a relay protocol.", + frontendProtocol.name()); + return frontendProtocol; + } + } + } + + /** + * Connection parameters for a connection from the EPP server to Nomulus. + * + *

This protocol is associated to a {@link NioSocketChannel} established by the EPP server + * connecting to a remote peer. + */ + @AutoValue + abstract class BackendProtocol implements Protocol { + /** The hostname that the EPP server connects to. */ + public abstract String host(); + + /** Whether the protocol is expected to connect to localhost. */ + public abstract boolean isLocal(); + + /** Builder of {@link BackendProtocol}. */ + @AutoValue.Builder + public abstract static class Builder extends Protocol.Builder { + public abstract Builder host(String value); + + public abstract Builder isLocal(boolean value); + + abstract BackendProtocol autoBuild(); + + @Override + public BackendProtocol build() { + BackendProtocol protocol = autoBuild(); + Preconditions.checkState( + !protocol.isLocal() || protocol.host().equals("localhost"), + "Local backend protocol must connect to localhost"); + return autoBuild(); + } + } + } +} diff --git a/core/src/main/java/google/registry/eppserver/handler/EppProxyProtocolHandler.java b/core/src/main/java/google/registry/eppserver/handler/EppProxyProtocolHandler.java new file mode 100644 index 00000000000..60c88f8b0c8 --- /dev/null +++ b/core/src/main/java/google/registry/eppserver/handler/EppProxyProtocolHandler.java @@ -0,0 +1,199 @@ +// Copyright 2017 The Nomulus Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package google.registry.eppserver.handler; + +import static com.google.common.base.Preconditions.checkState; +import static java.nio.charset.StandardCharsets.US_ASCII; + +import com.google.common.flogger.FluentLogger; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.ByteToMessageDecoder; +import io.netty.util.AttributeKey; +import jakarta.inject.Inject; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.util.List; + +/** + * Handler that processes possible existence of a PROXY protocol v1 header. + * + *

When an EPP client connects to the registry (through the proxy), the registry performs two + * validations to ensure that only known registrars are allowed. First it checks the sha265 hash of + * the client SSL certificate and match it to the hash stored in the database for the registrar. It + * then checks if the connection is from an allow-listed IP address that belongs to that registrar. + * + *

The proxy receives client connects via the GCP load balancer, which results in the loss of + * original client IP from the channel. Luckily, the load balancer supports the PROXY protocol v1, + * which adds a header with source IP information, among other things, to the TCP request at the + * start of the connection. + * + *

This handler determines if a connection is proxied (PROXY protocol v1 header present) and + * correctly sets the source IP address to the channel's attribute regardless of whether it is + * proxied. After that it removes itself from the channel pipeline because the proxy header is only + * present at the beginning of the connection. + * + *

This handler must be the very first handler in a protocol, even before SSL handlers, because + * PROXY protocol header comes as the very first thing, even before SSL handshake request. + * + * @see The PROXY protocol + */ +public class EppProxyProtocolHandler extends ByteToMessageDecoder { + + /** Key used to retrieve origin IP address from a channel's attribute. */ + public static final AttributeKey REMOTE_ADDRESS_KEY = + AttributeKey.valueOf("REMOTE_ADDRESS_KEY"); + + private static final FluentLogger logger = FluentLogger.forEnclosingClass(); + + // The proxy header must start with this prefix. + // Sample header: "PROXY TCP4 255.255.255.255 255.255.255.255 65535 65535\r\n". + private static final byte[] HEADER_PREFIX = "PROXY".getBytes(US_ASCII); + + private boolean finished = false; + private String proxyHeader = null; + + @Inject + EppProxyProtocolHandler() {} + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + super.channelRead(ctx, msg); + if (finished) { + String remoteIP; + if (proxyHeader != null) { + logger.atFine().log("PROXIED CONNECTION: %s", ctx.channel()); + logger.atFine().log("PROXY HEADER for channel %s: %s", ctx.channel(), proxyHeader); + String[] headerArray = proxyHeader.split(" ", -1); + if (headerArray.length == 6) { + remoteIP = headerArray[2]; + logger.atFine().log( + "Header parsed, using %s as remote IP for channel %s", remoteIP, ctx.channel()); + // If the header is "PROXY UNKNOWN" + // (see https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt), likely when the + // remote connection to the external load balancer is through special means, make it + // 0.0.0.0 so that it can be treated accordingly by the relevant quota configs. + } else if (headerArray.length == 2 && headerArray[1].equals("UNKNOWN")) { + logger.atFine().log( + "Header parsed, source IP unknown, using 0.0.0.0 as remote IP for channel %s", + ctx.channel()); + remoteIP = "0.0.0.0"; + } else { + logger.atFine().log( + "Cannot parse the header, using source IP as remote IP for channel %s", + ctx.channel()); + remoteIP = getSourceIP(ctx); + } + } else { + logger.atFine().log( + "No header present, using source IP directly for channel %s", ctx.channel()); + remoteIP = getSourceIP(ctx); + } + if (remoteIP != null) { + ctx.channel().attr(REMOTE_ADDRESS_KEY).set(remoteIP); + } else { + logger.atWarning().log("Not able to obtain remote IP for channel %s", ctx.channel()); + } + // ByteToMessageDecoder automatically flushes unread bytes in the ByteBuf to the next handler + // when itself is being removed. + ctx.pipeline().remove(this); + } + } + + private static String getSourceIP(ChannelHandlerContext ctx) { + SocketAddress remoteAddress = ctx.channel().remoteAddress(); + return (remoteAddress instanceof InetSocketAddress inetSocketAddress) + ? inetSocketAddress.getAddress().getHostAddress() + : null; + } + + /** + * Attempts to decode an internally accumulated buffer and find the proxy protocol header. + * + *

When the connection is not proxied (i. e. the initial bytes are not "PROXY"), simply set + * {@link #finished} to true and allow the handler to be removed. Otherwise the handler waits + * until there's enough bytes to parse the header, save the parsed header to {@link #proxyHeader}, + * and then mark {@link #finished}. + * + * @param in internally accumulated buffer, newly arrived bytes are appended to it. + * @param out objects passed to the next handler, in this case nothing is ever passed because the + * header itself is processed and written to the attribute of the proxy, and the handler is + * then removed from the pipeline. + */ + @Override + protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) { + // Wait until there are more bytes available than the header's length before processing. + if (in.readableBytes() >= HEADER_PREFIX.length) { + if (containsHeader(in)) { + // The inbound message contains the header, it must be a proxied connection. Note that + // currently proxied connection is only used for EPP protocol, which requires the connection + // to be SSL enabled. So the beginning of the inbound message upon connection can only be + // either the proxy header (when proxied), or SSL handshake request (when not proxied), + // which does not start with "PROXY". Therefore it is safe to assume that if the beginning + // of the message contains "PROXY", it must be proxied, and must contain \r\n. + int eol = findEndOfLine(in); + // If eol is not found, that is because that we do not yet have enough inbound message, do + // nothing and wait for more bytes to be readable. eol will eventually be positive because + // of the reasoning above: The connection starts with "PROXY", so it must be a proxied + // connection and contain \r\n. + if (eol >= 0) { + // ByteBuf.readBytes is called so that the header is processed and not passed to handlers + // further in the pipeline. + byte[] headerBytes = new byte[eol]; + in.readBytes(headerBytes); + proxyHeader = new String(headerBytes, US_ASCII); + // Skip \r\n. + in.skipBytes(2); + // Proxy header processed, mark finished so that this handler is removed. + finished = true; + } + } else { + // The inbound message does not contain a proxy header, mark finished so that this handler + // is removed. Note that no inbound bytes are actually processed by this handler because we + // did not call ByteBuf.readBytes(), but ByteBuf.getByte(), which does not change reader + // index of the ByteBuf. So any inbound byte is then passed to the next handler to process. + finished = true; + } + } + } + + /** + * Returns the index in the buffer of the end of line found. Returns -1 if no end of line was + * found in the buffer. + */ + private static int findEndOfLine(final ByteBuf buffer) { + final int n = buffer.writerIndex(); + for (int i = buffer.readerIndex(); i < n; i++) { + final byte b = buffer.getByte(i); + if (b == '\r' && i < n - 1 && buffer.getByte(i + 1) == '\n') { + return i; // \r\n + } + } + return -1; // Not found. + } + + /** Checks if the given buffer contains the proxy header prefix. */ + private boolean containsHeader(ByteBuf buffer) { + // The readable bytes is always more or equal to the size of the header prefix because this + // method is only called when this condition is true. + checkState(buffer.readableBytes() >= HEADER_PREFIX.length); + for (int i = 0; i < HEADER_PREFIX.length; ++i) { + if (buffer.getByte(buffer.readerIndex() + i) != HEADER_PREFIX[i]) { + return false; + } + } + return true; + } +} diff --git a/core/src/main/java/google/registry/eppserver/handler/EppServiceHandler.java b/core/src/main/java/google/registry/eppserver/handler/EppServiceHandler.java new file mode 100644 index 00000000000..9ba28b0bfb7 --- /dev/null +++ b/core/src/main/java/google/registry/eppserver/handler/EppServiceHandler.java @@ -0,0 +1,236 @@ +// Copyright 2024 The Nomulus Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package google.registry.eppserver.handler; + +import static google.registry.eppserver.handler.EppProxyProtocolHandler.REMOTE_ADDRESS_KEY; +import static google.registry.networking.handler.SslServerInitializer.CLIENT_CERTIFICATE_PROMISE_KEY; +import static google.registry.util.GcpJsonFormatter.setCurrentRequest; +import static google.registry.util.GcpJsonFormatter.setCurrentTraceId; +import static google.registry.util.GcpJsonFormatter.unsetCurrentRequest; +import static google.registry.util.X509Utils.getCertificateHash; +import static java.nio.charset.StandardCharsets.UTF_8; + +import com.google.common.flogger.FluentLogger; +import google.registry.config.RegistryConfig.Config; +import google.registry.eppserver.EppProtocolModule.CommandQuota; +import google.registry.eppserver.EppProtocolModule.EppProtocol; +import google.registry.eppserver.EppProtocolModule.IpQuota; +import google.registry.eppserver.metric.FrontendMetrics; +import google.registry.eppserver.quota.QuotaManager; +import google.registry.module.RegistryServlet; +import google.registry.util.FakeHttpServletRequest; +import google.registry.util.FakeHttpServletResponse; +import google.registry.util.ProxyHttpHeaders; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.util.AttributeKey; +import io.netty.util.concurrent.Future; +import io.netty.util.concurrent.Promise; +import jakarta.inject.Inject; +import jakarta.inject.Named; +import java.security.cert.X509Certificate; +import java.util.UUID; +import java.util.function.Supplier; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Unified processor for EPP protocol traffic. + * + *

Consolidates throttling, session management, and in-process execution. Extracts registrar ID + * (clID) directly from EPP login XML for accurate throttling. + */ +public class EppServiceHandler extends SimpleChannelInboundHandler { + + private static final FluentLogger logger = FluentLogger.forEnclosingClass(); + + // Fast regex to extract clID from EPP login command without full XML parsing. + private static final Pattern CLID_PATTERN = + Pattern.compile("([^<]+)", Pattern.CASE_INSENSITIVE); + + public static final AttributeKey CLIENT_CERTIFICATE_HASH_KEY = + AttributeKey.valueOf("CLIENT_CERTIFICATE_HASH_KEY"); + + private final byte[] helloBytes; + private final FrontendMetrics metrics; + private final QuotaManager connectionQuotaManager; + private final QuotaManager commandQuotaManager; + private final QuotaManager ipQuotaManager; + private final Supplier idTokenSupplier; + private final String projectId; + + private String sslClientCertificateHash; + private String clientAddress; + private String registrarId; // The clID extracted from login + private String sessionCookie; + + private boolean ipQuotaAcquired = false; + private boolean connectionQuotaAcquired = false; + + @Inject + public EppServiceHandler( + @Named("hello") byte[] helloBytes, + FrontendMetrics metrics, + @EppProtocol QuotaManager connectionQuotaManager, + @CommandQuota QuotaManager commandQuotaManager, + @IpQuota QuotaManager ipQuotaManager, + @Named("idToken") Supplier idTokenSupplier, + @Config("projectId") String projectId) { + this.helloBytes = helloBytes.clone(); + this.metrics = metrics; + this.connectionQuotaManager = connectionQuotaManager; + this.commandQuotaManager = commandQuotaManager; + this.ipQuotaManager = ipQuotaManager; + this.idTokenSupplier = idTokenSupplier; + this.projectId = projectId; + } + + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + Promise certPromise = ctx.channel().attr(CLIENT_CERTIFICATE_PROMISE_KEY).get(); + if (certPromise != null) { + certPromise.addListener( + (Promise promise) -> { + if (promise.isSuccess()) { + onSslHandshakeComplete(ctx, promise.get()); + } else { + logger.atWarning().withCause(promise.cause()).log("SSL handshake failed"); + Future unusedFuture = ctx.close(); + } + }); + } + super.channelActive(ctx); + } + + private void onSslHandshakeComplete(ChannelHandlerContext ctx, X509Certificate cert) { + sslClientCertificateHash = getCertificateHash(cert); + clientAddress = ctx.channel().attr(REMOTE_ADDRESS_KEY).get(); + ctx.channel().attr(CLIENT_CERTIFICATE_HASH_KEY).set(sslClientCertificateHash); + + // 1. Connection throttling (IP and Certificate) + // We use the certificate hash for connection limits because clID isn't known yet. + if (!ipQuotaManager.acquireQuota(new QuotaManager.QuotaRequest(clientAddress)).success()) { + metrics.registerQuotaRejection("epp_connection_ip", clientAddress); + Future unusedFuture = ctx.close(); + return; + } + ipQuotaAcquired = true; + + if (!connectionQuotaManager + .acquireQuota(new QuotaManager.QuotaRequest(sslClientCertificateHash)) + .success()) { + metrics.registerQuotaRejection("epp_connection", sslClientCertificateHash); + Future unusedFuture = ctx.close(); + return; + } + connectionQuotaAcquired = true; + + metrics.registerActiveConnection("epp", sslClientCertificateHash, ctx.channel()); + + // 2. Trigger initial EPP + handleEppFrame(ctx, Unpooled.wrappedBuffer(helloBytes)); + } + + @Override + protected void channelRead0(ChannelHandlerContext ctx, ByteBuf frame) { + handleEppFrame(ctx, frame); + } + + private void handleEppFrame(ChannelHandlerContext ctx, ByteBuf frame) { + if (ipQuotaAcquired) { + ipQuotaManager.refreshQuota(new QuotaManager.QuotaRequest(clientAddress)); + } + if (connectionQuotaAcquired) { + connectionQuotaManager.refreshQuota(new QuotaManager.QuotaRequest(sslClientCertificateHash)); + } + + String xml = frame.toString(UTF_8); + + // 1. Maturing Identity: If we don't have clID yet, try to extract it from a login command. + if (registrarId == null) { + Matcher matcher = CLID_PATTERN.matcher(xml); + if (matcher.find()) { + registrarId = matcher.group(1).trim(); + logger.atInfo().log("Identified registrar: %s", registrarId); + } + } + + // 2. Command-level rate limiting + // Use clID if identified, otherwise fallback to cert hash (for the login command itself). + String throttleId = (registrarId != null) ? registrarId : sslClientCertificateHash; + if (throttleId != null) { + if (!commandQuotaManager.acquireQuota(new QuotaManager.QuotaRequest(throttleId)).success()) { + metrics.registerQuotaRejection("epp_command", throttleId); + Future unusedFuture = ctx.close(); + return; + } + } + + // 3. Execute command in-process + FakeHttpServletRequest req = new FakeHttpServletRequest(); + req.setRequestUri("/_dr/epp"); + req.setBody(xml.getBytes(UTF_8)); + req.setHeader(ProxyHttpHeaders.CERTIFICATE_HASH, sslClientCertificateHash); + req.setHeader(ProxyHttpHeaders.IP_ADDRESS, clientAddress); + if (registrarId != null) { + req.setHeader(ProxyHttpHeaders.REGISTRAR_ID, registrarId); + } + if (sessionCookie != null) { + req.setHeader("Cookie", sessionCookie); + } + req.setHeader("Authorization", "Bearer " + idTokenSupplier.get()); + + FakeHttpServletResponse rsp = new FakeHttpServletResponse(); + String traceId = + String.format( + "projects/%s/traces/%s", projectId, UUID.randomUUID().toString().replace("-", "")); + setCurrentTraceId(traceId); + setCurrentRequest("POST", "/_dr/epp", "Netty-EPP", "EPP/1.0"); + try { + RegistryServlet.component.requestHandler().handleRequest(req, rsp); + String setCookie = rsp.getHeader("Set-Cookie"); + if (setCookie != null) { + sessionCookie = setCookie; + } + + ByteBuf out = Unpooled.wrappedBuffer(rsp.getPayload()); + if ("close".equals(rsp.getHeader(ProxyHttpHeaders.EPP_SESSION))) { + Future unusedFuture = ctx.writeAndFlush(out).addListener(ChannelFutureListener.CLOSE); + } else { + Future unusedFuture = ctx.writeAndFlush(out); + } + } catch (Exception e) { + logger.atSevere().withCause(e).log("Internal EPP processing error"); + Future unusedFuture = ctx.close(); + } finally { + setCurrentTraceId(null); + unsetCurrentRequest(); + } + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + if (connectionQuotaAcquired) { + connectionQuotaManager.releaseQuota(new QuotaManager.QuotaRebate(sslClientCertificateHash)); + } + if (ipQuotaAcquired) { + ipQuotaManager.releaseQuota(new QuotaManager.QuotaRebate(clientAddress)); + } + super.channelInactive(ctx); + } +} diff --git a/core/src/main/java/google/registry/eppserver/handler/HealthCheckHandler.java b/core/src/main/java/google/registry/eppserver/handler/HealthCheckHandler.java new file mode 100644 index 00000000000..d2030446c58 --- /dev/null +++ b/core/src/main/java/google/registry/eppserver/handler/HealthCheckHandler.java @@ -0,0 +1,43 @@ +// Copyright 2017 The Nomulus Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package google.registry.eppserver.handler; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import java.nio.charset.StandardCharsets; + +/** A handler that responds to GCP load balancer health check message */ +public class HealthCheckHandler extends ChannelInboundHandlerAdapter { + + private final ByteBuf checkRequest; + private final ByteBuf checkResponse; + + public HealthCheckHandler(String checkRequest, String checkResponse) { + this.checkRequest = Unpooled.wrappedBuffer(checkRequest.getBytes(StandardCharsets.US_ASCII)); + this.checkResponse = Unpooled.wrappedBuffer(checkResponse.getBytes(StandardCharsets.US_ASCII)); + } + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) { + ByteBuf buf = (ByteBuf) msg; + if (buf.equals(checkRequest)) { + ChannelFuture unusedFuture = ctx.writeAndFlush(checkResponse); + } + buf.release(); + } +} diff --git a/core/src/main/java/google/registry/eppserver/metric/BaseMetrics.java b/core/src/main/java/google/registry/eppserver/metric/BaseMetrics.java new file mode 100644 index 00000000000..a07cfe5091a --- /dev/null +++ b/core/src/main/java/google/registry/eppserver/metric/BaseMetrics.java @@ -0,0 +1,61 @@ +// Copyright 2019 The Nomulus Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package google.registry.eppserver.metric; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.ImmutableSet; +import com.google.monitoring.metrics.CustomFitter; +import com.google.monitoring.metrics.ExponentialFitter; +import com.google.monitoring.metrics.FibonacciFitter; +import com.google.monitoring.metrics.LabelDescriptor; + +/** Base class for metrics. */ +public abstract class BaseMetrics { + + /** + * Labels to register metrics with. + * + *

The client certificate hash value is only used for EPP metrics. In order to get the actual + * registrar name, one can use the {@code nomulus} tool: + * + *

+   * nomulus -e production list_registrars -f clientCertificateHash | grep $HASH
+   * 
+ */ + protected static final ImmutableSet LABELS = + ImmutableSet.of( + LabelDescriptor.create("protocol", "Name of the protocol."), + LabelDescriptor.create( + "client_cert_hash", "SHA256 hash of the client certificate, if available.")); + + // Maximum request size is defined in the config file, this is not realistic and we'd be out of + // memory when the size approaches 1 GB. + protected static final CustomFitter DEFAULT_SIZE_FITTER = FibonacciFitter.create(1073741824); + + // Maximum 1 hour latency, this is not specified by the spec, but given we have a one hour idle + // timeout, it seems reasonable that maximum latency is set to 1 hour as well. If we are + // approaching anywhere near 1 hour latency, we'd be way out of SLO anyway. + protected static final ExponentialFitter DEFAULT_LATENCY_FITTER = + ExponentialFitter.create(22, 2, 1.0); + + /** + * Resets all metrics. + * + *

This should only be used in tests to reset states. Production code should not call this + * method. + */ + @VisibleForTesting + abstract void resetMetrics(); +} diff --git a/core/src/main/java/google/registry/eppserver/metric/FrontendMetrics.java b/core/src/main/java/google/registry/eppserver/metric/FrontendMetrics.java new file mode 100644 index 00000000000..fb64bf325d0 --- /dev/null +++ b/core/src/main/java/google/registry/eppserver/metric/FrontendMetrics.java @@ -0,0 +1,126 @@ +// Copyright 2017 The Nomulus Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package google.registry.eppserver.metric; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.monitoring.metrics.EventMetric; +import com.google.monitoring.metrics.IncrementableMetric; +import com.google.monitoring.metrics.Metric; +import com.google.monitoring.metrics.MetricRegistryImpl; +import google.registry.util.NonFinalForTesting; +import io.netty.channel.Channel; +import io.netty.channel.group.ChannelGroup; +import io.netty.channel.group.DefaultChannelGroup; +import io.netty.util.concurrent.GlobalEventExecutor; +import jakarta.inject.Inject; +import jakarta.inject.Named; +import jakarta.inject.Singleton; +import java.time.Duration; +import java.util.Map; +import java.util.Random; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** Frontend metrics instrumentation. */ +@Singleton +public class FrontendMetrics extends BaseMetrics { + + private static final ConcurrentMap, ChannelGroup> activeConnections = + new ConcurrentHashMap<>(); + + static final Metric activeConnectionsGauge = + MetricRegistryImpl.getDefault() + .newGauge( + "/eppserver/active_connections", + "Number of active connections from clients to the EPP server.", + "Active Connections", + LABELS, + () -> + activeConnections.entrySet().stream() + .collect( + ImmutableMap.toImmutableMap( + Map.Entry::getKey, entry -> (long) entry.getValue().size())), + Long.class); + + static final IncrementableMetric totalConnectionsCounter = + MetricRegistryImpl.getDefault() + .newIncrementableMetric( + "/eppserver/total_connections", + "Total number connections ever made from clients to the EPP server.", + "Total Connections", + LABELS); + + static final IncrementableMetric quotaRejectionsCounter = + MetricRegistryImpl.getDefault() + .newIncrementableMetric( + "/eppserver/quota_rejections", + "Total number rejected quota request made by EPP server for each connection.", + "Quota Rejections", + LABELS); + + static final EventMetric latencyMs = + MetricRegistryImpl.getDefault() + .newEventMetric( + "/eppserver/latency_ms", + "Round-trip time between a request received and its corresponding response is sent.", + "Latency Milliseconds", + LABELS, + DEFAULT_LATENCY_FITTER); + + private final Random random; + private final double frontendMetricsRatio; + + @Inject + FrontendMetrics(@Named("frontendMetricsRatio") double frontendMetricsRatio, Random random) { + this.frontendMetricsRatio = frontendMetricsRatio; + this.random = random; + } + + @Override + void resetMetrics() { + totalConnectionsCounter.reset(); + activeConnections.clear(); + latencyMs.reset(); + } + + @NonFinalForTesting + public void registerActiveConnection(String protocol, String certHash, Channel channel) { + totalConnectionsCounter.increment(protocol, certHash); + ImmutableList labels = ImmutableList.of(protocol, certHash); + ChannelGroup channelGroup; + if (activeConnections.containsKey(labels)) { + channelGroup = activeConnections.get(labels); + } else { + channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); + activeConnections.put(labels, channelGroup); + } + channelGroup.add(channel); + } + + @NonFinalForTesting + public void registerQuotaRejection(String protocol, String certHash) { + quotaRejectionsCounter.increment(protocol, certHash); + } + + @NonFinalForTesting + public void responseSent(String protocol, String certHash, Duration latency) { + // Short-circuit metrics recording randomly according to the configured ratio. + if (random.nextDouble() > frontendMetricsRatio) { + return; + } + latencyMs.record(latency.toMillis(), protocol, certHash); + } +} diff --git a/core/src/main/java/google/registry/eppserver/quota/QuotaManager.java b/core/src/main/java/google/registry/eppserver/quota/QuotaManager.java new file mode 100644 index 00000000000..c35fb663b5e --- /dev/null +++ b/core/src/main/java/google/registry/eppserver/quota/QuotaManager.java @@ -0,0 +1,166 @@ +// Copyright 2024 The Nomulus Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package google.registry.eppserver.quota; + +import com.google.common.collect.ImmutableMap; +import com.google.common.flogger.FluentLogger; +import google.registry.config.RegistryConfigSettings.Quota; +import google.registry.config.RegistryConfigSettings.Quota.QuotaGroup; +import javax.annotation.Nullable; +import javax.annotation.concurrent.ThreadSafe; +import redis.clients.jedis.UnifiedJedis; + +/** + * Unified manager for distributed quota enforcement using Redis/Valkey. + * + *

Handles both configuration lookup and atomic Redis operations for connection and command-level + * throttling. + */ +@ThreadSafe +public class QuotaManager { + + private static final FluentLogger logger = FluentLogger.forEnclosingClass(); + private static final int DEFAULT_TTL_SECONDS = 3600; + + /** Lua script to atomically decrement a token bucket with a TTL. */ + private static final String DECR_LUA = + "local current = redis.call('GET', KEYS[1]) " + + "if not current then " + + " redis.call('SET', KEYS[1], ARGV[1] - 1, 'EX', ARGV[2]) " + + " return tonumber(ARGV[1]) - 1 " + + "end " + + "if tonumber(current) <= 0 then " + + " return -1 " + + "end " + + "return redis.call('DECR', KEYS[1])"; + + /** Lua script to atomically increment back a connection token (capped at max). */ + private static final String INCR_LUA = + "local current = redis.call('GET', KEYS[1]) " + + "if current and tonumber(current) < tonumber(ARGV[1]) then " + + " return redis.call('INCR', KEYS[1]) " + + "end " + + "return nil"; + + /** Lua script to refresh the TTL of an existing token bucket. */ + private static final String EXPIRE_LUA = + "if redis.call('EXISTS', KEYS[1]) == 1 then " + + " return redis.call('EXPIRE', KEYS[1], ARGV[1]) " + + "end " + + "return 0"; + + private final UnifiedJedis jedis; + private final String quotaNamespace; + private final QuotaGroup defaultQuota; + private final ImmutableMap customQuotas; + + public QuotaManager(Quota quota, @Nullable UnifiedJedis jedis, String quotaNamespace) { + this.jedis = jedis; + this.quotaNamespace = quotaNamespace; + this.defaultQuota = quota.defaultQuota; + + ImmutableMap.Builder builder = ImmutableMap.builder(); + quota.customQuota.forEach(group -> group.userId.forEach(userId -> builder.put(userId, group))); + this.customQuotas = builder.build(); + } + + public record QuotaRequest(String userId) {} + + public record QuotaResponse(boolean success) {} + + public record QuotaRebate(String userId) {} + + /** Attempts to acquire a quota token from Redis. */ + public QuotaResponse acquireQuota(QuotaRequest request) { + String userId = request.userId(); + QuotaGroup group = customQuotas.getOrDefault(userId, defaultQuota); + + // Unlimited quota check + if (group.tokenAmount < 0) { + return new QuotaResponse(true); + } + + if (jedis == null) { + return new QuotaResponse(true); // Fail open if no Valkey configured + } + + // Use the first ID as the virtual group identity if it's a custom group, + // otherwise isolate each default user by their actual ID. + String redisId = + (group == defaultQuota || group.userId.isEmpty()) ? userId : group.userId.get(0); + String key = String.format("%s:%s", quotaNamespace, redisId); + int ttl = group.refillSeconds > 0 ? group.refillSeconds : DEFAULT_TTL_SECONDS; + + try { + Object result = + jedis.eval(DECR_LUA, 1, key, String.valueOf(group.tokenAmount), String.valueOf(ttl)); + + return new QuotaResponse(((Long) result) >= 0); + } catch (Exception e) { + logger.atSevere().withCause(e).log("Valkey error for quota key: %s", key); + return new QuotaResponse(true); // Fail open + } + } + + /** Refreshes the TTL of an existing quota token. */ + public void refreshQuota(QuotaRequest request) { + if (jedis == null) { + return; + } + + String userId = request.userId(); + QuotaGroup group = customQuotas.getOrDefault(userId, defaultQuota); + if (group.tokenAmount < 0) { + return; + } + + // Use the first ID as the virtual group identity if it's a custom group, + // otherwise isolate each default user by their actual ID. + String redisId = + (group == defaultQuota || group.userId.isEmpty()) ? userId : group.userId.get(0); + String key = String.format("%s:%s", quotaNamespace, redisId); + int ttl = group.refillSeconds > 0 ? group.refillSeconds : DEFAULT_TTL_SECONDS; + + try { + jedis.eval(EXPIRE_LUA, 1, key, String.valueOf(ttl)); + } catch (Exception e) { + logger.atSevere().withCause(e).log("Valkey error refreshing quota for: %s", key); + } + } + + /** Returns a token to the pool (used for connection throttling). */ + public void releaseQuota(QuotaRebate rebate) { + if (jedis == null) { + return; + } + + String userId = rebate.userId(); + QuotaGroup group = customQuotas.getOrDefault(userId, defaultQuota); + if (group.tokenAmount < 0) { + return; + } + + // Use the first ID as the virtual group identity if it's a custom group, + // otherwise isolate each default user by their actual ID. + String redisId = + (group == defaultQuota || group.userId.isEmpty()) ? userId : group.userId.get(0); + String key = String.format("%s:%s", quotaNamespace, redisId); + try { + jedis.eval(INCR_LUA, 1, key, String.valueOf(group.tokenAmount)); + } catch (Exception e) { + logger.atSevere().withCause(e).log("Valkey error releasing quota for: %s", key); + } + } +} diff --git a/core/src/main/java/google/registry/flows/TlsCredentials.java b/core/src/main/java/google/registry/flows/TlsCredentials.java index 3394f5fcbb1..97795eaf0b7 100644 --- a/core/src/main/java/google/registry/flows/TlsCredentials.java +++ b/core/src/main/java/google/registry/flows/TlsCredentials.java @@ -260,7 +260,7 @@ static Optional provideClientCertificateHash(HttpServletRequest req) { static Optional provideIpAddress(HttpServletRequest req) { Optional clientAddress = extractOptionalHeader(req, ProxyHttpHeaders.IP_ADDRESS); Optional fallbackClientAddress = - extractOptionalHeader(req, ProxyHttpHeaders.IP_ADDRESS); + extractOptionalHeader(req, ProxyHttpHeaders.FALLBACK_IP_ADDRESS); Optional clientInetAddr = clientAddress.map(TlsCredentials::parseInetAddress); return clientInetAddr.isPresent() ? clientInetAddr diff --git a/core/src/main/java/google/registry/module/EppServerLifecycleListener.java b/core/src/main/java/google/registry/module/EppServerLifecycleListener.java new file mode 100644 index 00000000000..485bbca8f47 --- /dev/null +++ b/core/src/main/java/google/registry/module/EppServerLifecycleListener.java @@ -0,0 +1,78 @@ +// Copyright 2024 The Nomulus Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package google.registry.module; + +import com.google.common.flogger.FluentLogger; +import google.registry.eppserver.DaggerEppServerModule_EppServerComponent; +import google.registry.eppserver.EppServer; +import google.registry.eppserver.EppServerModule; +import jakarta.servlet.ServletContextEvent; +import jakarta.servlet.ServletContextListener; +import jakarta.servlet.annotation.WebListener; + +/** A {@link ServletContextListener} that starts and stops the integrated Netty EPP server. */ +@WebListener +public class EppServerLifecycleListener implements ServletContextListener { + + private static final FluentLogger logger = FluentLogger.forEnclosingClass(); + private EppServer eppServer; + private EppServerModule.EppServerComponent eppServerComponent; + + @Override + public void contextInitialized(ServletContextEvent sce) { + if (!Boolean.parseBoolean(System.getenv("TCP_SERVER_ENABLED"))) { + logger.atInfo().log( + "TCP_SERVER_ENABLED is false or not set. Skipping integrated EPP server initialization."); + return; + } + + logger.atInfo().log("Initializing integrated EPP server..."); + try { + EppServerModule eppServerModule = new EppServerModule(); + eppServerComponent = + DaggerEppServerModule_EppServerComponent.builder() + .eppServerModule(eppServerModule) + .build(); + + eppServer = new EppServer(eppServerComponent); + eppServer.start(); + logger.atInfo().log("Integrated EPP server started successfully."); + } catch (Exception e) { + logger.atSevere().withCause(e).log("Failed to start integrated EPP server."); + } + } + + @Override + public void contextDestroyed(ServletContextEvent sce) { + if (eppServer != null) { + logger.atInfo().log("Stopping integrated EPP server..."); + eppServer.stop(); + logger.atInfo().log("Integrated EPP server stopped."); + } + if (eppServerComponent != null) { + eppServerComponent + .jedis() + .ifPresent( + jedis -> { + try { + jedis.close(); + logger.atInfo().log("Closed UnifiedJedis client."); + } catch (Exception e) { + logger.atSevere().withCause(e).log("Failed to close UnifiedJedis client."); + } + }); + } + } +} diff --git a/core/src/main/java/google/registry/module/MetricsLifecycleListener.java b/core/src/main/java/google/registry/module/MetricsLifecycleListener.java new file mode 100644 index 00000000000..5e04ce4fe29 --- /dev/null +++ b/core/src/main/java/google/registry/module/MetricsLifecycleListener.java @@ -0,0 +1,54 @@ +// Copyright 2026 The Nomulus Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package google.registry.module; + +import com.google.common.flogger.FluentLogger; +import com.google.monitoring.metrics.MetricReporter; +import jakarta.servlet.ServletContextEvent; +import jakarta.servlet.ServletContextListener; +import jakarta.servlet.annotation.WebListener; +import java.time.Duration; + +/** Starts and stops the single global JVM MetricReporter on application startup/shutdown. */ +@WebListener +public class MetricsLifecycleListener implements ServletContextListener { + + private static final FluentLogger logger = FluentLogger.forEnclosingClass(); + private MetricReporter metricReporter; + + @Override + public void contextInitialized(ServletContextEvent sce) { + logger.atInfo().log("Initializing global MetricReporter..."); + try { + metricReporter = RegistryServlet.component.metricReporter().get(); + metricReporter.startAsync().awaitRunning(Duration.ofSeconds(10)); + logger.atInfo().log("Global MetricReporter started successfully."); + } catch (Exception e) { + logger.atSevere().withCause(e).log("Failed to start global MetricReporter."); + } + } + + @Override + public void contextDestroyed(ServletContextEvent sce) { + if (metricReporter != null) { + try { + metricReporter.stopAsync().awaitTerminated(Duration.ofSeconds(10)); + logger.atInfo().log("Global MetricReporter stopped."); + } catch (Exception e) { + logger.atSevere().withCause(e).log("Failed to stop global MetricReporter."); + } + } + } +} diff --git a/core/src/main/java/google/registry/module/RegistryComponent.java b/core/src/main/java/google/registry/module/RegistryComponent.java index 6c6fe280e46..cc9806115b7 100644 --- a/core/src/main/java/google/registry/module/RegistryComponent.java +++ b/core/src/main/java/google/registry/module/RegistryComponent.java @@ -38,8 +38,6 @@ import google.registry.groups.GroupssettingsModule; import google.registry.keyring.KeyringModule; import google.registry.keyring.api.KeyModule; -import google.registry.module.RegistryComponent.RegistryModule; -import google.registry.module.RequestComponent.RequestComponentModule; import google.registry.monitoring.whitebox.StackdriverModule; import google.registry.mosapi.module.MosApiModule; import google.registry.persistence.PersistenceModule; @@ -55,7 +53,7 @@ import jakarta.inject.Provider; import jakarta.inject.Singleton; -/** Dagger component with instance lifetime. */ +/** Dagger component for the registry servlet. */ @Singleton @Component( modules = { @@ -74,14 +72,13 @@ GroupsModule.class, GroupssettingsModule.class, GsonModule.class, - MosApiModule.class, JSchModule.class, KeyModule.class, KeyringModule.class, + MosApiModule.class, NetHttpTransportModule.class, PersistenceModule.class, - RegistryModule.class, - RequestComponentModule.class, + RegistryComponent.RegistryModule.class, SecretManagerModule.class, ServerTridProviderModule.class, SheetsServiceModule.class, @@ -90,15 +87,18 @@ UtilsModule.class, VoidDnsWriterModule.class, }) -interface RegistryComponent { +public interface RegistryComponent { RequestHandler requestHandler(); + RequestAuthenticator requestAuthenticator(); + Lazy metricReporter(); @Config("projectId") String projectId(); - @Module + /** Module for {@link RegistryComponent}. */ + @Module(subcomponents = RequestComponent.class) class RegistryModule { @Provides static RequestHandler provideRequestHandler( diff --git a/core/src/main/java/google/registry/module/RegistryServlet.java b/core/src/main/java/google/registry/module/RegistryServlet.java index 173076d6f93..e9efc9a40ef 100644 --- a/core/src/main/java/google/registry/module/RegistryServlet.java +++ b/core/src/main/java/google/registry/module/RegistryServlet.java @@ -22,8 +22,6 @@ import static google.registry.util.RandomStringGenerator.insecureRandomStringGenerator; import static google.registry.util.StringGenerator.Alphabets.HEX_DIGITS_ONLY; -import com.google.monitoring.metrics.MetricReporter; -import dagger.Lazy; import google.registry.request.RequestHandler; import google.registry.util.GcpJsonFormatter; import google.registry.util.JdkLoggerConfig; @@ -49,9 +47,8 @@ public class RegistryServlet extends ServletBase { private static final RandomStringGenerator LOG_TRACE_ID_GENERATOR = insecureRandomStringGenerator(HEX_DIGITS_ONLY); - private static final RegistryComponent component = DaggerRegistryComponent.create(); + public static final RegistryComponent component = DaggerRegistryComponent.create(); private static final RequestHandler requestHandler = component.requestHandler(); - private static final Lazy metricReporter = component.metricReporter(); // The regex pattern to capture the cookies that we want to log. private static final Pattern COOKIE_REGEX_PATTERN = @@ -71,7 +68,7 @@ public class RegistryServlet extends ServletBase { } public RegistryServlet() { - super(requestHandler, metricReporter); + super(requestHandler); this.projectId = component.projectId(); } diff --git a/core/src/main/java/google/registry/module/ServletBase.java b/core/src/main/java/google/registry/module/ServletBase.java index 9f5d9b4b3b6..a64dce76290 100644 --- a/core/src/main/java/google/registry/module/ServletBase.java +++ b/core/src/main/java/google/registry/module/ServletBase.java @@ -15,8 +15,6 @@ package google.registry.module; import com.google.common.flogger.FluentLogger; -import com.google.monitoring.metrics.MetricReporter; -import dagger.Lazy; import google.registry.request.RequestHandler; import google.registry.util.SystemClock; import jakarta.servlet.http.HttpServlet; @@ -26,46 +24,22 @@ import java.security.Security; import java.time.Duration; import java.time.Instant; -import java.util.concurrent.TimeoutException; import org.bouncycastle.jce.provider.BouncyCastleProvider; /** Base for Servlets that handle all requests to our modules. */ public class ServletBase extends HttpServlet { private final RequestHandler requestHandler; - private final Lazy metricReporter; private static final FluentLogger logger = FluentLogger.forEnclosingClass(); private static final SystemClock clock = new SystemClock(); - public ServletBase(RequestHandler requestHandler, Lazy metricReporter) { + public ServletBase(RequestHandler requestHandler) { this.requestHandler = requestHandler; - this.metricReporter = metricReporter; } @Override public void init() { Security.addProvider(new BouncyCastleProvider()); - - // If the metric reporter failed to instantiate for any reason (bad keyring, bad json - // credential, etc.), we log the error but keep the main thread running. Also, the shutdown hook - // will only be registered if the metric reporter starts up correctly. - try { - metricReporter.get().startAsync().awaitRunning(Duration.ofSeconds(10)); - logger.atInfo().log("Started up MetricReporter."); - Runtime.getRuntime() - .addShutdownHook( - new Thread( - () -> { - try { - metricReporter.get().stopAsync().awaitTerminated(Duration.ofSeconds(10)); - logger.atInfo().log("Shut down MetricReporter."); - } catch (TimeoutException e) { - logger.atSevere().withCause(e).log("Failed to stop MetricReporter."); - } - })); - } catch (Exception e) { - logger.atSevere().withCause(e).log("Failed to initialize MetricReporter."); - } } @Override diff --git a/core/src/main/java/google/registry/util/FakeHttpServletRequest.java b/core/src/main/java/google/registry/util/FakeHttpServletRequest.java new file mode 100644 index 00000000000..9b2de909c59 --- /dev/null +++ b/core/src/main/java/google/registry/util/FakeHttpServletRequest.java @@ -0,0 +1,448 @@ +// Copyright 2024 The Nomulus Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package google.registry.util; + +import static java.nio.charset.StandardCharsets.UTF_8; + +import jakarta.servlet.AsyncContext; +import jakarta.servlet.DispatcherType; +import jakarta.servlet.ReadListener; +import jakarta.servlet.RequestDispatcher; +import jakarta.servlet.ServletConnection; +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletInputStream; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; +import jakarta.servlet.http.HttpUpgradeHandler; +import jakarta.servlet.http.Part; +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; +import java.security.Principal; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +/** Simple fake {@link HttpServletRequest} for internal use. */ +public class FakeHttpServletRequest implements HttpServletRequest { + + private final Map headers = new HashMap<>(); + private final Map attributes = new HashMap<>(); + private String method = "POST"; + private String requestUri = "/_dr/epp"; + private String serverName = "localhost"; + private byte[] body = new byte[0]; + private String contentType = "application/epp+xml"; + + public void setHeader(String name, String value) { + headers.put(name, value); + } + + public void setMethod(String method) { + this.method = method; + } + + public void setRequestUri(String requestUri) { + this.requestUri = requestUri; + } + + public void setBody(byte[] body) { + this.body = body.clone(); + } + + @Override + public String getHeader(String name) { + return headers.get(name); + } + + @Override + public Enumeration getHeaders(String name) { + String value = headers.get(name); + return Collections.enumeration( + value == null ? Collections.emptyList() : Collections.singletonList(value)); + } + + @Override + public Enumeration getHeaderNames() { + return Collections.enumeration(headers.keySet()); + } + + @Override + public int getIntHeader(String name) { + String value = headers.get(name); + return value == null ? -1 : Integer.parseInt(value); + } + + @Override + public String getMethod() { + return method; + } + + @Override + public String getRequestURI() { + return requestUri; + } + + @Override + public String getServerName() { + return serverName; + } + + @Override + public ServletInputStream getInputStream() throws IOException { + final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body); + return new ServletInputStream() { + @Override + public boolean isFinished() { + return byteArrayInputStream.available() == 0; + } + + @Override + public boolean isReady() { + return true; + } + + @Override + public void setReadListener(ReadListener readListener) {} + + @Override + public int read() throws IOException { + return byteArrayInputStream.read(); + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + return byteArrayInputStream.read(b, off, len); + } + }; + } + + @Override + public String getContentType() { + return contentType; + } + + // --- Implement other methods with defaults --- + @Override + public String getAuthType() { + return null; + } + + @Override + public Cookie[] getCookies() { + return new Cookie[0]; + } + + @Override + public long getDateHeader(String name) { + return -1; + } + + @Override + public String getPathInfo() { + return null; + } + + @Override + public String getPathTranslated() { + return null; + } + + @Override + public String getContextPath() { + return ""; + } + + @Override + public String getQueryString() { + return null; + } + + @Override + public String getRemoteUser() { + return null; + } + + @Override + public boolean isUserInRole(String role) { + return false; + } + + @Override + public Principal getUserPrincipal() { + return null; + } + + @Override + public String getRequestedSessionId() { + return null; + } + + @Override + public StringBuffer getRequestURL() { + return new StringBuffer(requestUri); + } + + @Override + public String getServletPath() { + return ""; + } + + @Override + public HttpSession getSession(boolean create) { + return null; + } + + @Override + public HttpSession getSession() { + return null; + } + + @Override + public String changeSessionId() { + return null; + } + + @Override + public boolean isRequestedSessionIdValid() { + return false; + } + + @Override + public boolean isRequestedSessionIdFromCookie() { + return false; + } + + @Override + public boolean isRequestedSessionIdFromURL() { + return false; + } + + @Override + public boolean authenticate(HttpServletResponse response) throws IOException, ServletException { + return false; + } + + @Override + public void login(String username, String password) throws ServletException {} + + @Override + public void logout() throws ServletException {} + + @Override + public Collection getParts() throws IOException, ServletException { + return null; + } + + @Override + public Part getPart(String name) throws IOException, ServletException { + return null; + } + + @Override + public T upgrade(Class handlerClass) + throws IOException, ServletException { + return null; + } + + @Override + public Object getAttribute(String name) { + return attributes.get(name); + } + + @Override + public Enumeration getAttributeNames() { + return Collections.enumeration(attributes.keySet()); + } + + @Override + public String getCharacterEncoding() { + return "UTF-8"; + } + + @Override + public void setCharacterEncoding(String env) throws UnsupportedEncodingException {} + + @Override + public int getContentLength() { + return body.length; + } + + @Override + public long getContentLengthLong() { + return body.length; + } + + @Override + public String getParameter(String name) { + return null; + } + + @Override + public Enumeration getParameterNames() { + return Collections.emptyEnumeration(); + } + + @Override + public String[] getParameterValues(String name) { + return null; + } + + @Override + public Map getParameterMap() { + return Collections.emptyMap(); + } + + @Override + public String getProtocol() { + return "HTTP/1.1"; + } + + @Override + public String getScheme() { + return "http"; + } + + @Override + public int getServerPort() { + return 8080; + } + + @Override + public BufferedReader getReader() throws IOException { + return new BufferedReader(new InputStreamReader(getInputStream(), UTF_8)); + } + + @Override + public String getRemoteAddr() { + return "127.0.0.1"; + } + + @Override + public String getRemoteHost() { + return "localhost"; + } + + @Override + public void setAttribute(String name, Object o) { + attributes.put(name, o); + } + + @Override + public void removeAttribute(String name) { + attributes.remove(name); + } + + @Override + public Locale getLocale() { + return Locale.US; + } + + @Override + public Enumeration getLocales() { + return Collections.enumeration(Collections.singleton(Locale.US)); + } + + @Override + public boolean isSecure() { + return false; + } + + @Override + public RequestDispatcher getRequestDispatcher(String path) { + return null; + } + + @Override + public int getRemotePort() { + return 0; + } + + @Override + public String getLocalName() { + return "localhost"; + } + + @Override + public String getLocalAddr() { + return "127.0.0.1"; + } + + @Override + public int getLocalPort() { + return 8080; + } + + @Override + public ServletContext getServletContext() { + return null; + } + + @Override + public AsyncContext startAsync() throws IllegalStateException { + return null; + } + + @Override + public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) + throws IllegalStateException { + return null; + } + + @Override + public boolean isAsyncStarted() { + return false; + } + + @Override + public boolean isAsyncSupported() { + return false; + } + + @Override + public AsyncContext getAsyncContext() { + return null; + } + + @Override + public DispatcherType getDispatcherType() { + return DispatcherType.REQUEST; + } + + @Override + public String getRequestId() { + return null; + } + + @Override + public String getProtocolRequestId() { + return null; + } + + @Override + public ServletConnection getServletConnection() { + return null; + } +} diff --git a/core/src/main/java/google/registry/util/FakeHttpServletResponse.java b/core/src/main/java/google/registry/util/FakeHttpServletResponse.java new file mode 100644 index 00000000000..68b0d68eba3 --- /dev/null +++ b/core/src/main/java/google/registry/util/FakeHttpServletResponse.java @@ -0,0 +1,222 @@ +// Copyright 2024 The Nomulus Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package google.registry.util; + +import static java.nio.charset.StandardCharsets.UTF_8; + +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.WriteListener; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletResponse; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +/** Simple fake {@link HttpServletResponse} for internal use. */ +public class FakeHttpServletResponse implements HttpServletResponse { + + private int status = SC_OK; + private String contentType; + private final Map headers = new HashMap<>(); + private final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + private final PrintWriter writer = new PrintWriter(outputStream, false, UTF_8); + + @Override + public void setStatus(int sc) { + this.status = sc; + } + + @Override + public int getStatus() { + return status; + } + + @Override + public void setContentType(String type) { + this.contentType = type; + } + + @Override + public String getContentType() { + return contentType; + } + + @Override + public void setHeader(String name, String value) { + headers.put(name, value); + } + + @Override + public void addHeader(String name, String value) { + headers.put(name, value); + } + + @Override + public String getHeader(String name) { + return headers.get(name); + } + + @Override + public Collection getHeaders(String name) { + String value = headers.get(name); + return value == null ? Collections.emptyList() : Collections.singletonList(value); + } + + @Override + public Collection getHeaderNames() { + return headers.keySet(); + } + + @Override + public ServletOutputStream getOutputStream() throws IOException { + return new ServletOutputStream() { + @Override + public boolean isReady() { + return true; + } + + @Override + public void setWriteListener(WriteListener writeListener) {} + + @Override + public void write(int b) throws IOException { + outputStream.write(b); + } + }; + } + + @Override + public PrintWriter getWriter() throws IOException { + return writer; + } + + public byte[] getPayload() { + writer.flush(); + return outputStream.toByteArray(); + } + + // --- Implement other methods with defaults --- + @Override + public void addCookie(Cookie cookie) {} + + @Override + public boolean containsHeader(String name) { + return headers.containsKey(name); + } + + @Override + public String encodeURL(String url) { + return url; + } + + @Override + public String encodeRedirectURL(String url) { + return url; + } + + @Override + public void sendError(int sc, String msg) throws IOException { + this.status = sc; + } + + @Override + public void sendError(int sc) throws IOException { + this.status = sc; + } + + @Override + public void sendRedirect(String location) throws IOException { + this.status = SC_FOUND; + } + + @Override + public void sendRedirect(String location, int sc, boolean clearBuffer) throws IOException { + this.status = sc; + } + + @Override + public void setDateHeader(String name, long date) {} + + @Override + public void addDateHeader(String name, long date) {} + + @Override + public void setIntHeader(String name, int value) {} + + @Override + public void addIntHeader(String name, int value) {} + + @Override + public void setCharacterEncoding(String charset) {} + + @Override + public void setContentLength(int len) {} + + @Override + public void setContentLengthLong(long len) {} + + @Override + public void setBufferSize(int size) {} + + @Override + public int getBufferSize() { + return 0; + } + + @Override + public void flushBuffer() throws IOException { + writer.flush(); + } + + @Override + public void resetBuffer() { + writer.flush(); + outputStream.reset(); + } + + @Override + public boolean isCommitted() { + return false; + } + + @Override + public void reset() { + writer.flush(); + outputStream.reset(); + headers.clear(); + status = SC_OK; + } + + @Override + public void setLocale(Locale loc) {} + + @Override + public Locale getLocale() { + return Locale.US; + } + + @Override + public String getCharacterEncoding() { + return "UTF-8"; + } + + @Override + public void sendEarlyHints() {} +} diff --git a/core/src/main/resources/google/registry/eppserver/hello.xml b/core/src/main/resources/google/registry/eppserver/hello.xml new file mode 100644 index 00000000000..30fb4a0f7a1 --- /dev/null +++ b/core/src/main/resources/google/registry/eppserver/hello.xml @@ -0,0 +1,4 @@ + + + + diff --git a/core/src/test/java/google/registry/eppserver/handler/EppProxyProtocolHandlerTest.java b/core/src/test/java/google/registry/eppserver/handler/EppProxyProtocolHandlerTest.java new file mode 100644 index 00000000000..d974e5711a9 --- /dev/null +++ b/core/src/test/java/google/registry/eppserver/handler/EppProxyProtocolHandlerTest.java @@ -0,0 +1,75 @@ +// Copyright 2024 The Nomulus Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package google.registry.eppserver.handler; + +import static com.google.common.truth.Truth.assertThat; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.embedded.EmbeddedChannel; +import java.nio.charset.StandardCharsets; +import org.junit.jupiter.api.Test; + +class EppProxyProtocolHandlerTest { + + @Test + void testProxyProtocol_parsesValidHeader() { + EppProxyProtocolHandler handler = new EppProxyProtocolHandler(); + EmbeddedChannel channel = new EmbeddedChannel(handler); + + String proxyHeader = "PROXY TCP4 192.168.1.1 10.0.0.1 50000 443\r\n"; + ByteBuf buffer = Unpooled.wrappedBuffer(proxyHeader.getBytes(StandardCharsets.US_ASCII)); + + channel.writeInbound(buffer); + + String remoteAddress = channel.attr(EppProxyProtocolHandler.REMOTE_ADDRESS_KEY).get(); + assertThat(remoteAddress).isEqualTo("192.168.1.1"); + assertThat(channel.pipeline().get(EppProxyProtocolHandler.class)).isNull(); + } + + @Test + void testProxyProtocol_unknownHeader() { + EppProxyProtocolHandler handler = new EppProxyProtocolHandler(); + EmbeddedChannel channel = new EmbeddedChannel(handler); + + String proxyHeader = "PROXY UNKNOWN\r\n"; + ByteBuf buffer = Unpooled.wrappedBuffer(proxyHeader.getBytes(StandardCharsets.US_ASCII)); + + channel.writeInbound(buffer); + + String remoteAddress = channel.attr(EppProxyProtocolHandler.REMOTE_ADDRESS_KEY).get(); + assertThat(remoteAddress).isEqualTo("0.0.0.0"); + assertThat(channel.pipeline().get(EppProxyProtocolHandler.class)).isNull(); + } + + @Test + void testProxyProtocol_noHeader_notProxied() { + EppProxyProtocolHandler handler = new EppProxyProtocolHandler(); + EmbeddedChannel channel = new EmbeddedChannel(handler); + + String normalData = "NOT_A_PROXY_HEADER"; + ByteBuf buffer = Unpooled.wrappedBuffer(normalData.getBytes(StandardCharsets.US_ASCII)); + + channel.writeInbound(buffer); + + String remoteAddress = channel.attr(EppProxyProtocolHandler.REMOTE_ADDRESS_KEY).get(); + // In EmbeddedChannel without remoteAddress mock, getSourceIP returns null + assertThat(remoteAddress).isNull(); + assertThat(channel.pipeline().get(EppProxyProtocolHandler.class)).isNull(); + + ByteBuf passedOn = channel.readInbound(); + assertThat(passedOn.toString(StandardCharsets.US_ASCII)).isEqualTo("NOT_A_PROXY_HEADER"); + } +} diff --git a/core/src/test/java/google/registry/eppserver/handler/EppServiceHandlerTest.java b/core/src/test/java/google/registry/eppserver/handler/EppServiceHandlerTest.java new file mode 100644 index 00000000000..be475718208 --- /dev/null +++ b/core/src/test/java/google/registry/eppserver/handler/EppServiceHandlerTest.java @@ -0,0 +1,121 @@ +// Copyright 2024 The Nomulus Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package google.registry.eppserver.handler; + +import static google.registry.eppserver.handler.EppProxyProtocolHandler.REMOTE_ADDRESS_KEY; +import static google.registry.networking.handler.SslServerInitializer.CLIENT_CERTIFICATE_PROMISE_KEY; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import google.registry.eppserver.metric.FrontendMetrics; +import google.registry.eppserver.quota.QuotaManager; +import google.registry.eppserver.quota.QuotaManager.QuotaRequest; +import google.registry.eppserver.quota.QuotaManager.QuotaResponse; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.util.Attribute; +import io.netty.util.concurrent.DefaultPromise; +import io.netty.util.concurrent.ImmediateEventExecutor; +import io.netty.util.concurrent.Promise; +import java.security.cert.X509Certificate; +import java.util.function.Supplier; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class EppServiceHandlerTest { + + @Mock private FrontendMetrics metrics; + @Mock private QuotaManager connectionQuotaManager; + @Mock private QuotaManager commandQuotaManager; + @Mock private QuotaManager ipQuotaManager; + @Mock private Supplier idTokenSupplier; + @Mock private ChannelHandlerContext ctx; + @Mock private Channel channel; + + @Mock private Attribute> certPromiseAttr; + @Mock private Attribute remoteAddressAttr; + @Mock private Attribute certHashAttr; + @Mock private X509Certificate certificate; + + private EppServiceHandler handler; + private DefaultPromise certPromise; + + @BeforeEach + void setUp() { + handler = + new EppServiceHandler( + new byte[] {'h', 'e', 'l', 'l', 'o'}, + metrics, + connectionQuotaManager, + commandQuotaManager, + ipQuotaManager, + idTokenSupplier, + "test-project"); + + when(ctx.channel()).thenReturn(channel); + } + + @Test + void testChannelActive_ipQuotaRejected() throws Exception { + certPromise = new DefaultPromise<>(ImmediateEventExecutor.INSTANCE); + when(channel.attr(CLIENT_CERTIFICATE_PROMISE_KEY)).thenReturn(certPromiseAttr); + when(certPromiseAttr.get()).thenReturn(certPromise); + + handler.channelActive(ctx); + + when(channel.attr(REMOTE_ADDRESS_KEY)).thenReturn(remoteAddressAttr); + when(remoteAddressAttr.get()).thenReturn("192.168.1.1"); + when(channel.attr(EppServiceHandler.CLIENT_CERTIFICATE_HASH_KEY)).thenReturn(certHashAttr); + when(certificate.getEncoded()).thenReturn(new byte[] {1, 2, 3}); + + // Reject IP quota + when(ipQuotaManager.acquireQuota(any(QuotaRequest.class))).thenReturn(new QuotaResponse(false)); + + certPromise.setSuccess(certificate); + + verify(metrics).registerQuotaRejection(eq("epp_connection_ip"), eq("192.168.1.1")); + verify(ctx).close(); + } + + @Test + void testChannelActive_connectionQuotaRejected() throws Exception { + certPromise = new DefaultPromise<>(ImmediateEventExecutor.INSTANCE); + when(channel.attr(CLIENT_CERTIFICATE_PROMISE_KEY)).thenReturn(certPromiseAttr); + when(certPromiseAttr.get()).thenReturn(certPromise); + + handler.channelActive(ctx); + + when(channel.attr(REMOTE_ADDRESS_KEY)).thenReturn(remoteAddressAttr); + when(remoteAddressAttr.get()).thenReturn("192.168.1.1"); + when(channel.attr(EppServiceHandler.CLIENT_CERTIFICATE_HASH_KEY)).thenReturn(certHashAttr); + when(certificate.getEncoded()).thenReturn(new byte[] {1, 2, 3}); + + // Accept IP quota but reject connection quota + when(ipQuotaManager.acquireQuota(any(QuotaRequest.class))).thenReturn(new QuotaResponse(true)); + when(connectionQuotaManager.acquireQuota(any(QuotaRequest.class))) + .thenReturn(new QuotaResponse(false)); + + certPromise.setSuccess(certificate); + + verify(metrics).registerQuotaRejection(eq("epp_connection"), any(String.class)); + verify(ctx).close(); + } +} diff --git a/core/src/test/java/google/registry/eppserver/handler/HealthCheckHandlerTest.java b/core/src/test/java/google/registry/eppserver/handler/HealthCheckHandlerTest.java new file mode 100644 index 00000000000..ddffce317e3 --- /dev/null +++ b/core/src/test/java/google/registry/eppserver/handler/HealthCheckHandlerTest.java @@ -0,0 +1,52 @@ +// Copyright 2024 The Nomulus Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package google.registry.eppserver.handler; + +import static com.google.common.truth.Truth.assertThat; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.embedded.EmbeddedChannel; +import java.nio.charset.StandardCharsets; +import org.junit.jupiter.api.Test; + +class HealthCheckHandlerTest { + + @Test + void testHealthCheck_success() { + HealthCheckHandler handler = new HealthCheckHandler("ping", "pong"); + EmbeddedChannel channel = new EmbeddedChannel(handler); + + ByteBuf pingMsg = Unpooled.wrappedBuffer("ping".getBytes(StandardCharsets.US_ASCII)); + channel.writeInbound(pingMsg); + + ByteBuf response = channel.readOutbound(); + assertThat(response.toString(StandardCharsets.US_ASCII)).isEqualTo("pong"); + assertThat(channel.isOpen()).isTrue(); + } + + @Test + void testHealthCheck_ignoresOtherMessages() { + HealthCheckHandler handler = new HealthCheckHandler("ping", "pong"); + EmbeddedChannel channel = new EmbeddedChannel(handler); + + ByteBuf otherMsg = Unpooled.wrappedBuffer("foo".getBytes(StandardCharsets.US_ASCII)); + channel.writeInbound(otherMsg); + + ByteBuf response = channel.readOutbound(); + assertThat(response).isNull(); + assertThat(channel.isOpen()).isTrue(); + } +} diff --git a/core/src/test/java/google/registry/eppserver/quota/QuotaManagerTest.java b/core/src/test/java/google/registry/eppserver/quota/QuotaManagerTest.java new file mode 100644 index 00000000000..0af0e659e4e --- /dev/null +++ b/core/src/test/java/google/registry/eppserver/quota/QuotaManagerTest.java @@ -0,0 +1,159 @@ +// Copyright 2024 The Nomulus Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package google.registry.eppserver.quota; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.google.common.collect.ImmutableList; +import google.registry.config.RegistryConfigSettings.Quota; +import google.registry.config.RegistryConfigSettings.Quota.QuotaGroup; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import redis.clients.jedis.UnifiedJedis; + +@ExtendWith(MockitoExtension.class) +class QuotaManagerTest { + + @Mock private UnifiedJedis jedis; + + private Quota quotaConfig; + private QuotaManager manager; + + @BeforeEach + void setUp() { + quotaConfig = new Quota(); + QuotaGroup defaultGroup = new QuotaGroup(); + defaultGroup.tokenAmount = 10; + defaultGroup.refillSeconds = 60; + quotaConfig.defaultQuota = defaultGroup; + + QuotaGroup customGroup = new QuotaGroup(); + customGroup.tokenAmount = 5; + customGroup.refillSeconds = 30; + customGroup.userId = ImmutableList.of("user1"); + quotaConfig.customQuota = ImmutableList.of(customGroup); + + manager = new QuotaManager(quotaConfig, jedis, "testQuota"); + } + + @Test + void testAcquireQuota_success() { + when(jedis.eval(anyString(), anyInt(), anyString(), anyString(), anyString())).thenReturn(5L); + + QuotaManager.QuotaResponse response = + manager.acquireQuota(new QuotaManager.QuotaRequest("user2")); + assertThat(response.success()).isTrue(); + verify(jedis).eval(anyString(), eq(1), eq("testQuota:user2"), eq("10"), eq("60")); + } + + @Test + void testAcquireQuota_failure() { + when(jedis.eval(anyString(), anyInt(), anyString(), anyString(), anyString())).thenReturn(-1L); + + QuotaManager.QuotaResponse response = + manager.acquireQuota(new QuotaManager.QuotaRequest("user1")); + assertThat(response.success()).isFalse(); + verify(jedis).eval(anyString(), eq(1), eq("testQuota:user1"), eq("5"), eq("30")); + } + + @Test + void testAcquireQuota_unlimited() { + quotaConfig.defaultQuota.tokenAmount = -1; + manager = new QuotaManager(quotaConfig, jedis, "testQuota"); + + QuotaManager.QuotaResponse response = + manager.acquireQuota(new QuotaManager.QuotaRequest("user2")); + assertThat(response.success()).isTrue(); + } + + @Test + void testAcquireQuota_jedisException_failsOpen() { + when(jedis.eval(anyString(), anyInt(), anyString(), anyString(), anyString())) + .thenThrow(new RuntimeException("Redis error")); + + QuotaManager.QuotaResponse response = + manager.acquireQuota(new QuotaManager.QuotaRequest("user2")); + assertThat(response.success()).isTrue(); + } + + @Test + void testRefreshQuota_success() { + manager.refreshQuota(new QuotaManager.QuotaRequest("user2")); + verify(jedis).eval(anyString(), eq(1), eq("testQuota:user2"), eq("60")); + } + + @Test + void testReleaseQuota_success() { + manager.releaseQuota(new QuotaManager.QuotaRebate("user2")); + verify(jedis).eval(anyString(), eq(1), eq("testQuota:user2"), eq("10")); + } + + @Test + void testGroupVirtualIdentity_usesFirstIdInList() { + // Modify config so "user1" is accompanied by a virtual group ID "my_group" + quotaConfig.customQuota.get(0).userId = ImmutableList.of("my_group", "user1", "user3"); + manager = new QuotaManager(quotaConfig, jedis, "testQuota"); + + when(jedis.eval(anyString(), anyInt(), anyString(), anyString(), anyString())).thenReturn(5L); + + QuotaManager.QuotaResponse response1 = + manager.acquireQuota(new QuotaManager.QuotaRequest("user1")); + QuotaManager.QuotaResponse response2 = + manager.acquireQuota(new QuotaManager.QuotaRequest("user3")); + + assertThat(response1.success()).isTrue(); + assertThat(response2.success()).isTrue(); + // 5 tokens, 30 seconds ttl + verify(jedis, times(2)).eval(anyString(), eq(1), eq("testQuota:my_group"), eq("5"), eq("30")); + } + + @Test + void testGroupVirtualIdentity_exceedsQuota_fails() { + // Modify config so "user1", "user2", "user3" share virtual group ID "my_group" + quotaConfig.customQuota.get(0).userId = ImmutableList.of("my_group", "user1", "user2", "user3"); + manager = new QuotaManager(quotaConfig, jedis, "testQuota"); + + // Simulate Redis returning 1, 0 for successful decrements, and -1 when empty + when(jedis.eval(anyString(), anyInt(), anyString(), anyString(), anyString())) + .thenReturn(1L) + .thenReturn(0L) + .thenReturn(-1L); + + // Act + QuotaManager.QuotaResponse response1 = + manager.acquireQuota(new QuotaManager.QuotaRequest("user1")); + QuotaManager.QuotaResponse response2 = + manager.acquireQuota(new QuotaManager.QuotaRequest("user2")); + QuotaManager.QuotaResponse response3 = + manager.acquireQuota(new QuotaManager.QuotaRequest("user3")); + + // Assert that the third request to the same group fails + assertThat(response1.success()).isTrue(); + assertThat(response2.success()).isTrue(); + assertThat(response3.success()).isFalse(); + + // Verify all 3 requests went to the shared bucket + verify(jedis, times(3)).eval(anyString(), eq(1), eq("testQuota:my_group"), eq("5"), eq("30")); + } +} diff --git a/core/src/test/java/google/registry/module/TestServlet.java b/core/src/test/java/google/registry/module/TestServlet.java index 173b1f622d5..f84bc24989c 100644 --- a/core/src/test/java/google/registry/module/TestServlet.java +++ b/core/src/test/java/google/registry/module/TestServlet.java @@ -14,9 +14,6 @@ package google.registry.module; -import com.google.monitoring.metrics.MetricReporter; -import dagger.Lazy; - /** * Servlet used in the test server to handle routing. * @@ -27,9 +24,8 @@ public class TestServlet extends ServletBase { private static final TestRegistryComponent component = DaggerTestRegistryComponent.create(); private static final TestRequestHandler requestHandler = component.requestHandler(); - private static final Lazy metricReporter = component.metricReporter(); public TestServlet() { - super(requestHandler, metricReporter); + super(requestHandler); } } diff --git a/core/src/test/java/google/registry/util/FakeHttpServletRequestTest.java b/core/src/test/java/google/registry/util/FakeHttpServletRequestTest.java new file mode 100644 index 00000000000..4048371c508 --- /dev/null +++ b/core/src/test/java/google/registry/util/FakeHttpServletRequestTest.java @@ -0,0 +1,78 @@ +// Copyright 2024 The Nomulus Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package google.registry.util; + +import static com.google.common.truth.Truth.assertThat; +import static java.nio.charset.StandardCharsets.UTF_8; + +import java.io.BufferedReader; +import java.io.IOException; +import java.util.Collections; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** Unit tests for {@link FakeHttpServletRequest}. */ +class FakeHttpServletRequestTest { + + private FakeHttpServletRequest request; + + @BeforeEach + void setUp() { + request = new FakeHttpServletRequest(); + } + + @Test + void testHeaders() { + request.setHeader("X-My-Header", "Value1"); + assertThat(request.getHeader("X-My-Header")).isEqualTo("Value1"); + assertThat(Collections.list(request.getHeaders("X-My-Header"))).containsExactly("Value1"); + assertThat(Collections.list(request.getHeaderNames())).containsExactly("X-My-Header"); + } + + @Test + void testIntHeader() { + request.setHeader("X-Int-Header", "42"); + assertThat(request.getIntHeader("X-Int-Header")).isEqualTo(42); + assertThat(request.getIntHeader("Missing")).isEqualTo(-1); + } + + @Test + void testMethodAndUri() { + request.setMethod("GET"); + request.setRequestUri("/test/path"); + assertThat(request.getMethod()).isEqualTo("GET"); + assertThat(request.getRequestURI()).isEqualTo("/test/path"); + } + + @Test + void testBodyAndReader() throws IOException { + String payload = "test body"; + request.setBody(payload.getBytes(UTF_8)); + + BufferedReader reader = request.getReader(); + assertThat(reader.readLine()).isEqualTo(payload); + assertThat(request.getContentLength()).isEqualTo(payload.length()); + } + + @Test + void testAttributes() { + request.setAttribute("attr1", "val1"); + assertThat(request.getAttribute("attr1")).isEqualTo("val1"); + assertThat(Collections.list(request.getAttributeNames())).containsExactly("attr1"); + + request.removeAttribute("attr1"); + assertThat(request.getAttribute("attr1")).isNull(); + } +} diff --git a/core/src/test/java/google/registry/util/FakeHttpServletResponseTest.java b/core/src/test/java/google/registry/util/FakeHttpServletResponseTest.java new file mode 100644 index 00000000000..3cced2b21d4 --- /dev/null +++ b/core/src/test/java/google/registry/util/FakeHttpServletResponseTest.java @@ -0,0 +1,105 @@ +// Copyright 2024 The Nomulus Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package google.registry.util; + +import static com.google.common.truth.Truth.assertThat; +import static jakarta.servlet.http.HttpServletResponse.SC_FOUND; +import static jakarta.servlet.http.HttpServletResponse.SC_OK; +import static java.nio.charset.StandardCharsets.UTF_8; + +import java.io.IOException; +import java.io.PrintWriter; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** Unit tests for {@link FakeHttpServletResponse}. */ +class FakeHttpServletResponseTest { + + private FakeHttpServletResponse response; + + @BeforeEach + void setUp() { + response = new FakeHttpServletResponse(); + } + + @Test + void testStatus() throws IOException { + assertThat(response.getStatus()).isEqualTo(SC_OK); + + response.setStatus(400); + assertThat(response.getStatus()).isEqualTo(400); + + response.sendError(500); + assertThat(response.getStatus()).isEqualTo(500); + + response.sendRedirect("/redirect"); + assertThat(response.getStatus()).isEqualTo(SC_FOUND); + } + + @Test + void testContentType() { + response.setContentType("text/plain"); + assertThat(response.getContentType()).isEqualTo("text/plain"); + } + + @Test + void testHeaders() { + response.setHeader("X-Test", "Val"); + assertThat(response.getHeader("X-Test")).isEqualTo("Val"); + assertThat(response.getHeaders("X-Test")).containsExactly("Val"); + assertThat(response.getHeaderNames()).containsExactly("X-Test"); + assertThat(response.containsHeader("X-Test")).isTrue(); + } + + @Test + void testWriterAndPayload() throws IOException { + PrintWriter writer = response.getWriter(); + writer.print("hello world"); + + assertThat(new String(response.getPayload(), UTF_8)).isEqualTo("hello world"); + } + + @Test + void testOutputStreamAndPayload() throws IOException { + byte[] data = "test bytes".getBytes(UTF_8); + response.getOutputStream().write(data); + + assertThat(response.getPayload()).isEqualTo(data); + } + + @Test + void testReset() throws IOException { + response.setStatus(404); + response.setHeader("A", "B"); + response.getWriter().print("content"); + + response.reset(); + + assertThat(response.getStatus()).isEqualTo(SC_OK); + assertThat(response.getHeaderNames()).isEmpty(); + assertThat(response.getPayload()).isEmpty(); + } + + @Test + void testSendRedirectWithStatus() throws IOException { + response.sendRedirect("/redirect", 301, false); + assertThat(response.getStatus()).isEqualTo(301); + } + + @Test + void testSendEarlyHints() { + response.sendEarlyHints(); // Should not throw + } +} diff --git a/db/buildscript-gradle.lockfile b/db/buildscript-gradle.lockfile index d8b261e2004..0abcaf7d69b 100644 --- a/db/buildscript-gradle.lockfile +++ b/db/buildscript-gradle.lockfile @@ -4,8 +4,8 @@ com.fasterxml.jackson.core:jackson-annotations:2.21=classpath gradle.plugin.org.flywaydb:gradle-plugin-publishing:12.2.0=classpath org.flywaydb.flyway:org.flywaydb.flyway.gradle.plugin:12.2.0=classpath -org.flywaydb:flyway-core:12.7.0=classpath -org.flywaydb:flyway-database-postgresql:12.7.0=classpath +org.flywaydb:flyway-core:12.8.1=classpath +org.flywaydb:flyway-database-postgresql:12.8.1=classpath tools.jackson.core:jackson-core:3.1.1=classpath tools.jackson.core:jackson-databind:3.1.1=classpath tools.jackson:jackson-bom:3.1.1=classpath diff --git a/db/gradle.lockfile b/db/gradle.lockfile index b44baf214f6..4f2152d07bd 100644 --- a/db/gradle.lockfile +++ b/db/gradle.lockfile @@ -109,8 +109,10 @@ org.codehaus.plexus:plexus-classworlds:2.6.0=checkstyle org.codehaus.plexus:plexus-component-annotations:2.1.0=checkstyle org.codehaus.plexus:plexus-container-default:2.1.0=checkstyle org.codehaus.plexus:plexus-utils:3.3.0=checkstyle -org.flywaydb:flyway-core:12.7.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.flywaydb:flyway-database-postgresql:12.7.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.flywaydb:flyway-core:12.7.0=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.flywaydb:flyway-core:12.8.1=compileClasspath +org.flywaydb:flyway-database-postgresql:12.7.0=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.flywaydb:flyway-database-postgresql:12.8.1=compileClasspath org.freemarker:freemarker:2.3.34=testCompileClasspath,testRuntimeClasspath org.hamcrest:hamcrest-core:1.3=testCompileClasspath,testRuntimeClasspath org.jacoco:org.jacoco.agent:0.8.14=jacocoAgent,jacocoAnt diff --git a/jetty/deploy-nomulus-for-env.sh b/jetty/deploy-nomulus-for-env.sh index 01675e20836..fabd0fb4044 100755 --- a/jetty/deploy-nomulus-for-env.sh +++ b/jetty/deploy-nomulus-for-env.sh @@ -33,7 +33,7 @@ line=$(gcloud container clusters list --project "${project}" | grep nomulus | gr parts=(${line}) echo "Updating cluster ${parts[0]} in location ${parts[1]}..." gcloud container fleet memberships get-credentials "${parts[0]}" --project "${project}" -for service in frontend backend pubapi console +for service in frontend backend pubapi console epp-server do sed s/GCP_PROJECT/"${project}"/g "./kubernetes/nomulus-${service}.yaml" | \ sed s/ENVIRONMENT/"${environment}"/g | \ diff --git a/jetty/gradle.lockfile b/jetty/gradle.lockfile index fa3455a3ba6..b54102594d9 100644 --- a/jetty/gradle.lockfile +++ b/jetty/gradle.lockfile @@ -3,14 +3,22 @@ # This file is expected to be part of source control. args4j:args4j:2.33=deploy_jar,runtimeClasspath,testRuntimeClasspath com.charleskorn.kaml:kaml:0.20.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.fasterxml.jackson.core:jackson-annotations:2.21=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.fasterxml.jackson.core:jackson-core:2.21.4=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.fasterxml.jackson.core:jackson-databind:2.21.4=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.21.4=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.21.4=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.fasterxml.jackson.datatype:jackson-datatype-joda:2.21.4=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.21.4=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.fasterxml.jackson:jackson-bom:2.21.4=deploy_jar,runtimeClasspath,testRuntimeClasspath +com.fasterxml.jackson.core:jackson-annotations:2.21=deploy_jar,testRuntimeClasspath +com.fasterxml.jackson.core:jackson-annotations:2.22=runtimeClasspath +com.fasterxml.jackson.core:jackson-core:2.21.4=deploy_jar,testRuntimeClasspath +com.fasterxml.jackson.core:jackson-core:2.22.0=runtimeClasspath +com.fasterxml.jackson.core:jackson-databind:2.21.4=deploy_jar,testRuntimeClasspath +com.fasterxml.jackson.core:jackson-databind:2.22.0=runtimeClasspath +com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.21.4=deploy_jar,testRuntimeClasspath +com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.22.0=runtimeClasspath +com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.21.4=deploy_jar,testRuntimeClasspath +com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.22.0=runtimeClasspath +com.fasterxml.jackson.datatype:jackson-datatype-joda:2.21.4=deploy_jar,testRuntimeClasspath +com.fasterxml.jackson.datatype:jackson-datatype-joda:2.22.0=runtimeClasspath +com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.21.4=deploy_jar,testRuntimeClasspath +com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.22.0=runtimeClasspath +com.fasterxml.jackson:jackson-bom:2.21.4=deploy_jar,testRuntimeClasspath +com.fasterxml.jackson:jackson-bom:2.22.0=runtimeClasspath com.fasterxml.woodstox:woodstox-core:7.0.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.github.ben-manes.caffeine:caffeine:3.0.5=annotationProcessor,testAnnotationProcessor com.github.ben-manes.caffeine:caffeine:3.2.4=deploy_jar,runtimeClasspath,testRuntimeClasspath @@ -26,55 +34,93 @@ com.github.jnr:jnr-posix:3.1.22=deploy_jar,runtimeClasspath,testRuntimeClasspath com.github.jnr:jnr-unixsocket:0.38.25=deploy_jar,runtimeClasspath,testRuntimeClasspath com.github.jnr:jnr-x86asm:1.0.2=deploy_jar,runtimeClasspath,testRuntimeClasspath com.github.kevinstern:software-and-algorithms:1.0=annotationProcessor,testAnnotationProcessor -com.github.mwiede:jsch:2.28.2=deploy_jar,runtimeClasspath,testRuntimeClasspath +com.github.mwiede:jsch:2.28.2=deploy_jar,testRuntimeClasspath +com.github.mwiede:jsch:2.28.3=runtimeClasspath com.google.android:annotations:4.1.1.4=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.api-client:google-api-client-jackson2:2.7.0=deploy_jar,runtimeClasspath,testRuntimeClasspath +com.google.api-client:google-api-client-gson:2.9.0=runtimeClasspath +com.google.api-client:google-api-client-jackson2:2.7.0=deploy_jar,testRuntimeClasspath com.google.api-client:google-api-client-java6:2.1.4=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.api-client:google-api-client-servlet:2.7.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.api-client:google-api-client:2.9.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.api.grpc:gapic-google-cloud-storage-v2:2.64.1=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1:3.24.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta1:0.196.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta2:0.196.0=deploy_jar,runtimeClasspath,testRuntimeClasspath +com.google.api.grpc:gapic-google-cloud-storage-v2:2.64.1=deploy_jar,testRuntimeClasspath +com.google.api.grpc:gapic-google-cloud-storage-v2:2.67.0=runtimeClasspath +com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1:3.24.0=deploy_jar,testRuntimeClasspath +com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1:3.27.0=runtimeClasspath +com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta1:0.196.0=deploy_jar,testRuntimeClasspath +com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta1:0.199.0=runtimeClasspath +com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta2:0.196.0=deploy_jar,testRuntimeClasspath +com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta2:0.199.0=runtimeClasspath com.google.api.grpc:grpc-google-cloud-bigtable-v2:2.73.1=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.api.grpc:grpc-google-cloud-pubsub-v1:1.132.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.api.grpc:grpc-google-cloud-spanner-admin-database-v1:6.113.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.api.grpc:grpc-google-cloud-spanner-admin-instance-v1:6.113.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.api.grpc:grpc-google-cloud-spanner-v1:6.113.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.api.grpc:grpc-google-cloud-storage-control-v2:2.44.1=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.api.grpc:grpc-google-cloud-storage-v2:2.64.1=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.api.grpc:grpc-google-common-protos:2.67.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.api.grpc:proto-google-cloud-bigquerystorage-v1:3.24.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.api.grpc:proto-google-cloud-bigquerystorage-v1alpha:3.24.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.api.grpc:proto-google-cloud-bigquerystorage-v1beta1:0.196.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.api.grpc:proto-google-cloud-bigquerystorage-v1beta2:0.196.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.api.grpc:proto-google-cloud-bigquerystorage-v1beta:3.24.0=deploy_jar,runtimeClasspath,testRuntimeClasspath +com.google.api.grpc:grpc-google-cloud-pubsub-v1:1.132.0=deploy_jar,testRuntimeClasspath +com.google.api.grpc:grpc-google-cloud-pubsub-v1:1.132.1=runtimeClasspath +com.google.api.grpc:grpc-google-cloud-spanner-admin-database-v1:6.113.0=deploy_jar,testRuntimeClasspath +com.google.api.grpc:grpc-google-cloud-spanner-admin-database-v1:6.116.0=runtimeClasspath +com.google.api.grpc:grpc-google-cloud-spanner-admin-instance-v1:6.113.0=deploy_jar,testRuntimeClasspath +com.google.api.grpc:grpc-google-cloud-spanner-admin-instance-v1:6.116.0=runtimeClasspath +com.google.api.grpc:grpc-google-cloud-spanner-v1:6.113.0=deploy_jar,testRuntimeClasspath +com.google.api.grpc:grpc-google-cloud-spanner-v1:6.116.0=runtimeClasspath +com.google.api.grpc:grpc-google-cloud-storage-control-v2:2.44.1=deploy_jar,testRuntimeClasspath +com.google.api.grpc:grpc-google-cloud-storage-control-v2:2.49.0=runtimeClasspath +com.google.api.grpc:grpc-google-cloud-storage-v2:2.64.1=deploy_jar,testRuntimeClasspath +com.google.api.grpc:grpc-google-cloud-storage-v2:2.67.0=runtimeClasspath +com.google.api.grpc:grpc-google-common-protos:2.67.0=deploy_jar,testRuntimeClasspath +com.google.api.grpc:grpc-google-common-protos:2.70.0=runtimeClasspath +com.google.api.grpc:proto-google-cloud-bigquerystorage-v1:3.24.0=deploy_jar,testRuntimeClasspath +com.google.api.grpc:proto-google-cloud-bigquerystorage-v1:3.27.0=runtimeClasspath +com.google.api.grpc:proto-google-cloud-bigquerystorage-v1alpha:3.24.0=deploy_jar,testRuntimeClasspath +com.google.api.grpc:proto-google-cloud-bigquerystorage-v1alpha:3.27.0=runtimeClasspath +com.google.api.grpc:proto-google-cloud-bigquerystorage-v1beta1:0.196.0=deploy_jar,testRuntimeClasspath +com.google.api.grpc:proto-google-cloud-bigquerystorage-v1beta1:0.199.0=runtimeClasspath +com.google.api.grpc:proto-google-cloud-bigquerystorage-v1beta2:0.196.0=deploy_jar,testRuntimeClasspath +com.google.api.grpc:proto-google-cloud-bigquerystorage-v1beta2:0.199.0=runtimeClasspath +com.google.api.grpc:proto-google-cloud-bigquerystorage-v1beta:3.24.0=deploy_jar,testRuntimeClasspath +com.google.api.grpc:proto-google-cloud-bigquerystorage-v1beta:3.27.0=runtimeClasspath com.google.api.grpc:proto-google-cloud-bigtable-admin-v2:2.73.1=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.api.grpc:proto-google-cloud-bigtable-v2:2.75.1=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.api.grpc:proto-google-cloud-compute-v1:1.102.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.api.grpc:proto-google-cloud-datastore-v1:0.128.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.api.grpc:proto-google-cloud-firestore-v1:3.39.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.api.grpc:proto-google-cloud-logging-v2:0.118.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.api.grpc:proto-google-cloud-monitoring-v3:3.85.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.api.grpc:proto-google-cloud-pubsub-v1:1.132.0=deploy_jar,runtimeClasspath,testRuntimeClasspath +com.google.api.grpc:proto-google-cloud-bigtable-v2:2.75.1=deploy_jar,testRuntimeClasspath +com.google.api.grpc:proto-google-cloud-bigtable-v2:2.77.0=runtimeClasspath +com.google.api.grpc:proto-google-cloud-compute-v1:1.102.0=deploy_jar,testRuntimeClasspath +com.google.api.grpc:proto-google-cloud-compute-v1:1.103.0=runtimeClasspath +com.google.api.grpc:proto-google-cloud-datastore-v1:0.128.0=deploy_jar,testRuntimeClasspath +com.google.api.grpc:proto-google-cloud-datastore-v1:0.131.0=runtimeClasspath +com.google.api.grpc:proto-google-cloud-firestore-v1:3.39.0=deploy_jar,testRuntimeClasspath +com.google.api.grpc:proto-google-cloud-firestore-v1:3.41.0=runtimeClasspath +com.google.api.grpc:proto-google-cloud-logging-v2:0.118.0=deploy_jar,testRuntimeClasspath +com.google.api.grpc:proto-google-cloud-logging-v2:0.121.0=runtimeClasspath +com.google.api.grpc:proto-google-cloud-monitoring-v3:3.85.0=deploy_jar,testRuntimeClasspath +com.google.api.grpc:proto-google-cloud-monitoring-v3:3.92.0=runtimeClasspath +com.google.api.grpc:proto-google-cloud-pubsub-v1:1.132.0=deploy_jar,testRuntimeClasspath +com.google.api.grpc:proto-google-cloud-pubsub-v1:1.132.1=runtimeClasspath com.google.api.grpc:proto-google-cloud-secretmanager-v1:2.51.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.api.grpc:proto-google-cloud-secretmanager-v1beta2:2.51.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.api.grpc:proto-google-cloud-spanner-admin-database-v1:6.113.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.api.grpc:proto-google-cloud-spanner-admin-instance-v1:6.113.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.api.grpc:proto-google-cloud-spanner-v1:6.113.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.api.grpc:proto-google-cloud-storage-control-v2:2.44.1=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.api.grpc:proto-google-cloud-storage-v2:2.64.1=deploy_jar,runtimeClasspath,testRuntimeClasspath +com.google.api.grpc:proto-google-cloud-spanner-admin-database-v1:6.113.0=deploy_jar,testRuntimeClasspath +com.google.api.grpc:proto-google-cloud-spanner-admin-database-v1:6.116.0=runtimeClasspath +com.google.api.grpc:proto-google-cloud-spanner-admin-instance-v1:6.113.0=deploy_jar,testRuntimeClasspath +com.google.api.grpc:proto-google-cloud-spanner-admin-instance-v1:6.116.0=runtimeClasspath +com.google.api.grpc:proto-google-cloud-spanner-v1:6.113.0=deploy_jar,testRuntimeClasspath +com.google.api.grpc:proto-google-cloud-spanner-v1:6.116.0=runtimeClasspath +com.google.api.grpc:proto-google-cloud-storage-control-v2:2.44.1=deploy_jar,testRuntimeClasspath +com.google.api.grpc:proto-google-cloud-storage-control-v2:2.49.0=runtimeClasspath +com.google.api.grpc:proto-google-cloud-storage-v2:2.64.1=deploy_jar,testRuntimeClasspath +com.google.api.grpc:proto-google-cloud-storage-v2:2.67.0=runtimeClasspath com.google.api.grpc:proto-google-cloud-tasks-v2:2.51.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.api.grpc:proto-google-cloud-tasks-v2beta2:0.141.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.api.grpc:proto-google-cloud-tasks-v2beta3:0.141.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.api.grpc:proto-google-common-protos:2.71.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.api.grpc:proto-google-iam-v1:1.62.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.api:api-common:2.63.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.api:gax-grpc:2.80.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.api:gax-httpjson:2.80.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.api:gax:2.80.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.apis:google-api-services-admin-directory:directory_v1-rev20260522-2.0.0=deploy_jar,runtimeClasspath,testRuntimeClasspath +com.google.api.grpc:proto-google-common-protos:2.71.0=deploy_jar,testRuntimeClasspath +com.google.api.grpc:proto-google-common-protos:2.72.0=runtimeClasspath +com.google.api.grpc:proto-google-iam-v1:1.62.0=deploy_jar,testRuntimeClasspath +com.google.api.grpc:proto-google-iam-v1:1.65.0=runtimeClasspath +com.google.api:api-common:2.63.0=deploy_jar,testRuntimeClasspath +com.google.api:api-common:2.64.0=runtimeClasspath +com.google.api:gax-grpc:2.80.0=deploy_jar,testRuntimeClasspath +com.google.api:gax-grpc:2.81.0=runtimeClasspath +com.google.api:gax-httpjson:2.80.0=deploy_jar,testRuntimeClasspath +com.google.api:gax-httpjson:2.81.0=runtimeClasspath +com.google.api:gax:2.80.0=deploy_jar,testRuntimeClasspath +com.google.api:gax:2.81.0=runtimeClasspath +com.google.apis:google-api-services-admin-directory:directory_v1-rev20260522-2.0.0=deploy_jar,testRuntimeClasspath +com.google.apis:google-api-services-admin-directory:directory_v1-rev20260531-2.0.0=runtimeClasspath com.google.apis:google-api-services-bigquery:v2-rev20251012-2.0.0=deploy_jar,runtimeClasspath,testRuntimeClasspath +com.google.apis:google-api-services-cloudkms:v1-rev20260522-2.0.0=runtimeClasspath com.google.apis:google-api-services-cloudresourcemanager:v1-rev20250606-2.0.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.apis:google-api-services-dataflow:v1b3-rev20260503-2.0.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.apis:google-api-services-dns:v1-rev20260421-2.0.0=deploy_jar,runtimeClasspath,testRuntimeClasspath @@ -89,41 +135,60 @@ com.google.apis:google-api-services-pubsub:v1-rev20220904-2.0.0=deploy_jar,runti com.google.apis:google-api-services-sheets:v4-rev20260213-2.0.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.apis:google-api-services-sqladmin:v1beta4-rev20260510-2.0.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.apis:google-api-services-storage:v1-rev20260204-2.0.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.auth:google-auth-library-credentials:1.47.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.auth:google-auth-library-oauth2-http:1.47.0=deploy_jar,runtimeClasspath,testRuntimeClasspath +com.google.auth:google-auth-library-credentials:1.47.0=deploy_jar,testRuntimeClasspath +com.google.auth:google-auth-library-credentials:1.48.0=runtimeClasspath +com.google.auth:google-auth-library-oauth2-http:1.47.0=deploy_jar,testRuntimeClasspath +com.google.auth:google-auth-library-oauth2-http:1.48.0=runtimeClasspath com.google.auto.service:auto-service-annotations:1.0.1=annotationProcessor,testAnnotationProcessor com.google.auto.service:auto-service-annotations:1.1.1=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.auto.value:auto-value-annotations:1.11.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.auto.value:auto-value-annotations:1.9=annotationProcessor,testAnnotationProcessor com.google.auto.value:auto-value:1.11.1=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.auto:auto-common:1.2.2=annotationProcessor,testAnnotationProcessor -com.google.cloud.bigdataoss:gcsio:2.2.26=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.cloud.bigdataoss:util:2.2.26=deploy_jar,runtimeClasspath,testRuntimeClasspath +com.google.cloud.bigdataoss:gcsio:2.2.26=deploy_jar,testRuntimeClasspath +com.google.cloud.bigdataoss:gcsio:3.1.16=runtimeClasspath +com.google.cloud.bigdataoss:util:2.2.26=deploy_jar,testRuntimeClasspath +com.google.cloud.bigdataoss:util:3.1.16=runtimeClasspath com.google.cloud.bigtable:bigtable-client-core-config:1.28.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.cloud.datastore:datastore-v1-proto-client:2.37.0=deploy_jar,runtimeClasspath,testRuntimeClasspath +com.google.cloud.datastore:datastore-v1-proto-client:2.37.0=deploy_jar,testRuntimeClasspath +com.google.cloud.datastore:datastore-v1-proto-client:2.40.0=runtimeClasspath com.google.cloud.opentelemetry:detector-resources-support:0.33.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.cloud.opentelemetry:exporter-metrics:0.33.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.cloud.opentelemetry:shared-resourcemapping:0.33.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.cloud.sql:jdbc-socket-factory-core:1.28.4=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.cloud.sql:postgres-socket-factory:1.28.4=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.cloud:google-cloud-bigquerystorage:3.24.0=deploy_jar,runtimeClasspath,testRuntimeClasspath +com.google.cloud:google-cloud-bigquerystorage:3.24.0=deploy_jar,testRuntimeClasspath +com.google.cloud:google-cloud-bigquerystorage:3.27.0=runtimeClasspath com.google.cloud:google-cloud-bigtable:2.73.1=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.cloud:google-cloud-compute:1.102.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.cloud:google-cloud-core-grpc:2.66.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.cloud:google-cloud-core-http:2.66.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.cloud:google-cloud-core:2.66.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.cloud:google-cloud-firestore:3.39.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.cloud:google-cloud-logging:3.29.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.cloud:google-cloud-monitoring:3.85.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.cloud:google-cloud-pubsub:1.150.0=deploy_jar,runtimeClasspath,testRuntimeClasspath +com.google.cloud:google-cloud-compute:1.102.0=deploy_jar,testRuntimeClasspath +com.google.cloud:google-cloud-compute:1.103.0=runtimeClasspath +com.google.cloud:google-cloud-core-grpc:2.66.0=deploy_jar,testRuntimeClasspath +com.google.cloud:google-cloud-core-grpc:2.69.0=runtimeClasspath +com.google.cloud:google-cloud-core-http:2.66.0=deploy_jar,testRuntimeClasspath +com.google.cloud:google-cloud-core-http:2.69.0=runtimeClasspath +com.google.cloud:google-cloud-core:2.66.0=deploy_jar,testRuntimeClasspath +com.google.cloud:google-cloud-core:2.69.0=runtimeClasspath +com.google.cloud:google-cloud-firestore:3.39.0=deploy_jar,testRuntimeClasspath +com.google.cloud:google-cloud-firestore:3.41.0=runtimeClasspath +com.google.cloud:google-cloud-logging:3.29.0=deploy_jar,testRuntimeClasspath +com.google.cloud:google-cloud-logging:3.32.0=runtimeClasspath +com.google.cloud:google-cloud-monitoring:3.85.0=deploy_jar,testRuntimeClasspath +com.google.cloud:google-cloud-monitoring:3.92.0=runtimeClasspath +com.google.cloud:google-cloud-pubsub:1.150.0=deploy_jar,testRuntimeClasspath +com.google.cloud:google-cloud-pubsub:1.150.1=runtimeClasspath com.google.cloud:google-cloud-secretmanager:2.51.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.cloud:google-cloud-spanner:6.113.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.cloud:google-cloud-storage-control:2.44.1=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.cloud:google-cloud-storage:2.64.1=deploy_jar,runtimeClasspath,testRuntimeClasspath +com.google.cloud:google-cloud-spanner:6.113.0=deploy_jar,testRuntimeClasspath +com.google.cloud:google-cloud-spanner:6.116.0=runtimeClasspath +com.google.cloud:google-cloud-storage-control:2.44.1=deploy_jar,testRuntimeClasspath +com.google.cloud:google-cloud-storage-control:2.49.0=runtimeClasspath +com.google.cloud:google-cloud-storage:2.64.1=deploy_jar,testRuntimeClasspath +com.google.cloud:google-cloud-storage:2.67.0=runtimeClasspath com.google.cloud:google-cloud-tasks:2.51.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.cloud:grpc-gcp:1.9.0=deploy_jar,runtimeClasspath,testRuntimeClasspath +com.google.cloud:grpc-gcp:1.10.0=runtimeClasspath +com.google.cloud:grpc-gcp:1.9.0=deploy_jar,testRuntimeClasspath com.google.cloud:libraries-bom:26.48.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.cloud:proto-google-cloud-firestore-bundle-v1:3.39.0=deploy_jar,runtimeClasspath,testRuntimeClasspath +com.google.cloud:proto-google-cloud-firestore-bundle-v1:3.39.0=deploy_jar,testRuntimeClasspath +com.google.cloud:proto-google-cloud-firestore-bundle-v1:3.41.0=runtimeClasspath com.google.code.findbugs:jsr305:3.0.2=checkstyle,deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.code.gson:gson:2.14.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.dagger:dagger:2.59.2=deploy_jar,runtimeClasspath,testRuntimeClasspath @@ -136,7 +201,8 @@ com.google.errorprone:error_prone_core:2.48.0=annotationProcessor,testAnnotation com.google.flatbuffers:flatbuffers-java:24.3.25=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.flogger:flogger-system-backend:0.7.4=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.flogger:flogger:0.7.4=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.flogger:google-extensions:0.7.1=deploy_jar,runtimeClasspath,testRuntimeClasspath +com.google.flogger:google-extensions:0.7.1=deploy_jar,testRuntimeClasspath +com.google.flogger:google-extensions:0.7.4=runtimeClasspath com.google.googlejavaformat:google-java-format:1.34.1=annotationProcessor,testAnnotationProcessor com.google.guava:failureaccess:1.0.3=annotationProcessor,checkstyle,deploy_jar,runtimeClasspath,testAnnotationProcessor,testRuntimeClasspath com.google.guava:guava:33.4.8-jre=checkstyle @@ -165,13 +231,17 @@ com.google.truth:truth:1.4.5=deploy_jar,runtimeClasspath,testRuntimeClasspath com.ibm.icu:icu4j:73.2=deploy_jar,runtimeClasspath,testRuntimeClasspath com.lmax:disruptor:3.4.2=deploy_jar,runtimeClasspath,testRuntimeClasspath com.puppycrawl.tools:checkstyle:10.24.0=checkstyle -com.squareup.okhttp3:okhttp-jvm:5.3.2=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.squareup.okhttp3:okhttp:5.3.2=deploy_jar,runtimeClasspath,testRuntimeClasspath +com.squareup.okhttp3:okhttp-jvm:5.3.2=deploy_jar,testRuntimeClasspath +com.squareup.okhttp3:okhttp-jvm:5.4.0=runtimeClasspath +com.squareup.okhttp3:okhttp:5.3.2=deploy_jar,testRuntimeClasspath +com.squareup.okhttp3:okhttp:5.4.0=runtimeClasspath com.squareup.okio:okio-bom:3.0.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.squareup.okio:okio-fakefilesystem-jvm:3.4.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.squareup.okio:okio-fakefilesystem:3.4.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.squareup.okio:okio-jvm:3.16.4=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.squareup.okio:okio:3.16.4=deploy_jar,runtimeClasspath,testRuntimeClasspath +com.squareup.okio:okio-jvm:3.16.4=deploy_jar,testRuntimeClasspath +com.squareup.okio:okio-jvm:3.17.0=runtimeClasspath +com.squareup.okio:okio:3.16.4=deploy_jar,testRuntimeClasspath +com.squareup.okio:okio:3.17.0=runtimeClasspath com.squareup.wire:wire-compiler:4.5.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.squareup.wire:wire-grpc-server-generator:4.5.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.squareup.wire:wire-grpc-server:4.5.0=deploy_jar,runtimeClasspath,testRuntimeClasspath @@ -203,53 +273,83 @@ io.github.java-diff-utils:java-diff-utils:4.17=deploy_jar,runtimeClasspath,testR io.grpc:grpc-alts:1.81.0=deploy_jar,runtimeClasspath,testRuntimeClasspath io.grpc:grpc-api:1.81.0=deploy_jar,runtimeClasspath,testRuntimeClasspath io.grpc:grpc-auth:1.81.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -io.grpc:grpc-census:1.76.3=deploy_jar,runtimeClasspath,testRuntimeClasspath +io.grpc:grpc-census:1.76.3=deploy_jar,testRuntimeClasspath +io.grpc:grpc-census:1.80.0=runtimeClasspath io.grpc:grpc-context:1.81.0=deploy_jar,runtimeClasspath,testRuntimeClasspath io.grpc:grpc-core:1.81.0=deploy_jar,runtimeClasspath,testRuntimeClasspath io.grpc:grpc-googleapis:1.81.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -io.grpc:grpc-grpclb:1.76.3=deploy_jar,runtimeClasspath,testRuntimeClasspath +io.grpc:grpc-grpclb:1.76.3=deploy_jar,testRuntimeClasspath +io.grpc:grpc-grpclb:1.80.0=runtimeClasspath io.grpc:grpc-inprocess:1.81.0=deploy_jar,runtimeClasspath,testRuntimeClasspath io.grpc:grpc-netty-shaded:1.81.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -io.grpc:grpc-netty:1.76.3=deploy_jar,runtimeClasspath,testRuntimeClasspath -io.grpc:grpc-opentelemetry:1.76.3=deploy_jar,runtimeClasspath,testRuntimeClasspath +io.grpc:grpc-netty:1.76.3=deploy_jar,testRuntimeClasspath +io.grpc:grpc-netty:1.80.0=runtimeClasspath +io.grpc:grpc-opentelemetry:1.76.3=deploy_jar,testRuntimeClasspath +io.grpc:grpc-opentelemetry:1.80.0=runtimeClasspath io.grpc:grpc-protobuf-lite:1.81.0=deploy_jar,runtimeClasspath,testRuntimeClasspath io.grpc:grpc-protobuf:1.81.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -io.grpc:grpc-rls:1.76.3=deploy_jar,runtimeClasspath,testRuntimeClasspath +io.grpc:grpc-rls:1.76.3=deploy_jar,testRuntimeClasspath +io.grpc:grpc-rls:1.80.0=runtimeClasspath io.grpc:grpc-services:1.81.0=deploy_jar,runtimeClasspath,testRuntimeClasspath io.grpc:grpc-stub:1.81.0=deploy_jar,runtimeClasspath,testRuntimeClasspath +io.grpc:grpc-testing:1.70.0=runtimeClasspath io.grpc:grpc-util:1.81.0=deploy_jar,runtimeClasspath,testRuntimeClasspath io.grpc:grpc-xds:1.81.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -io.netty:netty-buffer:4.1.124.Final=deploy_jar,runtimeClasspath,testRuntimeClasspath -io.netty:netty-codec-http2:4.1.124.Final=deploy_jar,runtimeClasspath,testRuntimeClasspath -io.netty:netty-codec-http:4.1.124.Final=deploy_jar,runtimeClasspath,testRuntimeClasspath -io.netty:netty-codec-socks:4.1.124.Final=deploy_jar,runtimeClasspath,testRuntimeClasspath -io.netty:netty-codec:4.1.124.Final=deploy_jar,runtimeClasspath,testRuntimeClasspath -io.netty:netty-common:4.1.124.Final=deploy_jar,runtimeClasspath,testRuntimeClasspath -io.netty:netty-handler-proxy:4.1.124.Final=deploy_jar,runtimeClasspath,testRuntimeClasspath -io.netty:netty-handler:4.1.124.Final=deploy_jar,runtimeClasspath,testRuntimeClasspath -io.netty:netty-resolver:4.1.124.Final=deploy_jar,runtimeClasspath,testRuntimeClasspath +io.netty:netty-buffer:4.1.124.Final=deploy_jar,testRuntimeClasspath +io.netty:netty-buffer:4.2.15.Final=runtimeClasspath +io.netty:netty-codec-base:4.2.15.Final=runtimeClasspath +io.netty:netty-codec-compression:4.2.15.Final=runtimeClasspath +io.netty:netty-codec-http2:4.1.124.Final=deploy_jar,testRuntimeClasspath +io.netty:netty-codec-http2:4.1.130.Final=runtimeClasspath +io.netty:netty-codec-http:4.1.124.Final=deploy_jar,testRuntimeClasspath +io.netty:netty-codec-http:4.2.15.Final=runtimeClasspath +io.netty:netty-codec-marshalling:4.2.15.Final=runtimeClasspath +io.netty:netty-codec-protobuf:4.2.15.Final=runtimeClasspath +io.netty:netty-codec-socks:4.1.124.Final=deploy_jar,testRuntimeClasspath +io.netty:netty-codec-socks:4.1.130.Final=runtimeClasspath +io.netty:netty-codec:4.1.124.Final=deploy_jar,testRuntimeClasspath +io.netty:netty-codec:4.2.15.Final=runtimeClasspath +io.netty:netty-common:4.1.124.Final=deploy_jar,testRuntimeClasspath +io.netty:netty-common:4.2.15.Final=runtimeClasspath +io.netty:netty-handler-proxy:4.1.124.Final=deploy_jar,testRuntimeClasspath +io.netty:netty-handler-proxy:4.1.130.Final=runtimeClasspath +io.netty:netty-handler:4.1.124.Final=deploy_jar,testRuntimeClasspath +io.netty:netty-handler:4.2.15.Final=runtimeClasspath +io.netty:netty-resolver:4.1.124.Final=deploy_jar,testRuntimeClasspath +io.netty:netty-resolver:4.2.15.Final=runtimeClasspath io.netty:netty-tcnative-boringssl-static:2.0.52.Final=deploy_jar,runtimeClasspath,testRuntimeClasspath io.netty:netty-tcnative-classes:2.0.52.Final=deploy_jar,runtimeClasspath,testRuntimeClasspath -io.netty:netty-transport-native-unix-common:4.1.124.Final=deploy_jar,runtimeClasspath,testRuntimeClasspath -io.netty:netty-transport:4.1.124.Final=deploy_jar,runtimeClasspath,testRuntimeClasspath +io.netty:netty-transport-native-unix-common:4.1.124.Final=deploy_jar,testRuntimeClasspath +io.netty:netty-transport-native-unix-common:4.2.15.Final=runtimeClasspath +io.netty:netty-transport:4.1.124.Final=deploy_jar,testRuntimeClasspath +io.netty:netty-transport:4.2.15.Final=runtimeClasspath io.opencensus:opencensus-api:0.31.1=deploy_jar,runtimeClasspath,testRuntimeClasspath -io.opencensus:opencensus-contrib-exemplar-util:0.31.0=deploy_jar,runtimeClasspath,testRuntimeClasspath +io.opencensus:opencensus-contrib-exemplar-util:0.31.0=deploy_jar,testRuntimeClasspath +io.opencensus:opencensus-contrib-exemplar-util:0.31.1=runtimeClasspath io.opencensus:opencensus-contrib-grpc-metrics:0.31.1=deploy_jar,runtimeClasspath,testRuntimeClasspath io.opencensus:opencensus-contrib-grpc-util:0.31.1=deploy_jar,runtimeClasspath,testRuntimeClasspath io.opencensus:opencensus-contrib-http-util:0.31.1=deploy_jar,runtimeClasspath,testRuntimeClasspath -io.opencensus:opencensus-contrib-resource-util:0.31.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -io.opencensus:opencensus-exporter-metrics-util:0.31.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -io.opencensus:opencensus-exporter-stats-stackdriver:0.31.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -io.opencensus:opencensus-impl-core:0.31.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -io.opencensus:opencensus-impl:0.31.0=deploy_jar,runtimeClasspath,testRuntimeClasspath +io.opencensus:opencensus-contrib-resource-util:0.31.0=deploy_jar,testRuntimeClasspath +io.opencensus:opencensus-contrib-resource-util:0.31.1=runtimeClasspath +io.opencensus:opencensus-exporter-metrics-util:0.31.0=deploy_jar,testRuntimeClasspath +io.opencensus:opencensus-exporter-metrics-util:0.31.1=runtimeClasspath +io.opencensus:opencensus-exporter-stats-stackdriver:0.31.0=deploy_jar,testRuntimeClasspath +io.opencensus:opencensus-exporter-stats-stackdriver:0.31.1=runtimeClasspath +io.opencensus:opencensus-impl-core:0.31.0=deploy_jar,testRuntimeClasspath +io.opencensus:opencensus-impl-core:0.31.1=runtimeClasspath +io.opencensus:opencensus-impl:0.31.0=deploy_jar,testRuntimeClasspath +io.opencensus:opencensus-impl:0.31.1=runtimeClasspath io.opentelemetry.contrib:opentelemetry-gcp-resources:1.37.0-alpha=deploy_jar,runtimeClasspath,testRuntimeClasspath io.opentelemetry.instrumentation:opentelemetry-grpc-1.6:2.1.0-alpha=deploy_jar,runtimeClasspath,testRuntimeClasspath io.opentelemetry.instrumentation:opentelemetry-instrumentation-api-incubator:2.1.0-alpha=deploy_jar,runtimeClasspath,testRuntimeClasspath io.opentelemetry.instrumentation:opentelemetry-instrumentation-api:2.1.0=deploy_jar,runtimeClasspath,testRuntimeClasspath io.opentelemetry.semconv:opentelemetry-semconv:1.29.0-alpha=deploy_jar,runtimeClasspath,testRuntimeClasspath -io.opentelemetry:opentelemetry-api:1.51.0=deploy_jar,runtimeClasspath,testRuntimeClasspath +io.opentelemetry:opentelemetry-api:1.51.0=deploy_jar,testRuntimeClasspath +io.opentelemetry:opentelemetry-api:1.56.0=runtimeClasspath io.opentelemetry:opentelemetry-bom:1.42.1=deploy_jar,runtimeClasspath,testRuntimeClasspath -io.opentelemetry:opentelemetry-context:1.51.0=deploy_jar,runtimeClasspath,testRuntimeClasspath +io.opentelemetry:opentelemetry-common:1.56.0=runtimeClasspath +io.opentelemetry:opentelemetry-context:1.51.0=deploy_jar,testRuntimeClasspath +io.opentelemetry:opentelemetry-context:1.56.0=runtimeClasspath io.opentelemetry:opentelemetry-extension-incubator:1.35.0-alpha=deploy_jar,runtimeClasspath,testRuntimeClasspath io.opentelemetry:opentelemetry-sdk-common:1.51.0=deploy_jar,runtimeClasspath,testRuntimeClasspath io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi:1.51.0=deploy_jar,runtimeClasspath,testRuntimeClasspath @@ -287,22 +387,38 @@ org.antlr:antlr4:4.13.2=deploy_jar,runtimeClasspath,testRuntimeClasspath org.apache.arrow:arrow-format:17.0.0=deploy_jar,runtimeClasspath,testRuntimeClasspath org.apache.arrow:arrow-memory-core:17.0.0=deploy_jar,runtimeClasspath,testRuntimeClasspath org.apache.arrow:arrow-vector:17.0.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -org.apache.avro:avro:1.11.4=deploy_jar,runtimeClasspath,testRuntimeClasspath -org.apache.beam:beam-model-fn-execution:2.73.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -org.apache.beam:beam-model-job-management:2.73.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -org.apache.beam:beam-model-pipeline:2.73.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -org.apache.beam:beam-runners-core-java:2.73.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -org.apache.beam:beam-runners-google-cloud-dataflow-java:2.73.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -org.apache.beam:beam-runners-java-fn-execution:2.73.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -org.apache.beam:beam-sdks-java-core:2.73.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -org.apache.beam:beam-sdks-java-expansion-service:2.73.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -org.apache.beam:beam-sdks-java-extensions-arrow:2.73.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -org.apache.beam:beam-sdks-java-extensions-avro:2.73.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -org.apache.beam:beam-sdks-java-extensions-google-cloud-platform-core:2.73.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -org.apache.beam:beam-sdks-java-extensions-protobuf:2.73.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -org.apache.beam:beam-sdks-java-harness:2.73.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -org.apache.beam:beam-sdks-java-io-google-cloud-platform:2.73.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -org.apache.beam:beam-sdks-java-transform-service-launcher:2.73.0=deploy_jar,runtimeClasspath,testRuntimeClasspath +org.apache.avro:avro:1.11.4=deploy_jar,testRuntimeClasspath +org.apache.avro:avro:1.12.0=runtimeClasspath +org.apache.beam:beam-model-fn-execution:2.73.0=deploy_jar,testRuntimeClasspath +org.apache.beam:beam-model-fn-execution:2.74.0=runtimeClasspath +org.apache.beam:beam-model-job-management:2.73.0=deploy_jar,testRuntimeClasspath +org.apache.beam:beam-model-job-management:2.74.0=runtimeClasspath +org.apache.beam:beam-model-pipeline:2.73.0=deploy_jar,testRuntimeClasspath +org.apache.beam:beam-model-pipeline:2.74.0=runtimeClasspath +org.apache.beam:beam-runners-core-java:2.73.0=deploy_jar,testRuntimeClasspath +org.apache.beam:beam-runners-core-java:2.74.0=runtimeClasspath +org.apache.beam:beam-runners-google-cloud-dataflow-java:2.73.0=deploy_jar,testRuntimeClasspath +org.apache.beam:beam-runners-google-cloud-dataflow-java:2.74.0=runtimeClasspath +org.apache.beam:beam-runners-java-fn-execution:2.73.0=deploy_jar,testRuntimeClasspath +org.apache.beam:beam-runners-java-fn-execution:2.74.0=runtimeClasspath +org.apache.beam:beam-sdks-java-core:2.73.0=deploy_jar,testRuntimeClasspath +org.apache.beam:beam-sdks-java-core:2.74.0=runtimeClasspath +org.apache.beam:beam-sdks-java-expansion-service:2.73.0=deploy_jar,testRuntimeClasspath +org.apache.beam:beam-sdks-java-expansion-service:2.74.0=runtimeClasspath +org.apache.beam:beam-sdks-java-extensions-arrow:2.73.0=deploy_jar,testRuntimeClasspath +org.apache.beam:beam-sdks-java-extensions-arrow:2.74.0=runtimeClasspath +org.apache.beam:beam-sdks-java-extensions-avro:2.73.0=deploy_jar,testRuntimeClasspath +org.apache.beam:beam-sdks-java-extensions-avro:2.74.0=runtimeClasspath +org.apache.beam:beam-sdks-java-extensions-google-cloud-platform-core:2.73.0=deploy_jar,testRuntimeClasspath +org.apache.beam:beam-sdks-java-extensions-google-cloud-platform-core:2.74.0=runtimeClasspath +org.apache.beam:beam-sdks-java-extensions-protobuf:2.73.0=deploy_jar,testRuntimeClasspath +org.apache.beam:beam-sdks-java-extensions-protobuf:2.74.0=runtimeClasspath +org.apache.beam:beam-sdks-java-harness:2.73.0=deploy_jar,testRuntimeClasspath +org.apache.beam:beam-sdks-java-harness:2.74.0=runtimeClasspath +org.apache.beam:beam-sdks-java-io-google-cloud-platform:2.73.0=deploy_jar,testRuntimeClasspath +org.apache.beam:beam-sdks-java-io-google-cloud-platform:2.74.0=runtimeClasspath +org.apache.beam:beam-sdks-java-transform-service-launcher:2.73.0=deploy_jar,testRuntimeClasspath +org.apache.beam:beam-sdks-java-transform-service-launcher:2.74.0=runtimeClasspath org.apache.beam:beam-vendor-grpc-1_69_0:0.1=deploy_jar,runtimeClasspath,testRuntimeClasspath org.apache.beam:beam-vendor-guava-32_1_2-jre:0.1=deploy_jar,runtimeClasspath,testRuntimeClasspath org.apache.commons:commons-compress:1.26.2=deploy_jar,runtimeClasspath,testRuntimeClasspath @@ -340,8 +456,10 @@ org.codehaus.woodstox:stax2-api:4.2.2=deploy_jar,runtimeClasspath,testRuntimeCla org.conscrypt:conscrypt-openjdk-uber:2.5.2=deploy_jar,runtimeClasspath,testRuntimeClasspath org.eclipse.angus:angus-activation:2.1.0-M1=deploy_jar,runtimeClasspath,testRuntimeClasspath org.eclipse.angus:jakarta.mail:2.1.0-M1=deploy_jar,runtimeClasspath,testRuntimeClasspath -org.flywaydb:flyway-core:12.7.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -org.flywaydb:flyway-database-postgresql:12.7.0=deploy_jar,runtimeClasspath,testRuntimeClasspath +org.flywaydb:flyway-core:12.7.0=deploy_jar,testRuntimeClasspath +org.flywaydb:flyway-core:12.8.1=runtimeClasspath +org.flywaydb:flyway-database-postgresql:12.7.0=deploy_jar,testRuntimeClasspath +org.flywaydb:flyway-database-postgresql:12.8.1=runtimeClasspath org.freemarker:freemarker:2.3.34=deploy_jar,runtimeClasspath,testRuntimeClasspath org.glassfish.jaxb:jaxb-core:4.0.6=deploy_jar,runtimeClasspath,testRuntimeClasspath org.glassfish.jaxb:jaxb-runtime:4.0.6=deploy_jar,runtimeClasspath,testRuntimeClasspath @@ -407,12 +525,17 @@ tools.jackson.core:jackson-core:3.1.1=deploy_jar,runtimeClasspath,testRuntimeCla tools.jackson.core:jackson-databind:3.1.1=deploy_jar,runtimeClasspath,testRuntimeClasspath tools.jackson:jackson-bom:3.1.1=deploy_jar,runtimeClasspath,testRuntimeClasspath us.fatehi:schemacrawler-api:17.1.7=deploy_jar,runtimeClasspath,testRuntimeClasspath -us.fatehi:schemacrawler-diagram:17.11.1=deploy_jar,runtimeClasspath,testRuntimeClasspath -us.fatehi:schemacrawler-operations:17.11.1=deploy_jar,runtimeClasspath,testRuntimeClasspath -us.fatehi:schemacrawler-postgresql:17.11.1=deploy_jar,runtimeClasspath,testRuntimeClasspath -us.fatehi:schemacrawler-text:17.11.1=deploy_jar,runtimeClasspath,testRuntimeClasspath +us.fatehi:schemacrawler-diagram:17.11.1=deploy_jar,testRuntimeClasspath +us.fatehi:schemacrawler-diagram:17.11.2=runtimeClasspath +us.fatehi:schemacrawler-operations:17.11.1=deploy_jar,testRuntimeClasspath +us.fatehi:schemacrawler-operations:17.11.2=runtimeClasspath +us.fatehi:schemacrawler-postgresql:17.11.1=deploy_jar,testRuntimeClasspath +us.fatehi:schemacrawler-postgresql:17.11.2=runtimeClasspath +us.fatehi:schemacrawler-text:17.11.1=deploy_jar,testRuntimeClasspath +us.fatehi:schemacrawler-text:17.11.2=runtimeClasspath us.fatehi:schemacrawler-tools:17.1.7=deploy_jar,runtimeClasspath,testRuntimeClasspath us.fatehi:schemacrawler-utility:17.1.7=deploy_jar,runtimeClasspath,testRuntimeClasspath -us.fatehi:schemacrawler:17.11.1=deploy_jar,runtimeClasspath,testRuntimeClasspath +us.fatehi:schemacrawler:17.11.1=deploy_jar,testRuntimeClasspath +us.fatehi:schemacrawler:17.11.2=runtimeClasspath xerces:xmlParserAPIs:2.6.2=deploy_jar,runtimeClasspath,testRuntimeClasspath empty=compileClasspath,providedCompile,providedRuntime,shadow,testCompileClasspath diff --git a/jetty/kubernetes/nomulus-epp-server.yaml b/jetty/kubernetes/nomulus-epp-server.yaml new file mode 100644 index 00000000000..30db21b293e --- /dev/null +++ b/jetty/kubernetes/nomulus-epp-server.yaml @@ -0,0 +1,100 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: epp-server + annotations: + tag: "latest" +spec: + selector: + matchLabels: + service: epp-server + template: + metadata: + labels: + service: epp-server + spec: + serviceAccountName: nomulus + nodeSelector: + cloud.google.com/machine-family: c4 + containers: + - name: frontend + image: gcr.io/GCP_PROJECT/nomulus + ports: + - containerPort: 8080 + name: http + - containerPort: 30002 + name: epp + - containerPort: 30000 + name: health-check + resources: + requests: + # explicit pod-slots 0 is required in order to downgrade node + # class from performance, which has implicit pod-slots 1 + cloud.google.com/pod-slots: 0 + cpu: "1200m" + memory: "3Gi" + limits: + # explicit pod-slots 0 is required in order to downgrade node + # class from performance, which has implicit pod-slots 1 + cloud.google.com/pod-slots: 0 + cpu: "2000m" + memory: "6Gi" + args: [ENVIRONMENT] + env: + - name: TCP_SERVER_ENABLED + value: "true" + - name: POD_ID + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: JETTY_WORKER_INSTANCE + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: NAMESPACE_ID + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: CONTAINER_NAME + value: frontend +--- +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: epp-server +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: epp-server + minReplicas: 2 + maxReplicas: 10 + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 80 +--- +apiVersion: v1 +kind: Service +metadata: + name: epp-server + annotations: + cloud.google.com/l4-rbs: enabled + networking.gke.io/weighted-load-balancing: pods-per-node + # MUST RESERVE THESE NEW IPS IN GCP BEFORE DEPLOYING + networking.gke.io/load-balancer-ip-addresses: "EPP-v2-ipv6-main,EPP-v2-ipv4-main" +spec: + type: LoadBalancer + # Traffic is directly delivered to a node, preserving the original source IP. + externalTrafficPolicy: Local + ipFamilies: [IPv4, IPv6] + ipFamilyPolicy: RequireDualStack + selector: + service: epp-server + ports: + - port: 700 + targetPort: epp + name: epp diff --git a/jetty/src/main/webapp/WEB-INF/web.xml b/jetty/src/main/webapp/WEB-INF/web.xml index e6ba35badcb..fa3a92559f6 100644 --- a/jetty/src/main/webapp/WEB-INF/web.xml +++ b/jetty/src/main/webapp/WEB-INF/web.xml @@ -2,6 +2,14 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd" version="6.0"> + + + google.registry.module.MetricsLifecycleListener + + + google.registry.module.EppServerLifecycleListener + + diff --git a/networking/gradle.lockfile b/networking/gradle.lockfile index 5b9b784f05c..59234b50442 100644 --- a/networking/gradle.lockfile +++ b/networking/gradle.lockfile @@ -88,20 +88,32 @@ io.grpc:grpc-services:1.81.0=deploy_jar,runtimeClasspath,testRuntimeClasspath io.grpc:grpc-stub:1.81.0=deploy_jar,runtimeClasspath,testRuntimeClasspath io.grpc:grpc-util:1.81.0=deploy_jar,runtimeClasspath,testRuntimeClasspath io.grpc:grpc-xds:1.81.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -io.netty:netty-buffer:4.2.14.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-codec-base:4.2.14.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-codec-compression:4.2.14.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-codec-http:4.2.14.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-codec-marshalling:4.2.14.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-codec-protobuf:4.2.14.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-codec:4.2.14.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-common:4.2.14.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-handler:4.2.14.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-resolver:4.2.14.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-buffer:4.2.14.Final=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-buffer:4.2.15.Final=compileClasspath +io.netty:netty-codec-base:4.2.14.Final=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-codec-base:4.2.15.Final=compileClasspath +io.netty:netty-codec-compression:4.2.14.Final=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-codec-compression:4.2.15.Final=compileClasspath +io.netty:netty-codec-http:4.2.14.Final=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-codec-http:4.2.15.Final=compileClasspath +io.netty:netty-codec-marshalling:4.2.14.Final=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-codec-marshalling:4.2.15.Final=compileClasspath +io.netty:netty-codec-protobuf:4.2.14.Final=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-codec-protobuf:4.2.15.Final=compileClasspath +io.netty:netty-codec:4.2.14.Final=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-codec:4.2.15.Final=compileClasspath +io.netty:netty-common:4.2.14.Final=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-common:4.2.15.Final=compileClasspath +io.netty:netty-handler:4.2.14.Final=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-handler:4.2.15.Final=compileClasspath +io.netty:netty-resolver:4.2.14.Final=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-resolver:4.2.15.Final=compileClasspath io.netty:netty-tcnative-boringssl-static:2.0.77.Final=deploy_jar,runtimeClasspath,testRuntimeClasspath io.netty:netty-tcnative-classes:2.0.77.Final=deploy_jar,runtimeClasspath,testRuntimeClasspath -io.netty:netty-transport-native-unix-common:4.2.14.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-transport:4.2.14.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-transport-native-unix-common:4.2.14.Final=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-transport-native-unix-common:4.2.15.Final=compileClasspath +io.netty:netty-transport:4.2.14.Final=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-transport:4.2.15.Final=compileClasspath io.opencensus:opencensus-api:0.31.1=deploy_jar,runtimeClasspath,testRuntimeClasspath io.opencensus:opencensus-contrib-http-util:0.31.1=deploy_jar,runtimeClasspath,testRuntimeClasspath io.perfmark:perfmark-api:0.27.0=deploy_jar,runtimeClasspath,testRuntimeClasspath diff --git a/util/gradle.lockfile b/util/gradle.lockfile index cacd5da62b8..f970a7258af 100644 --- a/util/gradle.lockfile +++ b/util/gradle.lockfile @@ -10,24 +10,36 @@ com.github.docker-java:docker-java-transport:3.4.2=testCompileClasspath,testRunt com.github.kevinstern:software-and-algorithms:1.0=annotationProcessor,testAnnotationProcessor com.google.android:annotations:4.1.1.4=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.api-client:google-api-client:2.7.2=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.api.grpc:proto-google-cloud-tasks-v2:2.92.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.api.grpc:proto-google-cloud-tasks-v2beta2:0.182.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.api.grpc:proto-google-cloud-tasks-v2beta3:0.182.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.api.grpc:proto-google-common-protos:2.71.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.api.grpc:proto-google-iam-v1:1.66.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.api:api-common:2.63.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.api:gax-grpc:2.80.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.api:gax-httpjson:2.80.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.api:gax:2.80.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.api.grpc:proto-google-cloud-tasks-v2:2.92.0=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.api.grpc:proto-google-cloud-tasks-v2:2.93.0=compileClasspath +com.google.api.grpc:proto-google-cloud-tasks-v2beta2:0.182.0=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.api.grpc:proto-google-cloud-tasks-v2beta2:2.93.0=compileClasspath +com.google.api.grpc:proto-google-cloud-tasks-v2beta3:0.182.0=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.api.grpc:proto-google-cloud-tasks-v2beta3:2.93.0=compileClasspath +com.google.api.grpc:proto-google-common-protos:2.71.0=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.api.grpc:proto-google-common-protos:2.72.0=compileClasspath +com.google.api.grpc:proto-google-iam-v1:1.66.0=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.api.grpc:proto-google-iam-v1:1.67.0=compileClasspath +com.google.api:api-common:2.63.0=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.api:api-common:2.64.0=compileClasspath +com.google.api:gax-grpc:2.80.0=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.api:gax-grpc:2.81.0=compileClasspath +com.google.api:gax-httpjson:2.80.0=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.api:gax-httpjson:2.81.0=compileClasspath +com.google.api:gax:2.80.0=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.api:gax:2.81.0=compileClasspath com.google.apis:google-api-services-monitoring:v3-rev20260129-2.0.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.auth:google-auth-library-credentials:1.47.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.auth:google-auth-library-oauth2-http:1.47.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.auth:google-auth-library-credentials:1.47.0=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.auth:google-auth-library-credentials:1.48.0=compileClasspath +com.google.auth:google-auth-library-oauth2-http:1.47.0=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.auth:google-auth-library-oauth2-http:1.48.0=compileClasspath com.google.auto.service:auto-service-annotations:1.0.1=annotationProcessor,testAnnotationProcessor com.google.auto.value:auto-value-annotations:1.11.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.auto.value:auto-value-annotations:1.9=annotationProcessor,testAnnotationProcessor com.google.auto.value:auto-value:1.11.1=annotationProcessor,deploy_jar,runtimeClasspath,testAnnotationProcessor,testRuntimeClasspath com.google.auto:auto-common:1.2.2=annotationProcessor,testAnnotationProcessor -com.google.cloud:google-cloud-tasks:2.92.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.cloud:google-cloud-tasks:2.92.0=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.cloud:google-cloud-tasks:2.93.0=compileClasspath com.google.code.findbugs:jsr305:3.0.2=annotationProcessor,checkstyle,compileClasspath,deploy_jar,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath com.google.code.gson:gson:2.13.2=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.dagger:dagger-compiler:2.59.2=annotationProcessor,testAnnotationProcessor