Initial version Change-Id: I37f26dfb2aa6c2a5aefb14c43006ff8ffb8343ca
diff --git a/.bazelversion b/.bazelversion new file mode 100644 index 0000000..47da986 --- /dev/null +++ b/.bazelversion
@@ -0,0 +1 @@ +9.1.0
diff --git a/BUILD b/BUILD new file mode 100644 index 0000000..6cc61e9 --- /dev/null +++ b/BUILD
@@ -0,0 +1,80 @@ +load( + "@com_googlesource_gerrit_bazlets//:gerrit_plugin.bzl", + "gerrit_plugin", + "gerrit_plugin_dependency_tests", + "gerrit_plugin_ext_test_deps", + "gerrit_plugin_test_util", + "gerrit_plugin_tests", +) +load( + "@com_googlesource_gerrit_bazlets//tools:in_gerrit_tree.bzl", + "in_gerrit_tree_enabled", +) +load( + "@com_googlesource_gerrit_bazlets//tools:runtime_jars_allowlist.bzl", + "runtime_jars_allowlist_test", +) +load( + "@com_googlesource_gerrit_bazlets//tools:runtime_jars_overlap.bzl", + "runtime_jars_overlap_test", +) +load("@rules_java//java:defs.bzl", "java_library") + +java_library( + name = "high-availability-plugin-neverlink", + neverlink = True, + exports = ["//plugins/high-availability"], +) + +PLUGIN = "high-availability-gcp" + +PLUGIN_DEPS = [ + "com.google.api:api.common", + "com.google.api:gax", + "com.google.api:gax.grpc", + "com.google.api:grpc.proto.google.cloud.pubsub.v1", + "com.google.auth:google.auth.library.oauth2.http", + "com.google.cloud:google.cloud.pubsub", + "io.grpc:grpc.api", + "io.grpc:grpc.netty.shaded", +] + +gerrit_plugin( + srcs = glob(["src/main/java/**/*.java"]), + ext_deps = PLUGIN_DEPS, + license = "LICENSE", + manifest_entries = [ + "Gerrit-PluginName: %s" % PLUGIN, + "Gerrit-Module: com.googlesource.gerrit.plugins.highavailability.gcp.Module", + "Implementation-Title: high-availability gcp pubsub provider", + "Implementation-URL: https://gerrit-review.googlesource.com/#/admin/projects/plugins/%s" % PLUGIN, + ], + plugin = PLUGIN, + resources = glob(["src/main/resources/**/*"]), + deps = ["high-availability-plugin-neverlink"], +) + +PLUGIN_TEST_DEPS = PLUGIN_DEPS + [ + "com.google.http.client:google.http.client", + "org.testcontainers:gcloud", + "org.testcontainers:testcontainers", +] + +gerrit_plugin_ext_test_deps( + plugin = PLUGIN, + ext_deps = PLUGIN_TEST_DEPS, +) + +gerrit_plugin_tests( + name = "tests", + srcs = glob(["src/test/java/**/*.java"]), + ext_deps_label = ":high-availability-gcp__plugin_test_deps", + javacopts = ["-Xep:DoNotMock:OFF"], + plugin = PLUGIN, + resources = glob(["src/test/resources/**/*"]), + tags = [ + PLUGIN, + "local", + ], + deps = ["//plugins/high-availability"], +)
diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..11069ed --- /dev/null +++ b/LICENSE
@@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +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.
diff --git a/MODULE.bazel b/MODULE.bazel new file mode 100644 index 0000000..c7faf88 --- /dev/null +++ b/MODULE.bazel
@@ -0,0 +1,77 @@ +module(name = "gerrit-high-availability-gcp") + +bazel_dep(name = "rules_jvm_external", version = "6.10") +bazel_dep(name = "com_googlesource_gerrit_bazlets") +git_override( + module_name = "com_googlesource_gerrit_bazlets", + commit = "fe7c19542f8dc0a5720be3a93dbbdd05551003ec", + remote = "https://gerrit.googlesource.com/bazlets", +) + +GERRIT_API_VERS = "3.13.5" + +gerrit_api_version = use_repo_rule("@com_googlesource_gerrit_bazlets//:gerrit_api_version.bzl", "gerrit_api_version") + +gerrit_api_version( + name = "gerrit_api_version", + version = GERRIT_API_VERS, + visibility = ["//visibility:public"], +) + +maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven") +maven.install( + name = "external_plugin_deps", + artifacts = [ + "com.google.gerrit:gerrit-plugin-api:" + GERRIT_API_VERS, + "com.google.gerrit:gerrit-acceptance-framework:" + GERRIT_API_VERS, + ], + # IMPORTANT: + # Do NOT set lock_file here. + # + # This repository is intentionally shared across plugins and contains + # only Gerrit-provided APIs (gerrit-plugin-api and acceptance framework) + # that are provided by Gerrit at runtime and must not be bundled into + # the plugin JAR. + # + # When built in-tree, rules_jvm_external merges maven.install() tags + # with the same name across all modules. Defining a lock_file for this + # shared repository in more than one plugin causes multiple lock files + # to be contributed for the same repository name, which is rejected by + # rules_jvm_external during module extension evaluation. + # + # Plugin runtime dependencies must instead be declared in a + # plugin-specific repository (e.g. oauth_plugin_deps) that owns + # its own lock file. +) +use_repo(maven, "external_plugin_deps") + +maven.install( + name = "high-availability-gcp_plugin_deps", + artifacts = [ + "com.google.cloud:google-cloud-pubsub:1.141.2", + "io.grpc:grpc-netty-shaded:1.74.0", + "org.testcontainers:gcloud:1.21.3", + "org.testcontainers:testcontainers:1.21.3", + ], + duplicate_version_warning = "error", + excluded_artifacts = [ + "org.slf4j:slf4j-api", + "commons-codec:commons-codec", + "com.google.guava:failureaccess", + "com.google.code.gson:gson", + "org.apache.httpcomponents:httpclient", + "org.apache.httpcomponents:httpcore", + "com.google.j2objc:j2objc-annotations", + "com.google.code.findbugs:jsr305", + "com.google.protobuf:protobuf-java", + ], + fail_if_repin_required = True, + fail_on_missing_checksum = True, + fetch_sources = True, + lock_file = "//:high-availability-gcp_plugin_deps.lock.json", + repositories = [ + "https://repo1.maven.org/maven2", + ], + version_conflict_policy = "pinned", +) +use_repo(maven, "high-availability-gcp_plugin_deps")
diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock new file mode 100644 index 0000000..ea6318c --- /dev/null +++ b/MODULE.bazel.lock
@@ -0,0 +1,247 @@ +{ + "lockFileVersion": 26, + "registryFileHashes": { + "https://bcr.bazel.build/bazel_registry.json": "8a28e4aff06ee60aed2a8c281907fb8bcbf3b753c91fb5a5c57da3215d5b3497", + "https://bcr.bazel.build/modules/abseil-cpp/20210324.2/MODULE.bazel": "7cd0312e064fde87c8d1cd79ba06c876bd23630c83466e9500321be55c96ace2", + "https://bcr.bazel.build/modules/abseil-cpp/20211102.0/MODULE.bazel": "70390338f7a5106231d20620712f7cccb659cd0e9d073d1991c038eb9fc57589", + "https://bcr.bazel.build/modules/abseil-cpp/20230125.1/MODULE.bazel": "89047429cb0207707b2dface14ba7f8df85273d484c2572755be4bab7ce9c3a0", + "https://bcr.bazel.build/modules/abseil-cpp/20230802.0.bcr.1/MODULE.bazel": "1c8cec495288dccd14fdae6e3f95f772c1c91857047a098fad772034264cc8cb", + "https://bcr.bazel.build/modules/abseil-cpp/20230802.0/MODULE.bazel": "d253ae36a8bd9ee3c5955384096ccb6baf16a1b1e93e858370da0a3b94f77c16", + "https://bcr.bazel.build/modules/abseil-cpp/20230802.1/MODULE.bazel": "fa92e2eb41a04df73cdabeec37107316f7e5272650f81d6cc096418fe647b915", + "https://bcr.bazel.build/modules/abseil-cpp/20240116.1/MODULE.bazel": "37bcdb4440fbb61df6a1c296ae01b327f19e9bb521f9b8e26ec854b6f97309ed", + "https://bcr.bazel.build/modules/abseil-cpp/20240116.2/MODULE.bazel": "73939767a4686cd9a520d16af5ab440071ed75cec1a876bf2fcfaf1f71987a16", + "https://bcr.bazel.build/modules/abseil-cpp/20250127.0/MODULE.bazel": "d1086e248cda6576862b4b3fe9ad76a214e08c189af5b42557a6e1888812c5d5", + "https://bcr.bazel.build/modules/abseil-cpp/20250127.1/MODULE.bazel": "c4a89e7ceb9bf1e25cf84a9f830ff6b817b72874088bf5141b314726e46a57c1", + "https://bcr.bazel.build/modules/abseil-cpp/20250512.1/MODULE.bazel": "d209fdb6f36ffaf61c509fcc81b19e81b411a999a934a032e10cd009a0226215", + "https://bcr.bazel.build/modules/abseil-cpp/20250814.1/MODULE.bazel": "51f2312901470cdab0dbdf3b88c40cd21c62a7ed58a3de45b365ddc5b11bcab2", + "https://bcr.bazel.build/modules/abseil-cpp/20250814.1/source.json": "cea3901d7e299da7320700abbaafe57a65d039f10d0d7ea601c4a66938ea4b0c", + "https://bcr.bazel.build/modules/abseil-py/2.1.0/MODULE.bazel": "5ebe5bf853769c65707e5c28f216798f7a4b1042015e6a36e6d03094d94bec8a", + "https://bcr.bazel.build/modules/abseil-py/2.1.0/source.json": "0e8fc4f088ce07099c1cd6594c20c7ddbb48b4b3c0849b7d94ba94be88ff042b", + "https://bcr.bazel.build/modules/apple_support/1.11.1/MODULE.bazel": "1843d7cd8a58369a444fc6000e7304425fba600ff641592161d9f15b179fb896", + "https://bcr.bazel.build/modules/apple_support/1.15.1/MODULE.bazel": "a0556fefca0b1bb2de8567b8827518f94db6a6e7e7d632b4c48dc5f865bc7c85", + "https://bcr.bazel.build/modules/apple_support/1.21.0/MODULE.bazel": "ac1824ed5edf17dee2fdd4927ada30c9f8c3b520be1b5fd02a5da15bc10bff3e", + "https://bcr.bazel.build/modules/apple_support/1.21.1/MODULE.bazel": "5809fa3efab15d1f3c3c635af6974044bac8a4919c62238cce06acee8a8c11f1", + "https://bcr.bazel.build/modules/apple_support/1.24.2/MODULE.bazel": "0e62471818affb9f0b26f128831d5c40b074d32e6dda5a0d3852847215a41ca4", + "https://bcr.bazel.build/modules/apple_support/1.24.2/source.json": "2c22c9827093250406c5568da6c54e6fdf0ef06238def3d99c71b12feb057a8d", + "https://bcr.bazel.build/modules/bazel_features/1.1.0/MODULE.bazel": "cfd42ff3b815a5f39554d97182657f8c4b9719568eb7fded2b9135f084bf760b", + "https://bcr.bazel.build/modules/bazel_features/1.1.1/MODULE.bazel": "27b8c79ef57efe08efccbd9dd6ef70d61b4798320b8d3c134fd571f78963dbcd", + "https://bcr.bazel.build/modules/bazel_features/1.10.0/MODULE.bazel": "f75e8807570484a99be90abcd52b5e1f390362c258bcb73106f4544957a48101", + "https://bcr.bazel.build/modules/bazel_features/1.11.0/MODULE.bazel": "f9382337dd5a474c3b7d334c2f83e50b6eaedc284253334cf823044a26de03e8", + "https://bcr.bazel.build/modules/bazel_features/1.13.0/MODULE.bazel": "c14c33c7c3c730612bdbe14ebbb5e61936b6f11322ea95a6e91cd1ba962f94df", + "https://bcr.bazel.build/modules/bazel_features/1.15.0/MODULE.bazel": "d38ff6e517149dc509406aca0db3ad1efdd890a85e049585b7234d04238e2a4d", + "https://bcr.bazel.build/modules/bazel_features/1.17.0/MODULE.bazel": "039de32d21b816b47bd42c778e0454217e9c9caac4a3cf8e15c7231ee3ddee4d", + "https://bcr.bazel.build/modules/bazel_features/1.18.0/MODULE.bazel": "1be0ae2557ab3a72a57aeb31b29be347bcdc5d2b1eb1e70f39e3851a7e97041a", + "https://bcr.bazel.build/modules/bazel_features/1.19.0/MODULE.bazel": "59adcdf28230d220f0067b1f435b8537dd033bfff8db21335ef9217919c7fb58", + "https://bcr.bazel.build/modules/bazel_features/1.21.0/MODULE.bazel": "675642261665d8eea09989aa3b8afb5c37627f1be178382c320d1b46afba5e3b", + "https://bcr.bazel.build/modules/bazel_features/1.23.0/MODULE.bazel": "fd1ac84bc4e97a5a0816b7fd7d4d4f6d837b0047cf4cbd81652d616af3a6591a", + "https://bcr.bazel.build/modules/bazel_features/1.27.0/MODULE.bazel": "621eeee06c4458a9121d1f104efb80f39d34deff4984e778359c60eaf1a8cb65", + "https://bcr.bazel.build/modules/bazel_features/1.28.0/MODULE.bazel": "4b4200e6cbf8fa335b2c3f43e1d6ef3e240319c33d43d60cc0fbd4b87ece299d", + "https://bcr.bazel.build/modules/bazel_features/1.3.0/MODULE.bazel": "cdcafe83ec318cda34e02948e81d790aab8df7a929cec6f6969f13a489ccecd9", + "https://bcr.bazel.build/modules/bazel_features/1.30.0/MODULE.bazel": "a14b62d05969a293b80257e72e597c2da7f717e1e69fa8b339703ed6731bec87", + "https://bcr.bazel.build/modules/bazel_features/1.33.0/MODULE.bazel": "8b8dc9d2a4c88609409c3191165bccec0e4cb044cd7a72ccbe826583303459f6", + "https://bcr.bazel.build/modules/bazel_features/1.4.1/MODULE.bazel": "e45b6bb2350aff3e442ae1111c555e27eac1d915e77775f6fdc4b351b758b5d7", + "https://bcr.bazel.build/modules/bazel_features/1.42.1/MODULE.bazel": "275a59b5406ff18c01739860aa70ad7ccb3cfb474579411decca11c93b951080", + "https://bcr.bazel.build/modules/bazel_features/1.42.1/source.json": "fcd4396b2df85f64f2b3bb436ad870793ecf39180f1d796f913cc9276d355309", + "https://bcr.bazel.build/modules/bazel_features/1.9.1/MODULE.bazel": "8f679097876a9b609ad1f60249c49d68bfab783dd9be012faf9d82547b14815a", + "https://bcr.bazel.build/modules/bazel_skylib/1.0.3/MODULE.bazel": "bcb0fd896384802d1ad283b4e4eb4d718eebd8cb820b0a2c3a347fb971afd9d8", + "https://bcr.bazel.build/modules/bazel_skylib/1.1.1/MODULE.bazel": "1add3e7d93ff2e6998f9e118022c84d163917d912f5afafb3058e3d2f1545b5e", + "https://bcr.bazel.build/modules/bazel_skylib/1.2.0/MODULE.bazel": "44fe84260e454ed94ad326352a698422dbe372b21a1ac9f3eab76eb531223686", + "https://bcr.bazel.build/modules/bazel_skylib/1.2.1/MODULE.bazel": "f35baf9da0efe45fa3da1696ae906eea3d615ad41e2e3def4aeb4e8bc0ef9a7a", + "https://bcr.bazel.build/modules/bazel_skylib/1.3.0/MODULE.bazel": "20228b92868bf5cfc41bda7afc8a8ba2a543201851de39d990ec957b513579c5", + "https://bcr.bazel.build/modules/bazel_skylib/1.4.1/MODULE.bazel": "a0dcb779424be33100dcae821e9e27e4f2901d9dfd5333efe5ac6a8d7ab75e1d", + "https://bcr.bazel.build/modules/bazel_skylib/1.4.2/MODULE.bazel": "3bd40978e7a1fac911d5989e6b09d8f64921865a45822d8b09e815eaa726a651", + "https://bcr.bazel.build/modules/bazel_skylib/1.5.0/MODULE.bazel": "32880f5e2945ce6a03d1fbd588e9198c0a959bb42297b2cfaf1685b7bc32e138", + "https://bcr.bazel.build/modules/bazel_skylib/1.6.1/MODULE.bazel": "8fdee2dbaace6c252131c00e1de4b165dc65af02ea278476187765e1a617b917", + "https://bcr.bazel.build/modules/bazel_skylib/1.7.0/MODULE.bazel": "0db596f4563de7938de764cc8deeabec291f55e8ec15299718b93c4423e9796d", + "https://bcr.bazel.build/modules/bazel_skylib/1.7.1/MODULE.bazel": "3120d80c5861aa616222ec015332e5f8d3171e062e3e804a2a0253e1be26e59b", + "https://bcr.bazel.build/modules/bazel_skylib/1.8.1/MODULE.bazel": "88ade7293becda963e0e3ea33e7d54d3425127e0a326e0d17da085a5f1f03ff6", + "https://bcr.bazel.build/modules/bazel_skylib/1.8.2/MODULE.bazel": "69ad6927098316848b34a9142bcc975e018ba27f08c4ff403f50c1b6e646ca67", + "https://bcr.bazel.build/modules/bazel_skylib/1.9.0/MODULE.bazel": "72997b29dfd95c3fa0d0c48322d05590418edef451f8db8db5509c57875fb4b7", + "https://bcr.bazel.build/modules/bazel_skylib/1.9.0/source.json": "7ad77c1e8c1b84222d9b3f3cae016a76639435744c19330b0b37c0a3c9da7dc0", + "https://bcr.bazel.build/modules/bazel_worker_api/0.0.1/MODULE.bazel": "02a13b77321773b2042e70ee5e4c5e099c8ddee4cf2da9cd420442c36938d4bd", + "https://bcr.bazel.build/modules/bazel_worker_api/0.0.4/MODULE.bazel": "460aa12d01231a80cce03c548287b433b321d205b0028ae596728c35e5ee442e", + "https://bcr.bazel.build/modules/bazel_worker_api/0.0.4/source.json": "d353c410d47a8b65d09fa98e83d57ebec257a2c2b9c6e42d6fda1cb25e5464a5", + "https://bcr.bazel.build/modules/bazel_worker_java/0.0.4/MODULE.bazel": "82494a01018bb7ef06d4a17ec4cd7a758721f10eb8b6c820a818e70d669500db", + "https://bcr.bazel.build/modules/bazel_worker_java/0.0.4/source.json": "a2d30458fd86cf022c2b6331e652526fa08e17573b2f5034a9dbcacdf9c2583c", + "https://bcr.bazel.build/modules/buildozer/8.5.1/MODULE.bazel": "a35d9561b3fc5b18797c330793e99e3b834a473d5fbd3d7d7634aafc9bdb6f8f", + "https://bcr.bazel.build/modules/buildozer/8.5.1/source.json": "e3386e6ff4529f2442800dee47ad28d3e6487f36a1f75ae39ae56c70f0cd2fbd", + "https://bcr.bazel.build/modules/gazelle/0.32.0/MODULE.bazel": "b499f58a5d0d3537f3cf5b76d8ada18242f64ec474d8391247438bf04f58c7b8", + "https://bcr.bazel.build/modules/gazelle/0.33.0/MODULE.bazel": "a13a0f279b462b784fb8dd52a4074526c4a2afe70e114c7d09066097a46b3350", + "https://bcr.bazel.build/modules/gazelle/0.34.0/MODULE.bazel": "abdd8ce4d70978933209db92e436deb3a8b737859e9354fb5fd11fb5c2004c8a", + "https://bcr.bazel.build/modules/gazelle/0.36.0/MODULE.bazel": "e375d5d6e9a6ca59b0cb38b0540bc9a05b6aa926d322f2de268ad267a2ee74c0", + "https://bcr.bazel.build/modules/gazelle/0.40.0/MODULE.bazel": "42ba5378ebe845fca43989a53186ab436d956db498acde790685fe0e8f9c6146", + "https://bcr.bazel.build/modules/gazelle/0.40.0/source.json": "1e5ef6e4d8b9b6836d93273c781e78ff829ea2e077afef7a57298040fa4f010a", + "https://bcr.bazel.build/modules/google_benchmark/1.8.2/MODULE.bazel": "a70cf1bba851000ba93b58ae2f6d76490a9feb74192e57ab8e8ff13c34ec50cb", + "https://bcr.bazel.build/modules/googletest/1.11.0/MODULE.bazel": "3a83f095183f66345ca86aa13c58b59f9f94a2f81999c093d4eeaa2d262d12f4", + "https://bcr.bazel.build/modules/googletest/1.14.0.bcr.1/MODULE.bazel": "22c31a561553727960057361aa33bf20fb2e98584bc4fec007906e27053f80c6", + "https://bcr.bazel.build/modules/googletest/1.14.0/MODULE.bazel": "cfbcbf3e6eac06ef9d85900f64424708cc08687d1b527f0ef65aa7517af8118f", + "https://bcr.bazel.build/modules/googletest/1.15.2/MODULE.bazel": "6de1edc1d26cafb0ea1a6ab3f4d4192d91a312fd2d360b63adaa213cd00b2108", + "https://bcr.bazel.build/modules/googletest/1.17.0/MODULE.bazel": "dbec758171594a705933a29fcf69293d2468c49ec1f2ebca65c36f504d72df46", + "https://bcr.bazel.build/modules/googletest/1.17.0/source.json": "38e4454b25fc30f15439c0378e57909ab1fd0a443158aa35aec685da727cd713", + "https://bcr.bazel.build/modules/jsoncpp/1.9.5/MODULE.bazel": "31271aedc59e815656f5736f282bb7509a97c7ecb43e927ac1a37966e0578075", + "https://bcr.bazel.build/modules/jsoncpp/1.9.6/MODULE.bazel": "2f8d20d3b7d54143213c4dfc3d98225c42de7d666011528dc8fe91591e2e17b0", + "https://bcr.bazel.build/modules/jsoncpp/1.9.6/source.json": "a04756d367a2126c3541682864ecec52f92cdee80a35735a3cb249ce015ca000", + "https://bcr.bazel.build/modules/libpfm/4.11.0/MODULE.bazel": "45061ff025b301940f1e30d2c16bea596c25b176c8b6b3087e92615adbd52902", + "https://bcr.bazel.build/modules/nlohmann_json/3.6.1/MODULE.bazel": "6f7b417dcc794d9add9e556673ad25cb3ba835224290f4f848f8e2db1e1fca74", + "https://bcr.bazel.build/modules/nlohmann_json/3.6.1/source.json": "f448c6e8963fdfa7eb831457df83ad63d3d6355018f6574fb017e8169deb43a9", + "https://bcr.bazel.build/modules/package_metadata/0.0.7/MODULE.bazel": "7adb03933fc8401f495800cf4eafcff0edc6da0ff55c7db223ef69d19f689486", + "https://bcr.bazel.build/modules/package_metadata/0.0.7/source.json": "50639625e937b56115012674c797cca7a05a96b4878c87d803c13dc2b31de8a0", + "https://bcr.bazel.build/modules/platforms/0.0.10/MODULE.bazel": "8cb8efaf200bdeb2150d93e162c40f388529a25852b332cec879373771e48ed5", + "https://bcr.bazel.build/modules/platforms/0.0.11/MODULE.bazel": "0daefc49732e227caa8bfa834d65dc52e8cc18a2faf80df25e8caea151a9413f", + "https://bcr.bazel.build/modules/platforms/0.0.4/MODULE.bazel": "9b328e31ee156f53f3c416a64f8491f7eb731742655a47c9eec4703a71644aee", + "https://bcr.bazel.build/modules/platforms/0.0.5/MODULE.bazel": "5733b54ea419d5eaf7997054bb55f6a1d0b5ff8aedf0176fef9eea44f3acda37", + "https://bcr.bazel.build/modules/platforms/0.0.6/MODULE.bazel": "ad6eeef431dc52aefd2d77ed20a4b353f8ebf0f4ecdd26a807d2da5aa8cd0615", + "https://bcr.bazel.build/modules/platforms/0.0.7/MODULE.bazel": "72fd4a0ede9ee5c021f6a8dd92b503e089f46c227ba2813ff183b71616034814", + "https://bcr.bazel.build/modules/platforms/0.0.8/MODULE.bazel": "9f142c03e348f6d263719f5074b21ef3adf0b139ee4c5133e2aa35664da9eb2d", + "https://bcr.bazel.build/modules/platforms/0.0.9/MODULE.bazel": "4a87a60c927b56ddd67db50c89acaa62f4ce2a1d2149ccb63ffd871d5ce29ebc", + "https://bcr.bazel.build/modules/platforms/1.0.0/MODULE.bazel": "f05feb42b48f1b3c225e4ccf351f367be0371411a803198ec34a389fb22aa580", + "https://bcr.bazel.build/modules/platforms/1.0.0/source.json": "f4ff1fd412e0246fd38c82328eb209130ead81d62dcd5a9e40910f867f733d96", + "https://bcr.bazel.build/modules/protobuf/21.7/MODULE.bazel": "a5a29bb89544f9b97edce05642fac225a808b5b7be74038ea3640fae2f8e66a7", + "https://bcr.bazel.build/modules/protobuf/23.1/MODULE.bazel": "88b393b3eb4101d18129e5db51847cd40a5517a53e81216144a8c32dfeeca52a", + "https://bcr.bazel.build/modules/protobuf/24.4/MODULE.bazel": "7bc7ce5f2abf36b3b7b7c8218d3acdebb9426aeb35c2257c96445756f970eb12", + "https://bcr.bazel.build/modules/protobuf/27.0/MODULE.bazel": "7873b60be88844a0a1d8f80b9d5d20cfbd8495a689b8763e76c6372998d3f64c", + "https://bcr.bazel.build/modules/protobuf/27.1/MODULE.bazel": "703a7b614728bb06647f965264967a8ef1c39e09e8f167b3ca0bb1fd80449c0d", + "https://bcr.bazel.build/modules/protobuf/27.2/MODULE.bazel": "32450b50673882e4c8c3d10a83f3bc82161b213ed2f80d17e38bece8f165c295", + "https://bcr.bazel.build/modules/protobuf/29.0-rc2/MODULE.bazel": "6241d35983510143049943fc0d57937937122baf1b287862f9dc8590fc4c37df", + "https://bcr.bazel.build/modules/protobuf/29.0-rc3/MODULE.bazel": "33c2dfa286578573afc55a7acaea3cada4122b9631007c594bf0729f41c8de92", + "https://bcr.bazel.build/modules/protobuf/29.1/MODULE.bazel": "557c3457560ff49e122ed76c0bc3397a64af9574691cb8201b4e46d4ab2ecb95", + "https://bcr.bazel.build/modules/protobuf/3.19.0/MODULE.bazel": "6b5fbb433f760a99a22b18b6850ed5784ef0e9928a72668b66e4d7ccd47db9b0", + "https://bcr.bazel.build/modules/protobuf/3.19.2/MODULE.bazel": "532ffe5f2186b69fdde039efe6df13ba726ff338c6bc82275ad433013fa10573", + "https://bcr.bazel.build/modules/protobuf/3.19.6/MODULE.bazel": "9233edc5e1f2ee276a60de3eaa47ac4132302ef9643238f23128fea53ea12858", + "https://bcr.bazel.build/modules/protobuf/31.1/MODULE.bazel": "379a389bb330b7b8c1cdf331cc90bf3e13de5614799b3b52cdb7c6f389f6b38e", + "https://bcr.bazel.build/modules/protobuf/32.1/MODULE.bazel": "89cd2866a9cb07fee9ff74c41ceace11554f32e0d849de4e23ac55515cfada4d", + "https://bcr.bazel.build/modules/protobuf/33.4/MODULE.bazel": "114775b816b38b6d0ca620450d6b02550c60ceedfdc8d9a229833b34a223dc42", + "https://bcr.bazel.build/modules/protobuf/33.4/source.json": "555f8686b4c7d6b5ba731fbea13bf656b4bfd9a7ff629c1d9d3f6e1d6155de79", + "https://bcr.bazel.build/modules/pybind11_bazel/2.11.1/MODULE.bazel": "88af1c246226d87e65be78ed49ecd1e6f5e98648558c14ce99176da041dc378e", + "https://bcr.bazel.build/modules/pybind11_bazel/2.12.0/MODULE.bazel": "e6f4c20442eaa7c90d7190d8dc539d0ab422f95c65a57cc59562170c58ae3d34", + "https://bcr.bazel.build/modules/pybind11_bazel/2.12.0/source.json": "6900fdc8a9e95866b8c0d4ad4aba4d4236317b5c1cd04c502df3f0d33afed680", + "https://bcr.bazel.build/modules/re2/2023-09-01/MODULE.bazel": "cb3d511531b16cfc78a225a9e2136007a48cf8a677e4264baeab57fe78a80206", + "https://bcr.bazel.build/modules/re2/2024-07-02.bcr.1/MODULE.bazel": "b4963dda9b31080be1905ef085ecd7dd6cd47c05c79b9cdf83ade83ab2ab271a", + "https://bcr.bazel.build/modules/re2/2024-07-02.bcr.1/source.json": "2ff292be6ef3340325ce8a045ecc326e92cbfab47c7cbab4bd85d28971b97ac4", + "https://bcr.bazel.build/modules/re2/2024-07-02/MODULE.bazel": "0eadc4395959969297cbcf31a249ff457f2f1d456228c67719480205aa306daa", + "https://bcr.bazel.build/modules/rules_android/0.1.1/MODULE.bazel": "48809ab0091b07ad0182defb787c4c5328bd3a278938415c00a7b69b50c4d3a8", + "https://bcr.bazel.build/modules/rules_android/0.6.6/MODULE.bazel": "b0fb569752aab65ab1a9db0a8f6cfaf5aa1754965e17e95dcf0e4d88e192a68d", + "https://bcr.bazel.build/modules/rules_android/0.6.6/source.json": "a9d8dc2d5a102dc03269a94acc886a4cab82cdcb9ccbc77b0f665d6d17a6ae09", + "https://bcr.bazel.build/modules/rules_apple/3.16.0/MODULE.bazel": "0d1caf0b8375942ce98ea944be754a18874041e4e0459401d925577624d3a54a", + "https://bcr.bazel.build/modules/rules_apple/4.1.0/MODULE.bazel": "76e10fd4a48038d3fc7c5dc6e63b7063bbf5304a2e3bd42edda6ec660eebea68", + "https://bcr.bazel.build/modules/rules_apple/4.1.0/source.json": "8ee81e1708756f81b343a5eb2b2f0b953f1d25c4ab3d4a68dc02754872e80715", + "https://bcr.bazel.build/modules/rules_cc/0.0.1/MODULE.bazel": "cb2aa0747f84c6c3a78dad4e2049c154f08ab9d166b1273835a8174940365647", + "https://bcr.bazel.build/modules/rules_cc/0.0.10/MODULE.bazel": "ec1705118f7eaedd6e118508d3d26deba2a4e76476ada7e0e3965211be012002", + "https://bcr.bazel.build/modules/rules_cc/0.0.13/MODULE.bazel": "0e8529ed7b323dad0775ff924d2ae5af7640b23553dfcd4d34344c7e7a867191", + "https://bcr.bazel.build/modules/rules_cc/0.0.15/MODULE.bazel": "6704c35f7b4a72502ee81f61bf88706b54f06b3cbe5558ac17e2e14666cd5dcc", + "https://bcr.bazel.build/modules/rules_cc/0.0.16/MODULE.bazel": "7661303b8fc1b4d7f532e54e9d6565771fea666fbdf839e0a86affcd02defe87", + "https://bcr.bazel.build/modules/rules_cc/0.0.17/MODULE.bazel": "2ae1d8f4238ec67d7185d8861cb0a2cdf4bc608697c331b95bf990e69b62e64a", + "https://bcr.bazel.build/modules/rules_cc/0.0.2/MODULE.bazel": "6915987c90970493ab97393024c156ea8fb9f3bea953b2f3ec05c34f19b5695c", + "https://bcr.bazel.build/modules/rules_cc/0.0.6/MODULE.bazel": "abf360251023dfe3efcef65ab9d56beefa8394d4176dd29529750e1c57eaa33f", + "https://bcr.bazel.build/modules/rules_cc/0.0.8/MODULE.bazel": "964c85c82cfeb6f3855e6a07054fdb159aced38e99a5eecf7bce9d53990afa3e", + "https://bcr.bazel.build/modules/rules_cc/0.0.9/MODULE.bazel": "836e76439f354b89afe6a911a7adf59a6b2518fafb174483ad78a2a2fde7b1c5", + "https://bcr.bazel.build/modules/rules_cc/0.1.1/MODULE.bazel": "2f0222a6f229f0bf44cd711dc13c858dad98c62d52bd51d8fc3a764a83125513", + "https://bcr.bazel.build/modules/rules_cc/0.1.2/MODULE.bazel": "557ddc3a96858ec0d465a87c0a931054d7dcfd6583af2c7ed3baf494407fd8d0", + "https://bcr.bazel.build/modules/rules_cc/0.1.5/MODULE.bazel": "88dfc9361e8b5ae1008ac38f7cdfd45ad738e4fa676a3ad67d19204f045a1fd8", + "https://bcr.bazel.build/modules/rules_cc/0.2.0/MODULE.bazel": "b5c17f90458caae90d2ccd114c81970062946f49f355610ed89bebf954f5783c", + "https://bcr.bazel.build/modules/rules_cc/0.2.13/MODULE.bazel": "eecdd666eda6be16a8d9dc15e44b5c75133405e820f620a234acc4b1fdc5aa37", + "https://bcr.bazel.build/modules/rules_cc/0.2.17/MODULE.bazel": "1849602c86cb60da8613d2de887f9566a6d354a6df6d7009f9d04a14402f9a84", + "https://bcr.bazel.build/modules/rules_cc/0.2.17/source.json": "3832f45d145354049137c0090df04629d9c2b5493dc5c2bf46f1834040133a07", + "https://bcr.bazel.build/modules/rules_cc/0.2.8/MODULE.bazel": "f1df20f0bf22c28192a794f29b501ee2018fa37a3862a1a2132ae2940a23a642", + "https://bcr.bazel.build/modules/rules_foreign_cc/0.9.0/MODULE.bazel": "c9e8c682bf75b0e7c704166d79b599f93b72cfca5ad7477df596947891feeef6", + "https://bcr.bazel.build/modules/rules_fuzzing/0.5.2/MODULE.bazel": "40c97d1144356f52905566c55811f13b299453a14ac7769dfba2ac38192337a8", + "https://bcr.bazel.build/modules/rules_go/0.41.0/MODULE.bazel": "55861d8e8bb0e62cbd2896f60ff303f62ffcb0eddb74ecb0e5c0cbe36fc292c8", + "https://bcr.bazel.build/modules/rules_go/0.42.0/MODULE.bazel": "8cfa875b9aa8c6fce2b2e5925e73c1388173ea3c32a0db4d2b4804b453c14270", + "https://bcr.bazel.build/modules/rules_go/0.46.0/MODULE.bazel": "3477df8bdcc49e698b9d25f734c4f3a9f5931ff34ee48a2c662be168f5f2d3fd", + "https://bcr.bazel.build/modules/rules_go/0.50.1/MODULE.bazel": "b91a308dc5782bb0a8021ad4330c81fea5bda77f96b9e4c117b9b9c8f6665ee0", + "https://bcr.bazel.build/modules/rules_go/0.51.0-rc2/MODULE.bazel": "edfc3a9cea7bedb0eaaff37b0d7817c1a4bf72b3c615580b0ffcee6c52690fd4", + "https://bcr.bazel.build/modules/rules_go/0.51.0-rc2/source.json": "6b5cd0b3da2bd0e6949580851db990a04af0a285f072b9a0f059424457cd8cc9", + "https://bcr.bazel.build/modules/rules_java/4.0.0/MODULE.bazel": "5a78a7ae82cd1a33cef56dc578c7d2a46ed0dca12643ee45edbb8417899e6f74", + "https://bcr.bazel.build/modules/rules_java/5.3.5/MODULE.bazel": "a4ec4f2db570171e3e5eb753276ee4b389bae16b96207e9d3230895c99644b86", + "https://bcr.bazel.build/modules/rules_java/6.3.0/MODULE.bazel": "a97c7678c19f236a956ad260d59c86e10a463badb7eb2eda787490f4c969b963", + "https://bcr.bazel.build/modules/rules_java/6.5.2/MODULE.bazel": "1d440d262d0e08453fa0c4d8f699ba81609ed0e9a9a0f02cd10b3e7942e61e31", + "https://bcr.bazel.build/modules/rules_java/7.1.0/MODULE.bazel": "30d9135a2b6561c761bd67bd4990da591e6bdc128790ce3e7afd6a3558b2fb64", + "https://bcr.bazel.build/modules/rules_java/7.10.0/MODULE.bazel": "530c3beb3067e870561739f1144329a21c851ff771cd752a49e06e3dc9c2e71a", + "https://bcr.bazel.build/modules/rules_java/7.12.2/MODULE.bazel": "579c505165ee757a4280ef83cda0150eea193eed3bef50b1004ba88b99da6de6", + "https://bcr.bazel.build/modules/rules_java/7.2.0/MODULE.bazel": "06c0334c9be61e6cef2c8c84a7800cef502063269a5af25ceb100b192453d4ab", + "https://bcr.bazel.build/modules/rules_java/7.4.0/MODULE.bazel": "a592852f8a3dd539e82ee6542013bf2cadfc4c6946be8941e189d224500a8934", + "https://bcr.bazel.build/modules/rules_java/7.6.1/MODULE.bazel": "2f14b7e8a1aa2f67ae92bc69d1ec0fa8d9f827c4e17ff5e5f02e91caa3b2d0fe", + "https://bcr.bazel.build/modules/rules_java/8.13.0/MODULE.bazel": "0444ebf737d144cf2bb2ccb368e7f1cce735264285f2a3711785827c1686625e", + "https://bcr.bazel.build/modules/rules_java/8.3.2/MODULE.bazel": "7336d5511ad5af0b8615fdc7477535a2e4e723a357b6713af439fe8cf0195017", + "https://bcr.bazel.build/modules/rules_java/8.5.1/MODULE.bazel": "d8a9e38cc5228881f7055a6079f6f7821a073df3744d441978e7a43e20226939", + "https://bcr.bazel.build/modules/rules_java/8.6.0/MODULE.bazel": "9c064c434606d75a086f15ade5edb514308cccd1544c2b2a89bbac4310e41c71", + "https://bcr.bazel.build/modules/rules_java/8.6.1/MODULE.bazel": "f4808e2ab5b0197f094cabce9f4b006a27766beb6a9975931da07099560ca9c2", + "https://bcr.bazel.build/modules/rules_java/9.1.0/MODULE.bazel": "ee63f27e36a3fada80342869361182f120a9819c74320e8e65b1e04ba0cd7a9d", + "https://bcr.bazel.build/modules/rules_java/9.1.0/source.json": "da589573c1dee2c9ac4a568b301269a2e8191110ff0345c1a959fa7ea6c4dfd6", + "https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/MODULE.bazel": "a56b85e418c83eb1839819f0b515c431010160383306d13ec21959ac412d2fe7", + "https://bcr.bazel.build/modules/rules_jvm_external/5.1/MODULE.bazel": "33f6f999e03183f7d088c9be518a63467dfd0be94a11d0055fe2d210f89aa909", + "https://bcr.bazel.build/modules/rules_jvm_external/5.2/MODULE.bazel": "d9351ba35217ad0de03816ef3ed63f89d411349353077348a45348b096615036", + "https://bcr.bazel.build/modules/rules_jvm_external/6.10/MODULE.bazel": "33e636ca6bc9ee0fa090a38aa33c631ded2d8cf6fead4124181d1b35dc474f7c", + "https://bcr.bazel.build/modules/rules_jvm_external/6.10/source.json": "c191249787625db72616a3fb3cc2786ab57355a2e3b615402b8b3b66b0f995b7", + "https://bcr.bazel.build/modules/rules_jvm_external/6.2/MODULE.bazel": "36a6e52487a855f33cb960724eb56547fa87e2c98a0474c3acad94339d7f8e99", + "https://bcr.bazel.build/modules/rules_jvm_external/6.3/MODULE.bazel": "c998e060b85f71e00de5ec552019347c8bca255062c990ac02d051bb80a38df0", + "https://bcr.bazel.build/modules/rules_jvm_external/6.6/MODULE.bazel": "153042249c7060536dc95b6bb9f9bb8063b8a0b0cb7acdb381bddbc2374aed55", + "https://bcr.bazel.build/modules/rules_jvm_external/6.7/MODULE.bazel": "e717beabc4d091ecb2c803c2d341b88590e9116b8bf7947915eeb33aab4f96dd", + "https://bcr.bazel.build/modules/rules_kotlin/1.9.5/MODULE.bazel": "043a16a572f610558ec2030db3ff0c9938574e7dd9f58bded1bb07c0192ef025", + "https://bcr.bazel.build/modules/rules_kotlin/1.9.6/MODULE.bazel": "d269a01a18ee74d0335450b10f62c9ed81f2321d7958a2934e44272fe82dcef3", + "https://bcr.bazel.build/modules/rules_kotlin/2.1.3/MODULE.bazel": "ce7def6d576aa8d3a9c6d10e13b4d157296229674371f67dbf788dae0afae3d5", + "https://bcr.bazel.build/modules/rules_kotlin/2.1.3/source.json": "0b0dc9400f14b5fbb13d278ad3bf0413cdbaf0da0db337e055b855e35b878a3b", + "https://bcr.bazel.build/modules/rules_license/0.0.3/MODULE.bazel": "627e9ab0247f7d1e05736b59dbb1b6871373de5ad31c3011880b4133cafd4bd0", + "https://bcr.bazel.build/modules/rules_license/0.0.7/MODULE.bazel": "088fbeb0b6a419005b89cf93fe62d9517c0a2b8bb56af3244af65ecfe37e7d5d", + "https://bcr.bazel.build/modules/rules_license/1.0.0/MODULE.bazel": "a7fda60eefdf3d8c827262ba499957e4df06f659330bbe6cdbdb975b768bb65c", + "https://bcr.bazel.build/modules/rules_license/1.0.0/source.json": "a52c89e54cc311196e478f8382df91c15f7a2bfdf4c6cd0e2675cc2ff0b56efb", + "https://bcr.bazel.build/modules/rules_pkg/0.7.0/MODULE.bazel": "df99f03fc7934a4737122518bb87e667e62d780b610910f0447665a7e2be62dc", + "https://bcr.bazel.build/modules/rules_pkg/1.0.1/MODULE.bazel": "5b1df97dbc29623bccdf2b0dcd0f5cb08e2f2c9050aab1092fd39a41e82686ff", + "https://bcr.bazel.build/modules/rules_pkg/1.0.1/source.json": "bd82e5d7b9ce2d31e380dd9f50c111d678c3bdaca190cb76b0e1c71b05e1ba8a", + "https://bcr.bazel.build/modules/rules_proto/4.0.0/MODULE.bazel": "a7a7b6ce9bee418c1a760b3d84f83a299ad6952f9903c67f19e4edd964894e06", + "https://bcr.bazel.build/modules/rules_proto/5.3.0-21.7/MODULE.bazel": "e8dff86b0971688790ae75528fe1813f71809b5afd57facb44dad9e8eca631b7", + "https://bcr.bazel.build/modules/rules_proto/6.0.0-rc1/MODULE.bazel": "1e5b502e2e1a9e825eef74476a5a1ee524a92297085015a052510b09a1a09483", + "https://bcr.bazel.build/modules/rules_proto/6.0.0/MODULE.bazel": "b531d7f09f58dce456cd61b4579ce8c86b38544da75184eadaf0a7cb7966453f", + "https://bcr.bazel.build/modules/rules_proto/6.0.2/MODULE.bazel": "ce916b775a62b90b61888052a416ccdda405212b6aaeb39522f7dc53431a5e73", + "https://bcr.bazel.build/modules/rules_proto/7.0.2/MODULE.bazel": "bf81793bd6d2ad89a37a40693e56c61b0ee30f7a7fdbaf3eabbf5f39de47dea2", + "https://bcr.bazel.build/modules/rules_proto/7.1.0/MODULE.bazel": "002d62d9108f75bb807cd56245d45648f38275cb3a99dcd45dfb864c5d74cb96", + "https://bcr.bazel.build/modules/rules_proto/7.1.0/source.json": "39f89066c12c24097854e8f57ab8558929f9c8d474d34b2c00ac04630ad8940e", + "https://bcr.bazel.build/modules/rules_python/0.10.2/MODULE.bazel": "cc82bc96f2997baa545ab3ce73f196d040ffb8756fd2d66125a530031cd90e5f", + "https://bcr.bazel.build/modules/rules_python/0.23.1/MODULE.bazel": "49ffccf0511cb8414de28321f5fcf2a31312b47c40cc21577144b7447f2bf300", + "https://bcr.bazel.build/modules/rules_python/0.25.0/MODULE.bazel": "72f1506841c920a1afec76975b35312410eea3aa7b63267436bfb1dd91d2d382", + "https://bcr.bazel.build/modules/rules_python/0.28.0/MODULE.bazel": "cba2573d870babc976664a912539b320cbaa7114cd3e8f053c720171cde331ed", + "https://bcr.bazel.build/modules/rules_python/0.31.0/MODULE.bazel": "93a43dc47ee570e6ec9f5779b2e64c1476a6ce921c48cc9a1678a91dd5f8fd58", + "https://bcr.bazel.build/modules/rules_python/0.33.2/MODULE.bazel": "3e036c4ad8d804a4dad897d333d8dce200d943df4827cb849840055be8d2e937", + "https://bcr.bazel.build/modules/rules_python/0.37.1/MODULE.bazel": "3faeb2d9fa0a81f8980643ee33f212308f4d93eea4b9ce6f36d0b742e71e9500", + "https://bcr.bazel.build/modules/rules_python/0.37.2/MODULE.bazel": "b5ffde91410745750b6c13be1c5dc4555ef5bc50562af4a89fd77807fdde626a", + "https://bcr.bazel.build/modules/rules_python/0.4.0/MODULE.bazel": "9208ee05fd48bf09ac60ed269791cf17fb343db56c8226a720fbb1cdf467166c", + "https://bcr.bazel.build/modules/rules_python/1.0.0/MODULE.bazel": "898a3d999c22caa585eb062b600f88654bf92efb204fa346fb55f6f8edffca43", + "https://bcr.bazel.build/modules/rules_python/1.3.0/MODULE.bazel": "8361d57eafb67c09b75bf4bbe6be360e1b8f4f18118ab48037f2bd50aa2ccb13", + "https://bcr.bazel.build/modules/rules_python/1.4.1/MODULE.bazel": "8991ad45bdc25018301d6b7e1d3626afc3c8af8aaf4bc04f23d0b99c938b73a6", + "https://bcr.bazel.build/modules/rules_python/1.6.0/MODULE.bazel": "7e04ad8f8d5bea40451cf80b1bd8262552aa73f841415d20db96b7241bd027d8", + "https://bcr.bazel.build/modules/rules_python/1.7.0/MODULE.bazel": "d01f995ecd137abf30238ad9ce97f8fc3ac57289c8b24bd0bf53324d937a14f8", + "https://bcr.bazel.build/modules/rules_python/1.7.0/source.json": "028a084b65dcf8f4dc4f82f8778dbe65df133f234b316828a82e060d81bdce32", + "https://bcr.bazel.build/modules/rules_robolectric/4.14.1.2/MODULE.bazel": "d44fec647d0aeb67b9f3b980cf68ba634976f3ae7ccd6c07d790b59b87a4f251", + "https://bcr.bazel.build/modules/rules_robolectric/4.14.1.2/source.json": "37c10335f2361c337c5c1f34ed36d2da70534c23088062b33a8bdaab68aa9dea", + "https://bcr.bazel.build/modules/rules_shell/0.1.2/MODULE.bazel": "66e4ca3ce084b04af0b9ff05ff14cab4e5df7503973818bb91cbc6cda08d32fc", + "https://bcr.bazel.build/modules/rules_shell/0.2.0/MODULE.bazel": "fda8a652ab3c7d8fee214de05e7a9916d8b28082234e8d2c0094505c5268ed3c", + "https://bcr.bazel.build/modules/rules_shell/0.3.0/MODULE.bazel": "de4402cd12f4cc8fda2354fce179fdb068c0b9ca1ec2d2b17b3e21b24c1a937b", + "https://bcr.bazel.build/modules/rules_shell/0.4.1/MODULE.bazel": "00e501db01bbf4e3e1dd1595959092c2fadf2087b2852d3f553b5370f5633592", + "https://bcr.bazel.build/modules/rules_shell/0.6.1/MODULE.bazel": "72e76b0eea4e81611ef5452aa82b3da34caca0c8b7b5c0c9584338aa93bae26b", + "https://bcr.bazel.build/modules/rules_shell/0.6.1/source.json": "20ec05cd5e592055e214b2da8ccb283c7f2a421ea0dc2acbf1aa792e11c03d0c", + "https://bcr.bazel.build/modules/rules_swift/1.16.0/MODULE.bazel": "4a09f199545a60d09895e8281362b1ff3bb08bbde69c6fc87aff5b92fcc916ca", + "https://bcr.bazel.build/modules/rules_swift/2.1.1/MODULE.bazel": "494900a80f944fc7aa61500c2073d9729dff0b764f0e89b824eb746959bc1046", + "https://bcr.bazel.build/modules/rules_swift/2.4.0/MODULE.bazel": "1639617eb1ede28d774d967a738b4a68b0accb40650beadb57c21846beab5efd", + "https://bcr.bazel.build/modules/rules_swift/3.1.2/MODULE.bazel": "72c8f5cf9d26427cee6c76c8e3853eb46ce6b0412a081b2b6db6e8ad56267400", + "https://bcr.bazel.build/modules/rules_swift/3.1.2/source.json": "e85761f3098a6faf40b8187695e3de6d97944e98abd0d8ce579cb2daf6319a66", + "https://bcr.bazel.build/modules/stardoc/0.5.1/MODULE.bazel": "1a05d92974d0c122f5ccf09291442580317cdd859f07a8655f1db9a60374f9f8", + "https://bcr.bazel.build/modules/stardoc/0.5.3/MODULE.bazel": "c7f6948dae6999bf0db32c1858ae345f112cacf98f174c7a8bb707e41b974f1c", + "https://bcr.bazel.build/modules/stardoc/0.6.2/MODULE.bazel": "7060193196395f5dd668eda046ccbeacebfd98efc77fed418dbe2b82ffaa39fd", + "https://bcr.bazel.build/modules/stardoc/0.7.0/MODULE.bazel": "05e3d6d30c099b6770e97da986c53bd31844d7f13d41412480ea265ac9e8079c", + "https://bcr.bazel.build/modules/stardoc/0.7.2/MODULE.bazel": "fc152419aa2ea0f51c29583fab1e8c99ddefd5b3778421845606ee628629e0e5", + "https://bcr.bazel.build/modules/stardoc/0.7.2/source.json": "58b029e5e901d6802967754adf0a9056747e8176f017cfe3607c0851f4d42216", + "https://bcr.bazel.build/modules/swift_argument_parser/1.3.1.1/MODULE.bazel": "5e463fbfba7b1701d957555ed45097d7f984211330106ccd1352c6e0af0dcf91", + "https://bcr.bazel.build/modules/swift_argument_parser/1.3.1.2/MODULE.bazel": "75aab2373a4bbe2a1260b9bf2a1ebbdbf872d3bd36f80bff058dccd82e89422f", + "https://bcr.bazel.build/modules/swift_argument_parser/1.3.1.2/source.json": "5fba48bbe0ba48761f9e9f75f92876cafb5d07c0ce059cc7a8027416de94a05b", + "https://bcr.bazel.build/modules/upb/0.0.0-20220923-a547704/MODULE.bazel": "7298990c00040a0e2f121f6c32544bab27d4452f80d9ce51349b1a28f3005c43", + "https://bcr.bazel.build/modules/upb/0.0.0-20230516-61a97ef/MODULE.bazel": "c0df5e35ad55e264160417fd0875932ee3c9dda63d9fccace35ac62f45e1b6f9", + "https://bcr.bazel.build/modules/zlib/1.2.11/MODULE.bazel": "07b389abc85fdbca459b69e2ec656ae5622873af3f845e1c9d80fe179f3effa0", + "https://bcr.bazel.build/modules/zlib/1.2.12/MODULE.bazel": "3b1a8834ada2a883674be8cbd36ede1b6ec481477ada359cd2d3ddc562340b27", + "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.5/MODULE.bazel": "eec517b5bbe5492629466e11dae908d043364302283de25581e3eb944326c4ca", + "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.5/source.json": "22bc55c47af97246cfc093d0acf683a7869377de362b5d1c552c2c2e16b7a806", + "https://bcr.bazel.build/modules/zlib/1.3.1/MODULE.bazel": "751c9940dcfe869f5f7274e1295422a34623555916eb98c174c1e945594bf198" + }, + "selectedYankedVersions": {}, + "moduleExtensions": {}, + "facts": {} +}
diff --git a/README.md b/README.md new file mode 100644 index 0000000..99ed80b --- /dev/null +++ b/README.md
@@ -0,0 +1,52 @@ +# Google GCP pubsub messaging for the Gerrit's high-availability plugin + +This plugin provides an extension of the Gerrit's high-availability plugin: +it provides GCP pubsub based message forwarding. + +## License + +This plugin is released under the same Apache 2.0 license and copyright holders +as of the Gerrit Code Review project. + +## How to build + +Refer to the [build instructions in the plugin documentation](src/main/resources/Documentation/build.md). + +## Installation + +Install + +- the `high-availability-gcp` plugin in the Gerrit site's `plugins` folder. + +## Sample configuration + +This is a minimal configuration: +``` +[pubsub] + projectId = my-gcp-project + privateKeyLocation = /path/to/private/key/my-gcp-project-6f0d80a3f655.json +``` + +A complete set of configuration options is given in the following example. +The example values given here are also the default values: +``` +[pubsub] + projectId = my-gcp-project + privateKeyLocation = /path/to/private/key/my-gcp-project-6f0d80a3f655.json +[pubsub "topics"] + main = default + streamEvents = stream-events +[pubsub "limits"] + subscriptionTimeout = 10s + shutdownTimeout = 10s + publisherThreadPoolSize = 4 + subscriberThreadPoolSize = 4 +[pubsub "retention"] + ackDeadline = 10s + messageRetentionDuration = 7d + retainAckedMessages = false +[pubsub "retry"] + maxDeliveryAttempts = 5 + minimumBackoff = 10s + maximumBackoff = 10m +```
diff --git a/external_plugin_deps.MODULE.bazel b/external_plugin_deps.MODULE.bazel new file mode 100644 index 0000000..86e1baf --- /dev/null +++ b/external_plugin_deps.MODULE.bazel
@@ -0,0 +1,19 @@ +# Module fragment for wiring this plugin's Bazel module into the +# Gerrit in-tree build when using Bzlmod. +# +# This file is included from Gerrit's plugins/external_plugin_deps.MODULE.bazel. +# It wires the plugin module into the root dependency graph and exposes +# any repositories it creates (for example via rules_jvm_external). +# +# NOTE: This file must be self-contained: bindings do not cross include() +# boundaries, so it re-declares the rules_jvm_external extension handle. + +maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven") + +bazel_dep(name = "gerrit-high-availability-gcp") +local_path_override( + module_name = "gerrit-high-availability-gcp", + path = "plugins/high-availability-gcp", +) + +use_repo(maven, "high-availability-gcp_plugin_deps")
diff --git a/high-availability-gcp_plugin_deps.lock.json b/high-availability-gcp_plugin_deps.lock.json new file mode 100644 index 0000000..90c47c7 --- /dev/null +++ b/high-availability-gcp_plugin_deps.lock.json
@@ -0,0 +1,1964 @@ +{ + "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": "THERE_IS_NO_DATA_ONLY_ZUUL", + "__INPUT_ARTIFACTS_HASH": { + "com.google.cloud:google-cloud-pubsub": -666200535, + "com.google.code.findbugs:jsr305": -313338248, + "com.google.code.gson:gson": -1752757951, + "com.google.guava:failureaccess": -1493823684, + "com.google.j2objc:j2objc-annotations": 1772577837, + "com.google.protobuf:protobuf-java": 1230241843, + "commons-codec:commons-codec": 284109768, + "io.grpc:grpc-netty-shaded": 155833209, + "org.apache.httpcomponents:httpclient": 1407535949, + "org.apache.httpcomponents:httpcore": -1485416135, + "org.slf4j:slf4j-api": 2145585153, + "org.testcontainers:gcloud": -1907533275, + "org.testcontainers:testcontainers": -14143717, + "repositories": -1949687017 + }, + "__RESOLVED_ARTIFACTS_HASH": { + "com.fasterxml.jackson.core:jackson-annotations": 895697199, + "com.fasterxml.jackson.core:jackson-annotations:jar:sources": 486697886, + "com.github.docker-java:docker-java-api": 1886720023, + "com.github.docker-java:docker-java-api:jar:sources": 1672381514, + "com.github.docker-java:docker-java-transport": -1400224799, + "com.github.docker-java:docker-java-transport-zerodep": 2022449988, + "com.github.docker-java:docker-java-transport-zerodep:jar:sources": -887161140, + "com.github.docker-java:docker-java-transport:jar:sources": -2085127734, + "com.google.android:annotations": 2096496032, + "com.google.android:annotations:jar:sources": -1797343139, + "com.google.api.grpc:proto-google-cloud-pubsub-v1": 1690392012, + "com.google.api.grpc:proto-google-cloud-pubsub-v1:jar:sources": -669655515, + "com.google.api.grpc:proto-google-common-protos": -1441949323, + "com.google.api.grpc:proto-google-common-protos:jar:sources": 1215634766, + "com.google.api.grpc:proto-google-iam-v1": -558695712, + "com.google.api.grpc:proto-google-iam-v1:jar:sources": 1976296040, + "com.google.api:api-common": 1628753455, + "com.google.api:api-common:jar:sources": 1715693714, + "com.google.api:gax": 1730969374, + "com.google.api:gax-grpc": -1868240732, + "com.google.api:gax-grpc:jar:sources": 1912967538, + "com.google.api:gax-httpjson": 1430230691, + "com.google.api:gax-httpjson:jar:sources": 1762375533, + "com.google.api:gax:jar:sources": 795439571, + "com.google.auth:google-auth-library-credentials": 1377746169, + "com.google.auth:google-auth-library-credentials:jar:sources": -1077072211, + "com.google.auth:google-auth-library-oauth2-http": -851052611, + "com.google.auth:google-auth-library-oauth2-http:jar:sources": 4332143, + "com.google.auto.value:auto-value-annotations": 641018093, + "com.google.auto.value:auto-value-annotations:jar:sources": -315473772, + "com.google.cloud:google-cloud-pubsub": 1109898268, + "com.google.cloud:google-cloud-pubsub:jar:sources": 904098729, + "com.google.errorprone:error_prone_annotations": 1571630350, + "com.google.errorprone:error_prone_annotations:jar:sources": -1864520284, + "com.google.guava:guava": 311887683, + "com.google.guava:guava:jar:sources": -1734233239, + "com.google.guava:listenablefuture": 1079558157, + "com.google.http-client:google-http-client": -1810196085, + "com.google.http-client:google-http-client-gson": -1986082497, + "com.google.http-client:google-http-client-gson:jar:sources": 1471652239, + "com.google.http-client:google-http-client:jar:sources": -688965411, + "com.google.protobuf:protobuf-java-util": 1292251753, + "com.google.protobuf:protobuf-java-util:jar:sources": 799127235, + "com.google.re2j:re2j": -1688126233, + "com.google.re2j:re2j:jar:sources": 485338798, + "io.grpc:grpc-alts": -1260622287, + "io.grpc:grpc-alts:jar:sources": -1070738474, + "io.grpc:grpc-api": 76065503, + "io.grpc:grpc-api:jar:sources": -57794343, + "io.grpc:grpc-auth": -412554713, + "io.grpc:grpc-auth:jar:sources": 1647941284, + "io.grpc:grpc-context": 985134038, + "io.grpc:grpc-context:jar:sources": 1947839703, + "io.grpc:grpc-core": 1134603238, + "io.grpc:grpc-core:jar:sources": 975172552, + "io.grpc:grpc-googleapis": 1053062773, + "io.grpc:grpc-googleapis:jar:sources": 483689619, + "io.grpc:grpc-grpclb": 1592256060, + "io.grpc:grpc-grpclb:jar:sources": -1258079685, + "io.grpc:grpc-inprocess": 1288958811, + "io.grpc:grpc-inprocess:jar:sources": -368478985, + "io.grpc:grpc-netty-shaded": -121118512, + "io.grpc:grpc-netty-shaded:jar:sources": 1947839703, + "io.grpc:grpc-protobuf": -126028522, + "io.grpc:grpc-protobuf-lite": -446621013, + "io.grpc:grpc-protobuf-lite:jar:sources": -591062309, + "io.grpc:grpc-protobuf:jar:sources": -1494054936, + "io.grpc:grpc-services": 186142787, + "io.grpc:grpc-services:jar:sources": -35620260, + "io.grpc:grpc-stub": -1726880885, + "io.grpc:grpc-stub:jar:sources": 648807495, + "io.grpc:grpc-util": -680552857, + "io.grpc:grpc-util:jar:sources": 1289119925, + "io.grpc:grpc-xds": -532069961, + "io.grpc:grpc-xds:jar:sources": -25639278, + "io.opencensus:opencensus-api": -26497261, + "io.opencensus:opencensus-api:jar:sources": 1085904850, + "io.opencensus:opencensus-contrib-http-util": -28003592, + "io.opencensus:opencensus-contrib-http-util:jar:sources": -879181311, + "io.opentelemetry:opentelemetry-api": -1533216960, + "io.opentelemetry:opentelemetry-api:jar:sources": 249136, + "io.opentelemetry:opentelemetry-context": 11027445, + "io.opentelemetry:opentelemetry-context:jar:sources": -97118549, + "io.opentelemetry:opentelemetry-semconv": 450634812, + "io.opentelemetry:opentelemetry-semconv:jar:sources": 1211550242, + "io.perfmark:perfmark-api": -1795841558, + "io.perfmark:perfmark-api:jar:sources": -1882329067, + "javax.annotation:javax.annotation-api": -1009230154, + "javax.annotation:javax.annotation-api:jar:sources": -1912277688, + "junit:junit": -1256429642, + "junit:junit:jar:sources": 940567721, + "net.java.dev.jna:jna": 82863035, + "net.java.dev.jna:jna:jar:sources": -1071055013, + "org.apache.commons:commons-compress": 176829, + "org.apache.commons:commons-compress:jar:sources": -2012564756, + "org.checkerframework:checker-qual": -1482757721, + "org.checkerframework:checker-qual:jar:sources": -677321624, + "org.codehaus.mojo:animal-sniffer-annotations": -1619058809, + "org.codehaus.mojo:animal-sniffer-annotations:jar:sources": 83684044, + "org.conscrypt:conscrypt-openjdk-uber": 742580967, + "org.conscrypt:conscrypt-openjdk-uber:jar:sources": 2090280717, + "org.hamcrest:hamcrest-core": 649657847, + "org.hamcrest:hamcrest-core:jar:sources": -1646511374, + "org.jetbrains:annotations": -845966580, + "org.jetbrains:annotations:jar:sources": 582621361, + "org.rnorth.duct-tape:duct-tape": -1059401544, + "org.rnorth.duct-tape:duct-tape:jar:sources": -1798015648, + "org.testcontainers:gcloud": 174145667, + "org.testcontainers:gcloud:jar:sources": -490222159, + "org.testcontainers:testcontainers": -186126913, + "org.testcontainers:testcontainers:jar:sources": 41064557, + "org.threeten:threetenbp": 1406815789, + "org.threeten:threetenbp:jar:sources": 1625133572 + }, + "artifacts": { + "com.fasterxml.jackson.core:jackson-annotations": { + "shasums": { + "jar": "49dfdc4cfa46d165ecfed630ba164b6641d59d5fe1aa698a19c13f966d3f13cf", + "sources": "6310be2ded72eed441291587ed4513c93566b5057b67d05d04cb6d1db4c726b2" + }, + "version": "2.10.3" + }, + "com.github.docker-java:docker-java-api": { + "shasums": { + "jar": "6789d68f95904cf274a6410fdfcc5530976bc94197c856909459bb8c64db557b", + "sources": "1a407574c5c7bf2170d7c9bb76db7341b43b3ee06dc44e215c31356a1d9f3c91" + }, + "version": "3.4.2" + }, + "com.github.docker-java:docker-java-transport": { + "shasums": { + "jar": "bc9981224fdc2d4726566fc723470601ceb14378265c865b4c203146a4e39765", + "sources": "a50fd13efc29146e047ac2a34403172ed62bf52752252286335df1b3e5cf1d18" + }, + "version": "3.4.2" + }, + "com.github.docker-java:docker-java-transport-zerodep": { + "shasums": { + "jar": "582b772e0c52fa9d24710617ef610655465edd9a2930b0db75fc8667d6a8d02b", + "sources": "c88c4745337a54eeae73788cdba9b1a80cebbd3cadfc701c3f8a9e7d217da314" + }, + "version": "3.4.2" + }, + "com.google.android:annotations": { + "shasums": { + "jar": "ba734e1e84c09d615af6a09d33034b4f0442f8772dec120efb376d86a565ae15", + "sources": "e9b667aa958df78ea1ad115f7bbac18a5869c3128b1d5043feb360b0cfce9d40" + }, + "version": "4.1.1.4" + }, + "com.google.api.grpc:proto-google-cloud-pubsub-v1": { + "shasums": { + "jar": "986670409707c11d0904a8a486a44040e2b1e50e60263fd075f2665d06113216", + "sources": "553a710bbb211c82e59f27969d7632d0c551fa1cbe96075c1672ad57362d03e4" + }, + "version": "1.123.2" + }, + "com.google.api.grpc:proto-google-common-protos": { + "shasums": { + "jar": "6655d67f916b4afebc8096c9a311b75a9cbcc1327ff2660681f7de8802d541e9", + "sources": "e9aba88ca9c4561205ed42023a650837d429ed3e27e2f21c333840fad1d03745" + }, + "version": "2.60.0" + }, + "com.google.api.grpc:proto-google-iam-v1": { + "shasums": { + "jar": "c09717925cd7280d3734dcce2f457aca98b56298d011da56e23314e8c7aafe18", + "sources": "999eb7be405e798c0489edfb6f4d505162dd7736894e80c5b5284f1a24d71ee6" + }, + "version": "1.55.0" + }, + "com.google.api:api-common": { + "shasums": { + "jar": "ace161b3aaa028ab54531e7c723a6f29c9fbc2db985b96412b2d3f17083f6937", + "sources": "cdcaa91e85bfefa42eedf594170da2354932aead98908e3b64955ea2cba03688" + }, + "version": "2.52.0" + }, + "com.google.api:gax": { + "shasums": { + "jar": "a0d891df8be06cebd8d05cd9c203cfeae166dd4d827730b58c4c859ac65f4a0b", + "sources": "5fa890b3faa9226cca3ac874c0c32881d942bc7fc60712d8a6e05cd24b4b5097" + }, + "version": "2.69.0" + }, + "com.google.api:gax-grpc": { + "shasums": { + "jar": "14ba424415c7cb370270375361d9d9288e015e8ccf59dd6ad37351b8b928b6a3", + "sources": "637747f8e32dca055ec75b5f57be27b01f94378f20ba7b71c953b7bccd33ace1" + }, + "version": "2.69.0" + }, + "com.google.api:gax-httpjson": { + "shasums": { + "jar": "c407b230c2ccce390caabfb7248f71b218e06fa46b313086300a6812565773e6", + "sources": "b604b0dba283a28e43a4d70e940e767d59f544159fd872da9495b1b4e50fc9fe" + }, + "version": "2.69.0" + }, + "com.google.auth:google-auth-library-credentials": { + "shasums": { + "jar": "1b498e9aaaf661b24afc460939055df3f4147a458ca453f22f710f1ef48fcee9", + "sources": "c084573686f0f1f4ff0a585dff5fa2a3511b483087b2bfc016812ab3fb17115e" + }, + "version": "1.37.1" + }, + "com.google.auth:google-auth-library-oauth2-http": { + "shasums": { + "jar": "bbb0425a2f95fdb3012785b6e5d20a75026a0b1596b9f5a6a40539f513c12da0", + "sources": "51e6b2489360aa3b990409d4e2f142d4f34e6fa93ed443a2d4d13e84de329434" + }, + "version": "1.37.1" + }, + "com.google.auto.value:auto-value-annotations": { + "shasums": { + "jar": "5a055ce4255333b3346e1a8703da5bf8ff049532286fdcd31712d624abe111dd", + "sources": "d7941e5f19bb38afcfa85350d57e5245856c23c98c2bbe32f6d31b5577f2bc33" + }, + "version": "1.11.0" + }, + "com.google.cloud:google-cloud-pubsub": { + "shasums": { + "jar": "2547f9ba75eeff6d9b175a3e72e796bc9da6d3b5f57f7a5d637f3722dc5762fe", + "sources": "80bf17ce3348b3d2ba01efcd20f6dbf7c7f99a70b2c45367fd9cc7e44d9f2889" + }, + "version": "1.141.2" + }, + "com.google.errorprone:error_prone_annotations": { + "shasums": { + "jar": "6661d5335090a5fc61dd869d2095bc6c1e2156e3aa47a6e4ababdf64c99a7889", + "sources": "bc4e1535cf5a236ca2e2cfb66d76da4cea99ae52d7447f57a2b05943cac21747" + }, + "version": "2.38.0" + }, + "com.google.guava:guava": { + "shasums": { + "jar": "b918c98a7e44dbe94ebd9fe3e40cddaadb5a93e6a78eb6008b42df237241e538", + "sources": "55ef6603b6ab1f6e3ae810b127561650ed682eb5f3fb50a212a658a74087b457" + }, + "version": "33.4.0-jre" + }, + "com.google.guava:listenablefuture": { + "shasums": { + "jar": "b372a037d4230aa57fbeffdef30fd6123f9c0c2db85d0aced00c91b974f33f99" + }, + "version": "9999.0-empty-to-avoid-conflict-with-guava" + }, + "com.google.http-client:google-http-client": { + "shasums": { + "jar": "22447fde9f2e33e27a23a25953b1c622ead6c055c761fde6ca50573c9473457a", + "sources": "7fd1f5c075db280e2a878bf14c7da248e2984e6411a509bcad3e667dd3dd936d" + }, + "version": "1.47.1" + }, + "com.google.http-client:google-http-client-gson": { + "shasums": { + "jar": "64ac2b1313dca6b6fc9bd14128ab186528fe992b094f5371fa7f828eed8903bd", + "sources": "f1cd1055487dd85133f3665645c9a06a892664a500b6fe7a3e4eef4c2cefdbaf" + }, + "version": "1.47.1" + }, + "com.google.protobuf:protobuf-java-util": { + "shasums": { + "jar": "e7196eaefc3f81afa1571821635c7aefe2933f4be4a9df9e601fb59214b50269", + "sources": "ddfec53ff55c50f9017c3f4b9fba20253df6f59b1e5439a8cc74efff0f2366d2" + }, + "version": "3.25.8" + }, + "com.google.re2j:re2j": { + "shasums": { + "jar": "7b52c72156dd7f98b3237a5b35c1d34fba381b21048c89208913ad80a45dfbd7", + "sources": "4378db846db2d91c8028fe32efcb183a1f1b4157339d0a3b4c46b2c6b0655d7c" + }, + "version": "1.8" + }, + "io.grpc:grpc-alts": { + "shasums": { + "jar": "c0671401abc87cf6bd6cdb58a0bacf29aff0eb720d4d0f32373c128208cbe8fd", + "sources": "f6629f36e3b795d338f8f89273aff4e2f5f92da8fa435c1258c83a89ee9a249d" + }, + "version": "1.71.0" + }, + "io.grpc:grpc-api": { + "shasums": { + "jar": "7c2c39b76fd50ea4f58a7f4c12e82640e355fc92ca025be48072be1bcc2b177d", + "sources": "595571ec768ccbc3984fd080d184cfc4e0a57c36ae3ca7eed83a31b7293e8c33" + }, + "version": "1.74.0" + }, + "io.grpc:grpc-auth": { + "shasums": { + "jar": "1968885f6034075fcc49ef66ae6cd24d7ee6a44243779b1582dd715a3e84f1c4", + "sources": "99e071b6f01d657d462c55925d1c1502487ac0e74d1b570ccb017b48eede0141" + }, + "version": "1.71.0" + }, + "io.grpc:grpc-context": { + "shasums": { + "jar": "fec0773020404a1f9b9610168692c8ed47fb77181c2ba37cc842ba7664bfc6b0", + "sources": "0a45ca2dc3452783be72412c0e75e0a57c276e76d29d778a22e331279f08d5c0" + }, + "version": "1.74.0" + }, + "io.grpc:grpc-core": { + "shasums": { + "jar": "5175e6223ee1f0a63cf93049aabe7bd5b675cc739b286fe3b320a88fa90c320e", + "sources": "2c7a8d053a2bfa7c423ca01f1b6462bf402ff710aea8c49a73c2d0d73ba8e358" + }, + "version": "1.74.0" + }, + "io.grpc:grpc-googleapis": { + "shasums": { + "jar": "44277ccc6c9d3bc9cd3f1e17689c83168a72b132c00f56f93a7d6e1b9984ee5d", + "sources": "7909fa9cd191ba52e23d6442e369a068fa8bc8fed35b56bf884decbf179d2838" + }, + "version": "1.71.0" + }, + "io.grpc:grpc-grpclb": { + "shasums": { + "jar": "57867a44967669ea2a024ec97f3c497a9cdc7c73d06cee0f0e19185ef88a38fe", + "sources": "50f6cb31a4462bd8c6c00ef2b7e458fbf2b2d132c156989899191d67b73bad55" + }, + "version": "1.71.0" + }, + "io.grpc:grpc-inprocess": { + "shasums": { + "jar": "86c7de6e38cd3ea40b3720a037f58069c65f1a5d291aea370261eeebfe0557af", + "sources": "2d1dcf37332d23340411197c675a6831414eec1450a9829d58b84a4ce03cfc99" + }, + "version": "1.71.0" + }, + "io.grpc:grpc-netty-shaded": { + "shasums": { + "jar": "aecca383751c69d3fde1bdfc0d74e845a76f83887041440fc9e194a5f07e6c8f", + "sources": "0a45ca2dc3452783be72412c0e75e0a57c276e76d29d778a22e331279f08d5c0" + }, + "version": "1.74.0" + }, + "io.grpc:grpc-protobuf": { + "shasums": { + "jar": "7eba0625fba8e176c0fc1b80610fc2c52c036d95805ffdd3f6985a51c968de94", + "sources": "f56c4b166cfb083a2108857cb80eabb6a79a7856c0674490e4fa9849b29a7ff8" + }, + "version": "1.71.0" + }, + "io.grpc:grpc-protobuf-lite": { + "shasums": { + "jar": "ffad977400b49649fa2d5404abaadfca66c7e5956974f192411e8946a12e5f53", + "sources": "5cbc25c897d26e0cdeca771a79ad2d990b558957319cdc3856f13b8753f2a69a" + }, + "version": "1.71.0" + }, + "io.grpc:grpc-services": { + "shasums": { + "jar": "e966ba062b6793083ab163c38103d4fcbc71ed9f91db4bd81b30fcb975a0a845", + "sources": "2bd30bcc48fb39755743f7241add8ba17188f14a3b84ddfa052c1e6455e98065" + }, + "version": "1.71.0" + }, + "io.grpc:grpc-stub": { + "shasums": { + "jar": "51912e612b28db65eec0332a8be494b3c54d347ecd2c5f6a7fdd2cb994fab360", + "sources": "0cb35ee903b8bf016a719bcd00362c7dc8f25600bd0732192c7c95f1cec16e20" + }, + "version": "1.71.0" + }, + "io.grpc:grpc-util": { + "shasums": { + "jar": "296dc07438c43454afc1a7f2736086550feb1fc783f9547337ad2ae721e24350", + "sources": "ba7b02ef3f3a7ed93881c1c37db8b7372458215c7e2e7d57d1a7fc91725d94e1" + }, + "version": "1.74.0" + }, + "io.grpc:grpc-xds": { + "shasums": { + "jar": "b570b80809531410f965cc04e29466da1c338da434db5fcaa29b822d40fe80b5", + "sources": "011f131d452ebfbc749cff6780e2d1a812caea2deb75b8d8ce21cf778b66b66a" + }, + "version": "1.71.0" + }, + "io.opencensus:opencensus-api": { + "shasums": { + "jar": "f1474d47f4b6b001558ad27b952e35eda5cc7146788877fc52938c6eba24b382", + "sources": "6748d57aaae81995514ad3e2fb11a95aa88e158b3f93450288018eaccf31e86b" + }, + "version": "0.31.1" + }, + "io.opencensus:opencensus-contrib-http-util": { + "shasums": { + "jar": "3ea995b55a4068be22989b70cc29a4d788c2d328d1d50613a7a9afd13fdd2d0a", + "sources": "d55afd5f96dc724bd903a77a38b0a344d0e59f02a64b9ab2f32618bc582ea924" + }, + "version": "0.31.1" + }, + "io.opentelemetry:opentelemetry-api": { + "shasums": { + "jar": "6566f1f1133d611ff4e8b8fdb8eb18577b970425620315363ee9be43843b14bf", + "sources": "1e7bac30757e9504ea737c4be341130d8f013a3290059022156bce1a06d64546" + }, + "version": "1.47.0" + }, + "io.opentelemetry:opentelemetry-context": { + "shasums": { + "jar": "15b4fc4234e6dca6d54800d572694ecbd07ba52c15fc5b221b4da5517ce8d90d", + "sources": "7c959f00d13fc1698a7d37878715e6c42b5ee89b2076a324ffaf8de5b9348b2b" + }, + "version": "1.47.0" + }, + "io.opentelemetry:opentelemetry-semconv": { + "shasums": { + "jar": "b57721963af36e72f10d7f882bb247b775f8d182777cfe8b1907b40282ff47eb", + "sources": "4e9077a1e6167f8bfe114cb99a8c706bfa85048000364ea3897864f3fa0b5e01" + }, + "version": "1.26.0-alpha" + }, + "io.perfmark:perfmark-api": { + "shasums": { + "jar": "c7b478503ec524e55df19b424d46d27c8a68aeb801664fadd4f069b71f52d0f6", + "sources": "311551ab29cf51e5a8abee6a019e88dee47d1ea71deb9fcd3649db9c51b237bc" + }, + "version": "0.27.0" + }, + "javax.annotation:javax.annotation-api": { + "shasums": { + "jar": "e04ba5195bcd555dc95650f7cc614d151e4bcd52d29a10b8aa2197f3ab89ab9b", + "sources": "128971e52e0d84a66e3b6e049dab8ad7b2c58b7e1ad37fa2debd3d40c2947b95" + }, + "version": "1.3.2" + }, + "junit:junit": { + "shasums": { + "jar": "8e495b634469d64fb8acfa3495a065cbacc8a0fff55ce1e31007be4c16dc57d3", + "sources": "34181df6482d40ea4c046b063cb53c7ffae94bdf1b1d62695bdf3adf9dea7e3a" + }, + "version": "4.13.2" + }, + "net.java.dev.jna:jna": { + "shasums": { + "jar": "66d4f819a062a51a1d5627bffc23fac55d1677f0e0a1feba144aabdd670a64bb", + "sources": "a4c45843e8f60df141c4f37602365a421bb278ca1ef30ba0a043d6a871dd29f4" + }, + "version": "5.13.0" + }, + "org.apache.commons:commons-compress": { + "shasums": { + "jar": "fbf2c5d275e513c08f4e4fb539726dbf3ac2142af956ba93efb983244f0c36bd", + "sources": "b79da77c5bb6e9b528d60c7336a59dc08cab29baac52f3d35441455c5fec4db1" + }, + "version": "1.24.0" + }, + "org.checkerframework:checker-qual": { + "shasums": { + "jar": "8b9d9a36eaaf7c0fc26503c83cd97d8c9c0f9e2913cc2a6e92ac26c735d4dcbe", + "sources": "546424b9b019b3d5b16716ec280cfc4e23b25feebecc2b60f9721d1fab6635d5" + }, + "version": "3.49.0" + }, + "org.codehaus.mojo:animal-sniffer-annotations": { + "shasums": { + "jar": "c720e6e5bcbe6b2f48ded75a47bccdb763eede79d14330102e0d352e3d89ed92", + "sources": "4270ce5531ed0f12e4234e08f240ef3b45ee3ceeb16e28d44abc61c12cf522ca" + }, + "version": "1.24" + }, + "org.conscrypt:conscrypt-openjdk-uber": { + "shasums": { + "jar": "eaf537d98e033d0f0451cd1b8cc74e02d7b55ec882da63c88060d806ba89c348", + "sources": "aa1d02e65351e202e83ece0614bce1022aa1da6e77313ef7c7663ab45fa9e3a5" + }, + "version": "2.5.2" + }, + "org.hamcrest:hamcrest-core": { + "shasums": { + "jar": "66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9", + "sources": "e223d2d8fbafd66057a8848cc94222d63c3cedd652cc48eddc0ab5c39c0f84df" + }, + "version": "1.3" + }, + "org.jetbrains:annotations": { + "shasums": { + "jar": "195fb0da046d55bb042e91543484cf1da68b02bb7afbfe031f229e45ac84b3f2", + "sources": "b2c0d02e0a32c56d359e99634e7d769f9b1a8cd6e25061995abad1c1baf86f56" + }, + "version": "17.0.0" + }, + "org.rnorth.duct-tape:duct-tape": { + "shasums": { + "jar": "31cef12ddec979d1f86d7cf708c41a17da523d05c685fd6642e9d0b2addb7240", + "sources": "b385fd2c2b435c313b3f02988d351503230c9631bfb432261cbd8ce9765d2a26" + }, + "version": "1.0.8" + }, + "org.testcontainers:gcloud": { + "shasums": { + "jar": "795970b08782e997deff916f25b9aadb05285b4a273bbc70ad5040bfff127ddd", + "sources": "116e67a7f29601ee942206a4430c207f66707950aa598c533dfa27394fa51229" + }, + "version": "1.21.3" + }, + "org.testcontainers:testcontainers": { + "shasums": { + "jar": "ef934ddac6f42759d71c303a2844544a56cf381b33437149955d3d8c992bec5d", + "sources": "e594cd4f3f95dfec1dff67e3ba4eabb2f1ef818238e35d1aea611939d3a0bb69" + }, + "version": "1.21.3" + }, + "org.threeten:threetenbp": { + "shasums": { + "jar": "857917d2319a4e92dc1c5e3aeb75a0dac84445ed315e7ac3d82bb8d2b298977f", + "sources": "b4d3602a948a10ea275991d4144c3cbbd9b9000bd3b58dfc7b74b240688ca4a9" + }, + "version": "1.7.0" + } + }, + "dependencies": { + "com.github.docker-java:docker-java-api": [ + "com.fasterxml.jackson.core:jackson-annotations" + ], + "com.github.docker-java:docker-java-transport-zerodep": [ + "com.github.docker-java:docker-java-transport", + "net.java.dev.jna:jna" + ], + "com.google.api.grpc:proto-google-cloud-pubsub-v1": [ + "com.google.auto.value:auto-value-annotations", + "com.google.errorprone:error_prone_annotations", + "com.google.guava:listenablefuture", + "javax.annotation:javax.annotation-api", + "org.checkerframework:checker-qual" + ], + "com.google.cloud:google-cloud-pubsub": [ + "com.google.android:annotations", + "com.google.api.grpc:proto-google-cloud-pubsub-v1", + "com.google.api.grpc:proto-google-common-protos", + "com.google.api.grpc:proto-google-iam-v1", + "com.google.api:api-common", + "com.google.api:gax", + "com.google.api:gax-grpc", + "com.google.api:gax-httpjson", + "com.google.auth:google-auth-library-credentials", + "com.google.auth:google-auth-library-oauth2-http", + "com.google.auto.value:auto-value-annotations", + "com.google.errorprone:error_prone_annotations", + "com.google.guava:guava", + "com.google.guava:listenablefuture", + "com.google.http-client:google-http-client", + "com.google.http-client:google-http-client-gson", + "com.google.protobuf:protobuf-java-util", + "com.google.re2j:re2j", + "io.grpc:grpc-alts", + "io.grpc:grpc-api", + "io.grpc:grpc-auth", + "io.grpc:grpc-context", + "io.grpc:grpc-core", + "io.grpc:grpc-googleapis", + "io.grpc:grpc-grpclb", + "io.grpc:grpc-inprocess", + "io.grpc:grpc-netty-shaded", + "io.grpc:grpc-protobuf", + "io.grpc:grpc-protobuf-lite", + "io.grpc:grpc-services", + "io.grpc:grpc-stub", + "io.grpc:grpc-util", + "io.grpc:grpc-xds", + "io.opencensus:opencensus-api", + "io.opencensus:opencensus-contrib-http-util", + "io.opentelemetry:opentelemetry-api", + "io.opentelemetry:opentelemetry-context", + "io.opentelemetry:opentelemetry-semconv", + "io.perfmark:perfmark-api", + "javax.annotation:javax.annotation-api", + "org.checkerframework:checker-qual", + "org.codehaus.mojo:animal-sniffer-annotations", + "org.conscrypt:conscrypt-openjdk-uber", + "org.threeten:threetenbp" + ], + "com.google.guava:guava": [ + "com.google.errorprone:error_prone_annotations", + "com.google.guava:listenablefuture", + "org.checkerframework:checker-qual" + ], + "io.grpc:grpc-api": [ + "com.google.errorprone:error_prone_annotations", + "com.google.guava:guava" + ], + "io.grpc:grpc-context": [ + "io.grpc:grpc-api" + ], + "io.grpc:grpc-core": [ + "com.google.android:annotations", + "com.google.errorprone:error_prone_annotations", + "com.google.guava:guava", + "io.grpc:grpc-api", + "io.grpc:grpc-context", + "io.perfmark:perfmark-api", + "org.codehaus.mojo:animal-sniffer-annotations" + ], + "io.grpc:grpc-netty-shaded": [ + "com.google.errorprone:error_prone_annotations", + "com.google.guava:guava", + "io.grpc:grpc-api", + "io.grpc:grpc-core", + "io.grpc:grpc-util", + "io.perfmark:perfmark-api", + "org.codehaus.mojo:animal-sniffer-annotations" + ], + "io.grpc:grpc-util": [ + "com.google.guava:guava", + "io.grpc:grpc-api", + "io.grpc:grpc-core", + "org.codehaus.mojo:animal-sniffer-annotations" + ], + "junit:junit": [ + "org.hamcrest:hamcrest-core" + ], + "org.rnorth.duct-tape:duct-tape": [ + "org.jetbrains:annotations" + ], + "org.testcontainers:gcloud": [ + "org.testcontainers:testcontainers" + ], + "org.testcontainers:testcontainers": [ + "com.github.docker-java:docker-java-api", + "com.github.docker-java:docker-java-transport-zerodep", + "junit:junit", + "org.apache.commons:commons-compress", + "org.rnorth.duct-tape:duct-tape" + ] + }, + "packages": { + "com.fasterxml.jackson.core:jackson-annotations": [ + "com.fasterxml.jackson.annotation" + ], + "com.github.docker-java:docker-java-api": [ + "com.github.dockerjava.api", + "com.github.dockerjava.api.async", + "com.github.dockerjava.api.command", + "com.github.dockerjava.api.exception", + "com.github.dockerjava.api.model" + ], + "com.github.docker-java:docker-java-transport": [ + "com.github.dockerjava.transport" + ], + "com.github.docker-java:docker-java-transport-zerodep": [ + "com.github.dockerjava.zerodep", + "com.github.dockerjava.zerodep.shaded.org.apache.commons.codec", + "com.github.dockerjava.zerodep.shaded.org.apache.commons.codec.binary", + "com.github.dockerjava.zerodep.shaded.org.apache.commons.codec.cli", + "com.github.dockerjava.zerodep.shaded.org.apache.commons.codec.digest", + "com.github.dockerjava.zerodep.shaded.org.apache.commons.codec.language", + "com.github.dockerjava.zerodep.shaded.org.apache.commons.codec.language.bm", + "com.github.dockerjava.zerodep.shaded.org.apache.commons.codec.net", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.client5.http", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.client5.http.async", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.client5.http.async.methods", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.client5.http.auth", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.client5.http.classic", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.client5.http.classic.methods", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.client5.http.config", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.client5.http.cookie", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.client5.http.entity", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.client5.http.entity.mime", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.client5.http.impl", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.client5.http.impl.async", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.client5.http.impl.auth", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.client5.http.impl.classic", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.client5.http.impl.cookie", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.client5.http.impl.io", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.client5.http.impl.nio", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.client5.http.impl.routing", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.client5.http.io", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.client5.http.nio", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.client5.http.protocol", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.client5.http.psl", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.client5.http.routing", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.client5.http.socket", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.client5.http.ssl", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.client5.http.utils", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.annotation", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.concurrent", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.function", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.http", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.http.config", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.http.impl", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.http.impl.bootstrap", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.http.impl.io", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.http.impl.nio", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.http.io", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.http.io.entity", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.http.io.ssl", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.http.io.support", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.http.message", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.http.nio", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.http.nio.command", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.http.nio.entity", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.http.nio.ssl", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.http.nio.support", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.http.nio.support.classic", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.http.protocol", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.http.ssl", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.http2", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.http2.config", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.http2.frame", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.http2.hpack", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.http2.impl", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.http2.impl.io", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.http2.impl.nio", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.http2.impl.nio.bootstrap", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.http2.nio", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.http2.nio.command", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.http2.nio.pool", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.http2.nio.support", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.http2.protocol", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.http2.ssl", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.io", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.net", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.pool", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.reactor", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.reactor.ssl", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.ssl", + "com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.util" + ], + "com.google.android:annotations": [ + "android.annotation" + ], + "com.google.api.grpc:proto-google-cloud-pubsub-v1": [ + "com.google.pubsub.v1" + ], + "com.google.api.grpc:proto-google-common-protos": [ + "com.google.api", + "com.google.apps.card.v1", + "com.google.cloud", + "com.google.cloud.audit", + "com.google.cloud.location", + "com.google.geo.type", + "com.google.logging.type", + "com.google.longrunning", + "com.google.rpc", + "com.google.rpc.context", + "com.google.shopping.type", + "com.google.type" + ], + "com.google.api.grpc:proto-google-iam-v1": [ + "com.google.iam.v1", + "com.google.iam.v1.logging" + ], + "com.google.api:api-common": [ + "com.google.api.core", + "com.google.api.pathtemplate", + "com.google.api.resourcenames" + ], + "com.google.api:gax": [ + "com.google.api.gax.batching", + "com.google.api.gax.core", + "com.google.api.gax.logging", + "com.google.api.gax.longrunning", + "com.google.api.gax.nativeimage", + "com.google.api.gax.paging", + "com.google.api.gax.retrying", + "com.google.api.gax.rpc", + "com.google.api.gax.rpc.internal", + "com.google.api.gax.rpc.mtls", + "com.google.api.gax.tracing", + "com.google.api.gax.util" + ], + "com.google.api:gax-grpc": [ + "com.google.api.gax.grpc", + "com.google.api.gax.grpc.nativeimage", + "com.google.longrunning", + "com.google.longrunning.stub" + ], + "com.google.api:gax-httpjson": [ + "com.google.api.gax.httpjson", + "com.google.api.gax.httpjson.longrunning", + "com.google.api.gax.httpjson.longrunning.stub" + ], + "com.google.auth:google-auth-library-credentials": [ + "com.google.auth" + ], + "com.google.auth:google-auth-library-oauth2-http": [ + "com.google.auth.http", + "com.google.auth.mtls", + "com.google.auth.oauth2" + ], + "com.google.auto.value:auto-value-annotations": [ + "com.google.auto.value", + "com.google.auto.value.extension.memoized", + "com.google.auto.value.extension.serializable", + "com.google.auto.value.extension.toprettystring" + ], + "com.google.cloud:google-cloud-pubsub": [ + "com.google.cloud.pubsub.v1", + "com.google.cloud.pubsub.v1.stub" + ], + "com.google.errorprone:error_prone_annotations": [ + "com.google.errorprone.annotations", + "com.google.errorprone.annotations.concurrent" + ], + "com.google.guava:guava": [ + "com.google.common.annotations", + "com.google.common.base", + "com.google.common.base.internal", + "com.google.common.cache", + "com.google.common.collect", + "com.google.common.escape", + "com.google.common.eventbus", + "com.google.common.graph", + "com.google.common.hash", + "com.google.common.html", + "com.google.common.io", + "com.google.common.math", + "com.google.common.net", + "com.google.common.primitives", + "com.google.common.reflect", + "com.google.common.util.concurrent", + "com.google.common.xml", + "com.google.thirdparty.publicsuffix" + ], + "com.google.http-client:google-http-client": [ + "com.google.api.client.http", + "com.google.api.client.http.apache", + "com.google.api.client.http.javanet", + "com.google.api.client.http.json", + "com.google.api.client.json", + "com.google.api.client.json.rpc2", + "com.google.api.client.json.webtoken", + "com.google.api.client.testing.http", + "com.google.api.client.testing.http.apache", + "com.google.api.client.testing.http.javanet", + "com.google.api.client.testing.json", + "com.google.api.client.testing.json.webtoken", + "com.google.api.client.testing.util", + "com.google.api.client.util", + "com.google.api.client.util.escape", + "com.google.api.client.util.store" + ], + "com.google.http-client:google-http-client-gson": [ + "com.google.api.client.json.gson" + ], + "com.google.protobuf:protobuf-java-util": [ + "com.google.protobuf.util" + ], + "com.google.re2j:re2j": [ + "com.google.re2j" + ], + "io.grpc:grpc-alts": [ + "io.grpc.alts", + "io.grpc.alts.internal" + ], + "io.grpc:grpc-api": [ + "io.grpc" + ], + "io.grpc:grpc-auth": [ + "io.grpc.auth" + ], + "io.grpc:grpc-core": [ + "io.grpc.internal" + ], + "io.grpc:grpc-googleapis": [ + "io.grpc.googleapis" + ], + "io.grpc:grpc-grpclb": [ + "io.grpc.grpclb", + "io.grpc.lb.v1" + ], + "io.grpc:grpc-inprocess": [ + "io.grpc.inprocess" + ], + "io.grpc:grpc-netty-shaded": [ + "io.grpc.netty.shaded.io.grpc.netty", + "io.grpc.netty.shaded.io.netty.bootstrap", + "io.grpc.netty.shaded.io.netty.buffer", + "io.grpc.netty.shaded.io.netty.buffer.search", + "io.grpc.netty.shaded.io.netty.channel", + "io.grpc.netty.shaded.io.netty.channel.embedded", + "io.grpc.netty.shaded.io.netty.channel.epoll", + "io.grpc.netty.shaded.io.netty.channel.group", + "io.grpc.netty.shaded.io.netty.channel.internal", + "io.grpc.netty.shaded.io.netty.channel.local", + "io.grpc.netty.shaded.io.netty.channel.nio", + "io.grpc.netty.shaded.io.netty.channel.oio", + "io.grpc.netty.shaded.io.netty.channel.pool", + "io.grpc.netty.shaded.io.netty.channel.socket", + "io.grpc.netty.shaded.io.netty.channel.socket.nio", + "io.grpc.netty.shaded.io.netty.channel.socket.oio", + "io.grpc.netty.shaded.io.netty.channel.unix", + "io.grpc.netty.shaded.io.netty.handler.address", + "io.grpc.netty.shaded.io.netty.handler.codec", + "io.grpc.netty.shaded.io.netty.handler.codec.base64", + "io.grpc.netty.shaded.io.netty.handler.codec.bytes", + "io.grpc.netty.shaded.io.netty.handler.codec.compression", + "io.grpc.netty.shaded.io.netty.handler.codec.http", + "io.grpc.netty.shaded.io.netty.handler.codec.http.cookie", + "io.grpc.netty.shaded.io.netty.handler.codec.http.cors", + "io.grpc.netty.shaded.io.netty.handler.codec.http.multipart", + "io.grpc.netty.shaded.io.netty.handler.codec.http.websocketx", + "io.grpc.netty.shaded.io.netty.handler.codec.http.websocketx.extensions", + "io.grpc.netty.shaded.io.netty.handler.codec.http.websocketx.extensions.compression", + "io.grpc.netty.shaded.io.netty.handler.codec.http2", + "io.grpc.netty.shaded.io.netty.handler.codec.json", + "io.grpc.netty.shaded.io.netty.handler.codec.marshalling", + "io.grpc.netty.shaded.io.netty.handler.codec.protobuf", + "io.grpc.netty.shaded.io.netty.handler.codec.rtsp", + "io.grpc.netty.shaded.io.netty.handler.codec.serialization", + "io.grpc.netty.shaded.io.netty.handler.codec.socks", + "io.grpc.netty.shaded.io.netty.handler.codec.socksx", + "io.grpc.netty.shaded.io.netty.handler.codec.socksx.v4", + "io.grpc.netty.shaded.io.netty.handler.codec.socksx.v5", + "io.grpc.netty.shaded.io.netty.handler.codec.spdy", + "io.grpc.netty.shaded.io.netty.handler.codec.string", + "io.grpc.netty.shaded.io.netty.handler.codec.xml", + "io.grpc.netty.shaded.io.netty.handler.flow", + "io.grpc.netty.shaded.io.netty.handler.flush", + "io.grpc.netty.shaded.io.netty.handler.ipfilter", + "io.grpc.netty.shaded.io.netty.handler.logging", + "io.grpc.netty.shaded.io.netty.handler.pcap", + "io.grpc.netty.shaded.io.netty.handler.proxy", + "io.grpc.netty.shaded.io.netty.handler.ssl", + "io.grpc.netty.shaded.io.netty.handler.ssl.ocsp", + "io.grpc.netty.shaded.io.netty.handler.ssl.util", + "io.grpc.netty.shaded.io.netty.handler.stream", + "io.grpc.netty.shaded.io.netty.handler.timeout", + "io.grpc.netty.shaded.io.netty.handler.traffic", + "io.grpc.netty.shaded.io.netty.internal.tcnative", + "io.grpc.netty.shaded.io.netty.resolver", + "io.grpc.netty.shaded.io.netty.util", + "io.grpc.netty.shaded.io.netty.util.collection", + "io.grpc.netty.shaded.io.netty.util.concurrent", + "io.grpc.netty.shaded.io.netty.util.internal", + "io.grpc.netty.shaded.io.netty.util.internal.logging", + "io.grpc.netty.shaded.io.netty.util.internal.shaded.org.jctools.queues", + "io.grpc.netty.shaded.io.netty.util.internal.shaded.org.jctools.queues.atomic", + "io.grpc.netty.shaded.io.netty.util.internal.shaded.org.jctools.util", + "io.grpc.netty.shaded.io.netty.util.internal.svm" + ], + "io.grpc:grpc-protobuf": [ + "io.grpc.protobuf" + ], + "io.grpc:grpc-protobuf-lite": [ + "io.grpc.protobuf.lite" + ], + "io.grpc:grpc-services": [ + "io.grpc.binarylog.v1", + "io.grpc.channelz.v1", + "io.grpc.health.v1", + "io.grpc.protobuf.services", + "io.grpc.protobuf.services.internal", + "io.grpc.reflection.v1", + "io.grpc.reflection.v1alpha", + "io.grpc.services" + ], + "io.grpc:grpc-stub": [ + "io.grpc.stub", + "io.grpc.stub.annotations" + ], + "io.grpc:grpc-util": [ + "io.grpc.util" + ], + "io.grpc:grpc-xds": [ + "io.grpc.xds", + "io.grpc.xds.client", + "io.grpc.xds.internal", + "io.grpc.xds.internal.rbac.engine", + "io.grpc.xds.internal.security", + "io.grpc.xds.internal.security.certprovider", + "io.grpc.xds.internal.security.trust", + "io.grpc.xds.orca", + "io.grpc.xds.shaded.com.github.udpa.udpa.type.v1", + "io.grpc.xds.shaded.com.github.xds.core.v3", + "io.grpc.xds.shaded.com.github.xds.data.orca.v3", + "io.grpc.xds.shaded.com.github.xds.service.orca.v3", + "io.grpc.xds.shaded.com.github.xds.type.matcher.v3", + "io.grpc.xds.shaded.com.github.xds.type.v3", + "io.grpc.xds.shaded.com.google.api.expr.v1alpha1", + "io.grpc.xds.shaded.dev.cel.expr", + "io.grpc.xds.shaded.envoy.annotations", + "io.grpc.xds.shaded.io.envoyproxy.envoy.admin.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.config.accesslog.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.config.bootstrap.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.config.cluster.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.config.core.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.config.endpoint.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.config.listener.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.config.metrics.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.config.overload.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.config.rbac.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.config.route.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.config.trace.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.data.accesslog.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.extensions.clusters.aggregate.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.extensions.filters.common.fault.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.extensions.filters.http.fault.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.extensions.filters.http.gcp_authn.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.extensions.filters.http.rate_limit_quota.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.extensions.filters.http.rbac.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.extensions.filters.http.router.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.extensions.load_balancing_policies.client_side_weighted_round_robin.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.extensions.load_balancing_policies.common.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.extensions.load_balancing_policies.least_request.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.extensions.load_balancing_policies.pick_first.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.extensions.load_balancing_policies.ring_hash.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.extensions.load_balancing_policies.round_robin.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.extensions.load_balancing_policies.wrr_locality.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.extensions.transport_sockets.tls.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.service.discovery.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.service.load_stats.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.service.rate_limit_quota.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.service.status.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.type.http.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.type.matcher.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.type.metadata.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.type.tracing.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.type.v3", + "io.grpc.xds.shaded.io.envoyproxy.pgv.validate", + "io.grpc.xds.shaded.udpa.annotations", + "io.grpc.xds.shaded.xds.annotations.v3" + ], + "io.opencensus:opencensus-api": [ + "io.opencensus.common", + "io.opencensus.internal", + "io.opencensus.metrics", + "io.opencensus.metrics.data", + "io.opencensus.metrics.export", + "io.opencensus.resource", + "io.opencensus.stats", + "io.opencensus.tags", + "io.opencensus.tags.propagation", + "io.opencensus.tags.unsafe", + "io.opencensus.trace", + "io.opencensus.trace.config", + "io.opencensus.trace.export", + "io.opencensus.trace.internal", + "io.opencensus.trace.propagation", + "io.opencensus.trace.samplers", + "io.opencensus.trace.unsafe" + ], + "io.opencensus:opencensus-contrib-http-util": [ + "io.opencensus.contrib.http", + "io.opencensus.contrib.http.util" + ], + "io.opentelemetry:opentelemetry-api": [ + "io.opentelemetry.api", + "io.opentelemetry.api.baggage", + "io.opentelemetry.api.baggage.propagation", + "io.opentelemetry.api.common", + "io.opentelemetry.api.internal", + "io.opentelemetry.api.logs", + "io.opentelemetry.api.metrics", + "io.opentelemetry.api.trace", + "io.opentelemetry.api.trace.propagation", + "io.opentelemetry.api.trace.propagation.internal" + ], + "io.opentelemetry:opentelemetry-context": [ + "io.opentelemetry.context", + "io.opentelemetry.context.internal.shaded", + "io.opentelemetry.context.propagation", + "io.opentelemetry.context.propagation.internal" + ], + "io.opentelemetry:opentelemetry-semconv": [ + "io.opentelemetry.semconv.resource.attributes", + "io.opentelemetry.semconv.trace.attributes" + ], + "io.perfmark:perfmark-api": [ + "io.perfmark" + ], + "javax.annotation:javax.annotation-api": [ + "javax.annotation", + "javax.annotation.security", + "javax.annotation.sql" + ], + "junit:junit": [ + "junit.extensions", + "junit.framework", + "junit.runner", + "junit.textui", + "org.junit", + "org.junit.experimental", + "org.junit.experimental.categories", + "org.junit.experimental.max", + "org.junit.experimental.results", + "org.junit.experimental.runners", + "org.junit.experimental.theories", + "org.junit.experimental.theories.internal", + "org.junit.experimental.theories.suppliers", + "org.junit.function", + "org.junit.internal", + "org.junit.internal.builders", + "org.junit.internal.management", + "org.junit.internal.matchers", + "org.junit.internal.requests", + "org.junit.internal.runners", + "org.junit.internal.runners.model", + "org.junit.internal.runners.rules", + "org.junit.internal.runners.statements", + "org.junit.matchers", + "org.junit.rules", + "org.junit.runner", + "org.junit.runner.manipulation", + "org.junit.runner.notification", + "org.junit.runners", + "org.junit.runners.model", + "org.junit.runners.parameterized", + "org.junit.validator" + ], + "net.java.dev.jna:jna": [ + "com.sun.jna", + "com.sun.jna.internal", + "com.sun.jna.ptr", + "com.sun.jna.win32" + ], + "org.apache.commons:commons-compress": [ + "org.apache.commons.compress", + "org.apache.commons.compress.archivers", + "org.apache.commons.compress.archivers.ar", + "org.apache.commons.compress.archivers.arj", + "org.apache.commons.compress.archivers.cpio", + "org.apache.commons.compress.archivers.dump", + "org.apache.commons.compress.archivers.examples", + "org.apache.commons.compress.archivers.jar", + "org.apache.commons.compress.archivers.sevenz", + "org.apache.commons.compress.archivers.tar", + "org.apache.commons.compress.archivers.zip", + "org.apache.commons.compress.changes", + "org.apache.commons.compress.compressors", + "org.apache.commons.compress.compressors.brotli", + "org.apache.commons.compress.compressors.bzip2", + "org.apache.commons.compress.compressors.deflate", + "org.apache.commons.compress.compressors.deflate64", + "org.apache.commons.compress.compressors.gzip", + "org.apache.commons.compress.compressors.lz4", + "org.apache.commons.compress.compressors.lz77support", + "org.apache.commons.compress.compressors.lzma", + "org.apache.commons.compress.compressors.lzw", + "org.apache.commons.compress.compressors.pack200", + "org.apache.commons.compress.compressors.snappy", + "org.apache.commons.compress.compressors.xz", + "org.apache.commons.compress.compressors.z", + "org.apache.commons.compress.compressors.zstandard", + "org.apache.commons.compress.harmony", + "org.apache.commons.compress.harmony.archive.internal.nls", + "org.apache.commons.compress.harmony.pack200", + "org.apache.commons.compress.harmony.unpack200", + "org.apache.commons.compress.harmony.unpack200.bytecode", + "org.apache.commons.compress.harmony.unpack200.bytecode.forms", + "org.apache.commons.compress.java.util.jar", + "org.apache.commons.compress.parallel", + "org.apache.commons.compress.utils" + ], + "org.checkerframework:checker-qual": [ + "org.checkerframework.checker.builder.qual", + "org.checkerframework.checker.calledmethods.qual", + "org.checkerframework.checker.compilermsgs.qual", + "org.checkerframework.checker.fenum.qual", + "org.checkerframework.checker.formatter.qual", + "org.checkerframework.checker.guieffect.qual", + "org.checkerframework.checker.i18n.qual", + "org.checkerframework.checker.i18nformatter.qual", + "org.checkerframework.checker.index.qual", + "org.checkerframework.checker.initialization.qual", + "org.checkerframework.checker.interning.qual", + "org.checkerframework.checker.lock.qual", + "org.checkerframework.checker.mustcall.qual", + "org.checkerframework.checker.nonempty.qual", + "org.checkerframework.checker.nullness.qual", + "org.checkerframework.checker.optional.qual", + "org.checkerframework.checker.propkey.qual", + "org.checkerframework.checker.regex.qual", + "org.checkerframework.checker.signature.qual", + "org.checkerframework.checker.signedness.qual", + "org.checkerframework.checker.sqlquotes.qual", + "org.checkerframework.checker.tainting.qual", + "org.checkerframework.checker.units.qual", + "org.checkerframework.common.aliasing.qual", + "org.checkerframework.common.initializedfields.qual", + "org.checkerframework.common.reflection.qual", + "org.checkerframework.common.returnsreceiver.qual", + "org.checkerframework.common.subtyping.qual", + "org.checkerframework.common.util.count.report.qual", + "org.checkerframework.common.value.qual", + "org.checkerframework.dataflow.qual", + "org.checkerframework.framework.qual" + ], + "org.codehaus.mojo:animal-sniffer-annotations": [ + "org.codehaus.mojo.animal_sniffer" + ], + "org.conscrypt:conscrypt-openjdk-uber": [ + "org.conscrypt", + "org.conscrypt.ct", + "org.conscrypt.io" + ], + "org.hamcrest:hamcrest-core": [ + "org.hamcrest", + "org.hamcrest.core", + "org.hamcrest.internal" + ], + "org.jetbrains:annotations": [ + "org.intellij.lang.annotations", + "org.jetbrains.annotations" + ], + "org.rnorth.duct-tape:duct-tape": [ + "org.rnorth.ducttape", + "org.rnorth.ducttape.circuitbreakers", + "org.rnorth.ducttape.inconsistents", + "org.rnorth.ducttape.ratelimits", + "org.rnorth.ducttape.timeouts", + "org.rnorth.ducttape.unreliables" + ], + "org.testcontainers:gcloud": [ + "org.testcontainers.containers" + ], + "org.testcontainers:testcontainers": [ + "org.testcontainers", + "org.testcontainers.containers", + "org.testcontainers.containers.output", + "org.testcontainers.containers.startupcheck", + "org.testcontainers.containers.traits", + "org.testcontainers.containers.wait.internal", + "org.testcontainers.containers.wait.strategy", + "org.testcontainers.core", + "org.testcontainers.dockerclient", + "org.testcontainers.images", + "org.testcontainers.images.builder", + "org.testcontainers.images.builder.dockerfile", + "org.testcontainers.images.builder.dockerfile.statement", + "org.testcontainers.images.builder.dockerfile.traits", + "org.testcontainers.images.builder.traits", + "org.testcontainers.jib", + "org.testcontainers.lifecycle", + "org.testcontainers.shaded.com.fasterxml.jackson.core", + "org.testcontainers.shaded.com.fasterxml.jackson.core.base", + "org.testcontainers.shaded.com.fasterxml.jackson.core.filter", + "org.testcontainers.shaded.com.fasterxml.jackson.core.format", + "org.testcontainers.shaded.com.fasterxml.jackson.core.io", + "org.testcontainers.shaded.com.fasterxml.jackson.core.json", + "org.testcontainers.shaded.com.fasterxml.jackson.core.sym", + "org.testcontainers.shaded.com.fasterxml.jackson.core.type", + "org.testcontainers.shaded.com.fasterxml.jackson.core.util", + "org.testcontainers.shaded.com.fasterxml.jackson.databind", + "org.testcontainers.shaded.com.fasterxml.jackson.databind.annotation", + "org.testcontainers.shaded.com.fasterxml.jackson.databind.cfg", + "org.testcontainers.shaded.com.fasterxml.jackson.databind.deser", + "org.testcontainers.shaded.com.fasterxml.jackson.databind.deser.impl", + "org.testcontainers.shaded.com.fasterxml.jackson.databind.deser.std", + "org.testcontainers.shaded.com.fasterxml.jackson.databind.exc", + "org.testcontainers.shaded.com.fasterxml.jackson.databind.ext", + "org.testcontainers.shaded.com.fasterxml.jackson.databind.introspect", + "org.testcontainers.shaded.com.fasterxml.jackson.databind.jsonFormatVisitors", + "org.testcontainers.shaded.com.fasterxml.jackson.databind.jsonschema", + "org.testcontainers.shaded.com.fasterxml.jackson.databind.jsontype", + "org.testcontainers.shaded.com.fasterxml.jackson.databind.jsontype.impl", + "org.testcontainers.shaded.com.fasterxml.jackson.databind.module", + "org.testcontainers.shaded.com.fasterxml.jackson.databind.node", + "org.testcontainers.shaded.com.fasterxml.jackson.databind.ser", + "org.testcontainers.shaded.com.fasterxml.jackson.databind.ser.impl", + "org.testcontainers.shaded.com.fasterxml.jackson.databind.ser.std", + "org.testcontainers.shaded.com.fasterxml.jackson.databind.type", + "org.testcontainers.shaded.com.fasterxml.jackson.databind.util", + "org.testcontainers.shaded.com.github.dockerjava.core", + "org.testcontainers.shaded.com.github.dockerjava.core.async", + "org.testcontainers.shaded.com.github.dockerjava.core.command", + "org.testcontainers.shaded.com.github.dockerjava.core.dockerfile", + "org.testcontainers.shaded.com.github.dockerjava.core.exception", + "org.testcontainers.shaded.com.github.dockerjava.core.exec", + "org.testcontainers.shaded.com.github.dockerjava.core.util", + "org.testcontainers.shaded.com.google.common.annotations", + "org.testcontainers.shaded.com.google.common.base", + "org.testcontainers.shaded.com.google.common.base.internal", + "org.testcontainers.shaded.com.google.common.cache", + "org.testcontainers.shaded.com.google.common.collect", + "org.testcontainers.shaded.com.google.common.escape", + "org.testcontainers.shaded.com.google.common.eventbus", + "org.testcontainers.shaded.com.google.common.graph", + "org.testcontainers.shaded.com.google.common.hash", + "org.testcontainers.shaded.com.google.common.html", + "org.testcontainers.shaded.com.google.common.io", + "org.testcontainers.shaded.com.google.common.math", + "org.testcontainers.shaded.com.google.common.net", + "org.testcontainers.shaded.com.google.common.primitives", + "org.testcontainers.shaded.com.google.common.reflect", + "org.testcontainers.shaded.com.google.common.util.concurrent", + "org.testcontainers.shaded.com.google.common.util.concurrent.internal", + "org.testcontainers.shaded.com.google.common.xml", + "org.testcontainers.shaded.com.google.errorprone.annotations", + "org.testcontainers.shaded.com.google.errorprone.annotations.concurrent", + "org.testcontainers.shaded.com.google.thirdparty.publicsuffix", + "org.testcontainers.shaded.com.trilead.ssh2", + "org.testcontainers.shaded.com.trilead.ssh2.auth", + "org.testcontainers.shaded.com.trilead.ssh2.channel", + "org.testcontainers.shaded.com.trilead.ssh2.crypto", + "org.testcontainers.shaded.com.trilead.ssh2.crypto.cipher", + "org.testcontainers.shaded.com.trilead.ssh2.crypto.dh", + "org.testcontainers.shaded.com.trilead.ssh2.crypto.digest", + "org.testcontainers.shaded.com.trilead.ssh2.log", + "org.testcontainers.shaded.com.trilead.ssh2.packets", + "org.testcontainers.shaded.com.trilead.ssh2.sftp", + "org.testcontainers.shaded.com.trilead.ssh2.signature", + "org.testcontainers.shaded.com.trilead.ssh2.transport", + "org.testcontainers.shaded.com.trilead.ssh2.util", + "org.testcontainers.shaded.org.apache.commons.io", + "org.testcontainers.shaded.org.apache.commons.io.build", + "org.testcontainers.shaded.org.apache.commons.io.charset", + "org.testcontainers.shaded.org.apache.commons.io.comparator", + "org.testcontainers.shaded.org.apache.commons.io.file", + "org.testcontainers.shaded.org.apache.commons.io.file.attribute", + "org.testcontainers.shaded.org.apache.commons.io.file.spi", + "org.testcontainers.shaded.org.apache.commons.io.filefilter", + "org.testcontainers.shaded.org.apache.commons.io.function", + "org.testcontainers.shaded.org.apache.commons.io.input", + "org.testcontainers.shaded.org.apache.commons.io.input.buffer", + "org.testcontainers.shaded.org.apache.commons.io.monitor", + "org.testcontainers.shaded.org.apache.commons.io.output", + "org.testcontainers.shaded.org.apache.commons.io.serialization", + "org.testcontainers.shaded.org.apache.commons.lang3", + "org.testcontainers.shaded.org.apache.commons.lang3.arch", + "org.testcontainers.shaded.org.apache.commons.lang3.builder", + "org.testcontainers.shaded.org.apache.commons.lang3.compare", + "org.testcontainers.shaded.org.apache.commons.lang3.concurrent", + "org.testcontainers.shaded.org.apache.commons.lang3.concurrent.locks", + "org.testcontainers.shaded.org.apache.commons.lang3.event", + "org.testcontainers.shaded.org.apache.commons.lang3.exception", + "org.testcontainers.shaded.org.apache.commons.lang3.function", + "org.testcontainers.shaded.org.apache.commons.lang3.math", + "org.testcontainers.shaded.org.apache.commons.lang3.mutable", + "org.testcontainers.shaded.org.apache.commons.lang3.reflect", + "org.testcontainers.shaded.org.apache.commons.lang3.stream", + "org.testcontainers.shaded.org.apache.commons.lang3.text", + "org.testcontainers.shaded.org.apache.commons.lang3.text.translate", + "org.testcontainers.shaded.org.apache.commons.lang3.time", + "org.testcontainers.shaded.org.apache.commons.lang3.tuple", + "org.testcontainers.shaded.org.awaitility", + "org.testcontainers.shaded.org.awaitility.classpath", + "org.testcontainers.shaded.org.awaitility.constraint", + "org.testcontainers.shaded.org.awaitility.core", + "org.testcontainers.shaded.org.awaitility.pollinterval", + "org.testcontainers.shaded.org.awaitility.reflect", + "org.testcontainers.shaded.org.awaitility.reflect.exception", + "org.testcontainers.shaded.org.awaitility.spi", + "org.testcontainers.shaded.org.bouncycastle", + "org.testcontainers.shaded.org.bouncycastle.asn1", + "org.testcontainers.shaded.org.bouncycastle.asn1.anssi", + "org.testcontainers.shaded.org.bouncycastle.asn1.bc", + "org.testcontainers.shaded.org.bouncycastle.asn1.bsi", + "org.testcontainers.shaded.org.bouncycastle.asn1.cmc", + "org.testcontainers.shaded.org.bouncycastle.asn1.cmp", + "org.testcontainers.shaded.org.bouncycastle.asn1.cms", + "org.testcontainers.shaded.org.bouncycastle.asn1.cms.ecc", + "org.testcontainers.shaded.org.bouncycastle.asn1.crmf", + "org.testcontainers.shaded.org.bouncycastle.asn1.cryptlib", + "org.testcontainers.shaded.org.bouncycastle.asn1.cryptopro", + "org.testcontainers.shaded.org.bouncycastle.asn1.dvcs", + "org.testcontainers.shaded.org.bouncycastle.asn1.eac", + "org.testcontainers.shaded.org.bouncycastle.asn1.edec", + "org.testcontainers.shaded.org.bouncycastle.asn1.esf", + "org.testcontainers.shaded.org.bouncycastle.asn1.ess", + "org.testcontainers.shaded.org.bouncycastle.asn1.est", + "org.testcontainers.shaded.org.bouncycastle.asn1.gm", + "org.testcontainers.shaded.org.bouncycastle.asn1.gnu", + "org.testcontainers.shaded.org.bouncycastle.asn1.iana", + "org.testcontainers.shaded.org.bouncycastle.asn1.icao", + "org.testcontainers.shaded.org.bouncycastle.asn1.isara", + "org.testcontainers.shaded.org.bouncycastle.asn1.isismtt", + "org.testcontainers.shaded.org.bouncycastle.asn1.isismtt.ocsp", + "org.testcontainers.shaded.org.bouncycastle.asn1.isismtt.x509", + "org.testcontainers.shaded.org.bouncycastle.asn1.iso", + "org.testcontainers.shaded.org.bouncycastle.asn1.kisa", + "org.testcontainers.shaded.org.bouncycastle.asn1.microsoft", + "org.testcontainers.shaded.org.bouncycastle.asn1.misc", + "org.testcontainers.shaded.org.bouncycastle.asn1.mozilla", + "org.testcontainers.shaded.org.bouncycastle.asn1.nist", + "org.testcontainers.shaded.org.bouncycastle.asn1.nsri", + "org.testcontainers.shaded.org.bouncycastle.asn1.ntt", + "org.testcontainers.shaded.org.bouncycastle.asn1.ocsp", + "org.testcontainers.shaded.org.bouncycastle.asn1.oiw", + "org.testcontainers.shaded.org.bouncycastle.asn1.pkcs", + "org.testcontainers.shaded.org.bouncycastle.asn1.rosstandart", + "org.testcontainers.shaded.org.bouncycastle.asn1.sec", + "org.testcontainers.shaded.org.bouncycastle.asn1.smime", + "org.testcontainers.shaded.org.bouncycastle.asn1.teletrust", + "org.testcontainers.shaded.org.bouncycastle.asn1.tsp", + "org.testcontainers.shaded.org.bouncycastle.asn1.ua", + "org.testcontainers.shaded.org.bouncycastle.asn1.util", + "org.testcontainers.shaded.org.bouncycastle.asn1.x500", + "org.testcontainers.shaded.org.bouncycastle.asn1.x500.style", + "org.testcontainers.shaded.org.bouncycastle.asn1.x509", + "org.testcontainers.shaded.org.bouncycastle.asn1.x509.qualified", + "org.testcontainers.shaded.org.bouncycastle.asn1.x509.sigi", + "org.testcontainers.shaded.org.bouncycastle.asn1.x9", + "org.testcontainers.shaded.org.bouncycastle.cert", + "org.testcontainers.shaded.org.bouncycastle.cert.bc", + "org.testcontainers.shaded.org.bouncycastle.cert.cmp", + "org.testcontainers.shaded.org.bouncycastle.cert.crmf", + "org.testcontainers.shaded.org.bouncycastle.cert.crmf.bc", + "org.testcontainers.shaded.org.bouncycastle.cert.crmf.jcajce", + "org.testcontainers.shaded.org.bouncycastle.cert.dane", + "org.testcontainers.shaded.org.bouncycastle.cert.dane.fetcher", + "org.testcontainers.shaded.org.bouncycastle.cert.jcajce", + "org.testcontainers.shaded.org.bouncycastle.cert.ocsp", + "org.testcontainers.shaded.org.bouncycastle.cert.ocsp.jcajce", + "org.testcontainers.shaded.org.bouncycastle.cert.path", + "org.testcontainers.shaded.org.bouncycastle.cert.path.validations", + "org.testcontainers.shaded.org.bouncycastle.cert.selector", + "org.testcontainers.shaded.org.bouncycastle.cert.selector.jcajce", + "org.testcontainers.shaded.org.bouncycastle.cmc", + "org.testcontainers.shaded.org.bouncycastle.cms", + "org.testcontainers.shaded.org.bouncycastle.cms.bc", + "org.testcontainers.shaded.org.bouncycastle.cms.jcajce", + "org.testcontainers.shaded.org.bouncycastle.crypto", + "org.testcontainers.shaded.org.bouncycastle.crypto.agreement", + "org.testcontainers.shaded.org.bouncycastle.crypto.agreement.jpake", + "org.testcontainers.shaded.org.bouncycastle.crypto.agreement.kdf", + "org.testcontainers.shaded.org.bouncycastle.crypto.agreement.srp", + "org.testcontainers.shaded.org.bouncycastle.crypto.commitments", + "org.testcontainers.shaded.org.bouncycastle.crypto.constraints", + "org.testcontainers.shaded.org.bouncycastle.crypto.digests", + "org.testcontainers.shaded.org.bouncycastle.crypto.ec", + "org.testcontainers.shaded.org.bouncycastle.crypto.encodings", + "org.testcontainers.shaded.org.bouncycastle.crypto.engines", + "org.testcontainers.shaded.org.bouncycastle.crypto.examples", + "org.testcontainers.shaded.org.bouncycastle.crypto.fpe", + "org.testcontainers.shaded.org.bouncycastle.crypto.generators", + "org.testcontainers.shaded.org.bouncycastle.crypto.hpke", + "org.testcontainers.shaded.org.bouncycastle.crypto.io", + "org.testcontainers.shaded.org.bouncycastle.crypto.kems", + "org.testcontainers.shaded.org.bouncycastle.crypto.macs", + "org.testcontainers.shaded.org.bouncycastle.crypto.modes", + "org.testcontainers.shaded.org.bouncycastle.crypto.modes.gcm", + "org.testcontainers.shaded.org.bouncycastle.crypto.modes.kgcm", + "org.testcontainers.shaded.org.bouncycastle.crypto.paddings", + "org.testcontainers.shaded.org.bouncycastle.crypto.params", + "org.testcontainers.shaded.org.bouncycastle.crypto.parsers", + "org.testcontainers.shaded.org.bouncycastle.crypto.prng", + "org.testcontainers.shaded.org.bouncycastle.crypto.prng.drbg", + "org.testcontainers.shaded.org.bouncycastle.crypto.signers", + "org.testcontainers.shaded.org.bouncycastle.crypto.util", + "org.testcontainers.shaded.org.bouncycastle.dvcs", + "org.testcontainers.shaded.org.bouncycastle.eac", + "org.testcontainers.shaded.org.bouncycastle.eac.jcajce", + "org.testcontainers.shaded.org.bouncycastle.eac.operator", + "org.testcontainers.shaded.org.bouncycastle.eac.operator.jcajce", + "org.testcontainers.shaded.org.bouncycastle.est", + "org.testcontainers.shaded.org.bouncycastle.est.jcajce", + "org.testcontainers.shaded.org.bouncycastle.i18n", + "org.testcontainers.shaded.org.bouncycastle.i18n.filter", + "org.testcontainers.shaded.org.bouncycastle.iana", + "org.testcontainers.shaded.org.bouncycastle.internal.asn1.bsi", + "org.testcontainers.shaded.org.bouncycastle.internal.asn1.cms", + "org.testcontainers.shaded.org.bouncycastle.internal.asn1.eac", + "org.testcontainers.shaded.org.bouncycastle.internal.asn1.isismtt", + "org.testcontainers.shaded.org.bouncycastle.its", + "org.testcontainers.shaded.org.bouncycastle.its.bc", + "org.testcontainers.shaded.org.bouncycastle.its.jcajce", + "org.testcontainers.shaded.org.bouncycastle.its.operator", + "org.testcontainers.shaded.org.bouncycastle.jcajce", + "org.testcontainers.shaded.org.bouncycastle.jcajce.interfaces", + "org.testcontainers.shaded.org.bouncycastle.jcajce.io", + "org.testcontainers.shaded.org.bouncycastle.jcajce.provider.asymmetric", + "org.testcontainers.shaded.org.bouncycastle.jcajce.provider.asymmetric.dh", + "org.testcontainers.shaded.org.bouncycastle.jcajce.provider.asymmetric.dsa", + "org.testcontainers.shaded.org.bouncycastle.jcajce.provider.asymmetric.dstu", + "org.testcontainers.shaded.org.bouncycastle.jcajce.provider.asymmetric.ec", + "org.testcontainers.shaded.org.bouncycastle.jcajce.provider.asymmetric.ecgost", + "org.testcontainers.shaded.org.bouncycastle.jcajce.provider.asymmetric.ecgost12", + "org.testcontainers.shaded.org.bouncycastle.jcajce.provider.asymmetric.edec", + "org.testcontainers.shaded.org.bouncycastle.jcajce.provider.asymmetric.elgamal", + "org.testcontainers.shaded.org.bouncycastle.jcajce.provider.asymmetric.gost", + "org.testcontainers.shaded.org.bouncycastle.jcajce.provider.asymmetric.ies", + "org.testcontainers.shaded.org.bouncycastle.jcajce.provider.asymmetric.rsa", + "org.testcontainers.shaded.org.bouncycastle.jcajce.provider.asymmetric.util", + "org.testcontainers.shaded.org.bouncycastle.jcajce.provider.asymmetric.x509", + "org.testcontainers.shaded.org.bouncycastle.jcajce.provider.config", + "org.testcontainers.shaded.org.bouncycastle.jcajce.provider.digest", + "org.testcontainers.shaded.org.bouncycastle.jcajce.provider.drbg", + "org.testcontainers.shaded.org.bouncycastle.jcajce.provider.keystore", + "org.testcontainers.shaded.org.bouncycastle.jcajce.provider.keystore.bc", + "org.testcontainers.shaded.org.bouncycastle.jcajce.provider.keystore.bcfks", + "org.testcontainers.shaded.org.bouncycastle.jcajce.provider.keystore.pkcs12", + "org.testcontainers.shaded.org.bouncycastle.jcajce.provider.keystore.util", + "org.testcontainers.shaded.org.bouncycastle.jcajce.provider.symmetric", + "org.testcontainers.shaded.org.bouncycastle.jcajce.provider.symmetric.util", + "org.testcontainers.shaded.org.bouncycastle.jcajce.provider.util", + "org.testcontainers.shaded.org.bouncycastle.jcajce.spec", + "org.testcontainers.shaded.org.bouncycastle.jcajce.util", + "org.testcontainers.shaded.org.bouncycastle.jce", + "org.testcontainers.shaded.org.bouncycastle.jce.exception", + "org.testcontainers.shaded.org.bouncycastle.jce.interfaces", + "org.testcontainers.shaded.org.bouncycastle.jce.netscape", + "org.testcontainers.shaded.org.bouncycastle.jce.provider", + "org.testcontainers.shaded.org.bouncycastle.jce.spec", + "org.testcontainers.shaded.org.bouncycastle.math", + "org.testcontainers.shaded.org.bouncycastle.math.ec", + "org.testcontainers.shaded.org.bouncycastle.math.ec.custom.djb", + "org.testcontainers.shaded.org.bouncycastle.math.ec.custom.gm", + "org.testcontainers.shaded.org.bouncycastle.math.ec.custom.sec", + "org.testcontainers.shaded.org.bouncycastle.math.ec.endo", + "org.testcontainers.shaded.org.bouncycastle.math.ec.rfc7748", + "org.testcontainers.shaded.org.bouncycastle.math.ec.rfc8032", + "org.testcontainers.shaded.org.bouncycastle.math.ec.tools", + "org.testcontainers.shaded.org.bouncycastle.math.field", + "org.testcontainers.shaded.org.bouncycastle.math.raw", + "org.testcontainers.shaded.org.bouncycastle.mime", + "org.testcontainers.shaded.org.bouncycastle.mime.encoding", + "org.testcontainers.shaded.org.bouncycastle.mime.smime", + "org.testcontainers.shaded.org.bouncycastle.mozilla", + "org.testcontainers.shaded.org.bouncycastle.mozilla.jcajce", + "org.testcontainers.shaded.org.bouncycastle.oer", + "org.testcontainers.shaded.org.bouncycastle.oer.its", + "org.testcontainers.shaded.org.bouncycastle.oer.its.etsi102941", + "org.testcontainers.shaded.org.bouncycastle.oer.its.etsi102941.basetypes", + "org.testcontainers.shaded.org.bouncycastle.oer.its.etsi103097", + "org.testcontainers.shaded.org.bouncycastle.oer.its.etsi103097.extension", + "org.testcontainers.shaded.org.bouncycastle.oer.its.ieee1609dot2", + "org.testcontainers.shaded.org.bouncycastle.oer.its.ieee1609dot2.basetypes", + "org.testcontainers.shaded.org.bouncycastle.oer.its.ieee1609dot2dot1", + "org.testcontainers.shaded.org.bouncycastle.oer.its.template.etsi102941", + "org.testcontainers.shaded.org.bouncycastle.oer.its.template.etsi102941.basetypes", + "org.testcontainers.shaded.org.bouncycastle.oer.its.template.etsi103097", + "org.testcontainers.shaded.org.bouncycastle.oer.its.template.etsi103097.extension", + "org.testcontainers.shaded.org.bouncycastle.oer.its.template.ieee1609dot2", + "org.testcontainers.shaded.org.bouncycastle.oer.its.template.ieee1609dot2.basetypes", + "org.testcontainers.shaded.org.bouncycastle.oer.its.template.ieee1609dot2dot1", + "org.testcontainers.shaded.org.bouncycastle.openssl", + "org.testcontainers.shaded.org.bouncycastle.openssl.bc", + "org.testcontainers.shaded.org.bouncycastle.openssl.jcajce", + "org.testcontainers.shaded.org.bouncycastle.operator", + "org.testcontainers.shaded.org.bouncycastle.operator.bc", + "org.testcontainers.shaded.org.bouncycastle.operator.jcajce", + "org.testcontainers.shaded.org.bouncycastle.pkcs", + "org.testcontainers.shaded.org.bouncycastle.pkcs.bc", + "org.testcontainers.shaded.org.bouncycastle.pkcs.jcajce", + "org.testcontainers.shaded.org.bouncycastle.pkix", + "org.testcontainers.shaded.org.bouncycastle.pkix.jcajce", + "org.testcontainers.shaded.org.bouncycastle.pkix.util", + "org.testcontainers.shaded.org.bouncycastle.pkix.util.filter", + "org.testcontainers.shaded.org.bouncycastle.pqc.asn1", + "org.testcontainers.shaded.org.bouncycastle.pqc.crypto", + "org.testcontainers.shaded.org.bouncycastle.pqc.crypto.bike", + "org.testcontainers.shaded.org.bouncycastle.pqc.crypto.cmce", + "org.testcontainers.shaded.org.bouncycastle.pqc.crypto.crystals.dilithium", + "org.testcontainers.shaded.org.bouncycastle.pqc.crypto.crystals.kyber", + "org.testcontainers.shaded.org.bouncycastle.pqc.crypto.falcon", + "org.testcontainers.shaded.org.bouncycastle.pqc.crypto.frodo", + "org.testcontainers.shaded.org.bouncycastle.pqc.crypto.gemss", + "org.testcontainers.shaded.org.bouncycastle.pqc.crypto.hqc", + "org.testcontainers.shaded.org.bouncycastle.pqc.crypto.lms", + "org.testcontainers.shaded.org.bouncycastle.pqc.crypto.newhope", + "org.testcontainers.shaded.org.bouncycastle.pqc.crypto.ntru", + "org.testcontainers.shaded.org.bouncycastle.pqc.crypto.ntruprime", + "org.testcontainers.shaded.org.bouncycastle.pqc.crypto.picnic", + "org.testcontainers.shaded.org.bouncycastle.pqc.crypto.rainbow", + "org.testcontainers.shaded.org.bouncycastle.pqc.crypto.saber", + "org.testcontainers.shaded.org.bouncycastle.pqc.crypto.sphincs", + "org.testcontainers.shaded.org.bouncycastle.pqc.crypto.sphincsplus", + "org.testcontainers.shaded.org.bouncycastle.pqc.crypto.util", + "org.testcontainers.shaded.org.bouncycastle.pqc.crypto.xmss", + "org.testcontainers.shaded.org.bouncycastle.pqc.jcajce.interfaces", + "org.testcontainers.shaded.org.bouncycastle.pqc.jcajce.provider", + "org.testcontainers.shaded.org.bouncycastle.pqc.jcajce.provider.bike", + "org.testcontainers.shaded.org.bouncycastle.pqc.jcajce.provider.cmce", + "org.testcontainers.shaded.org.bouncycastle.pqc.jcajce.provider.dilithium", + "org.testcontainers.shaded.org.bouncycastle.pqc.jcajce.provider.falcon", + "org.testcontainers.shaded.org.bouncycastle.pqc.jcajce.provider.frodo", + "org.testcontainers.shaded.org.bouncycastle.pqc.jcajce.provider.gmss", + "org.testcontainers.shaded.org.bouncycastle.pqc.jcajce.provider.hqc", + "org.testcontainers.shaded.org.bouncycastle.pqc.jcajce.provider.kyber", + "org.testcontainers.shaded.org.bouncycastle.pqc.jcajce.provider.lms", + "org.testcontainers.shaded.org.bouncycastle.pqc.jcajce.provider.mceliece", + "org.testcontainers.shaded.org.bouncycastle.pqc.jcajce.provider.newhope", + "org.testcontainers.shaded.org.bouncycastle.pqc.jcajce.provider.ntru", + "org.testcontainers.shaded.org.bouncycastle.pqc.jcajce.provider.ntruprime", + "org.testcontainers.shaded.org.bouncycastle.pqc.jcajce.provider.picnic", + "org.testcontainers.shaded.org.bouncycastle.pqc.jcajce.provider.rainbow", + "org.testcontainers.shaded.org.bouncycastle.pqc.jcajce.provider.saber", + "org.testcontainers.shaded.org.bouncycastle.pqc.jcajce.provider.sphincs", + "org.testcontainers.shaded.org.bouncycastle.pqc.jcajce.provider.sphincsplus", + "org.testcontainers.shaded.org.bouncycastle.pqc.jcajce.provider.util", + "org.testcontainers.shaded.org.bouncycastle.pqc.jcajce.provider.xmss", + "org.testcontainers.shaded.org.bouncycastle.pqc.jcajce.spec", + "org.testcontainers.shaded.org.bouncycastle.pqc.legacy.crypto.gmss", + "org.testcontainers.shaded.org.bouncycastle.pqc.legacy.crypto.gmss.util", + "org.testcontainers.shaded.org.bouncycastle.pqc.legacy.crypto.mceliece", + "org.testcontainers.shaded.org.bouncycastle.pqc.legacy.crypto.qtesla", + "org.testcontainers.shaded.org.bouncycastle.pqc.legacy.crypto.rainbow", + "org.testcontainers.shaded.org.bouncycastle.pqc.legacy.crypto.rainbow.util", + "org.testcontainers.shaded.org.bouncycastle.pqc.legacy.math.linearalgebra", + "org.testcontainers.shaded.org.bouncycastle.pqc.math.ntru", + "org.testcontainers.shaded.org.bouncycastle.pqc.math.ntru.parameters", + "org.testcontainers.shaded.org.bouncycastle.tsp", + "org.testcontainers.shaded.org.bouncycastle.tsp.cms", + "org.testcontainers.shaded.org.bouncycastle.tsp.ers", + "org.testcontainers.shaded.org.bouncycastle.util", + "org.testcontainers.shaded.org.bouncycastle.util.encoders", + "org.testcontainers.shaded.org.bouncycastle.util.io", + "org.testcontainers.shaded.org.bouncycastle.util.io.pem", + "org.testcontainers.shaded.org.bouncycastle.util.test", + "org.testcontainers.shaded.org.bouncycastle.voms", + "org.testcontainers.shaded.org.bouncycastle.x509", + "org.testcontainers.shaded.org.bouncycastle.x509.extension", + "org.testcontainers.shaded.org.bouncycastle.x509.util", + "org.testcontainers.shaded.org.checkerframework.checker.builder.qual", + "org.testcontainers.shaded.org.checkerframework.checker.calledmethods.qual", + "org.testcontainers.shaded.org.checkerframework.checker.compilermsgs.qual", + "org.testcontainers.shaded.org.checkerframework.checker.fenum.qual", + "org.testcontainers.shaded.org.checkerframework.checker.formatter.qual", + "org.testcontainers.shaded.org.checkerframework.checker.guieffect.qual", + "org.testcontainers.shaded.org.checkerframework.checker.i18n.qual", + "org.testcontainers.shaded.org.checkerframework.checker.i18nformatter.qual", + "org.testcontainers.shaded.org.checkerframework.checker.index.qual", + "org.testcontainers.shaded.org.checkerframework.checker.initialization.qual", + "org.testcontainers.shaded.org.checkerframework.checker.interning.qual", + "org.testcontainers.shaded.org.checkerframework.checker.lock.qual", + "org.testcontainers.shaded.org.checkerframework.checker.mustcall.qual", + "org.testcontainers.shaded.org.checkerframework.checker.nullness.qual", + "org.testcontainers.shaded.org.checkerframework.checker.optional.qual", + "org.testcontainers.shaded.org.checkerframework.checker.propkey.qual", + "org.testcontainers.shaded.org.checkerframework.checker.regex.qual", + "org.testcontainers.shaded.org.checkerframework.checker.signature.qual", + "org.testcontainers.shaded.org.checkerframework.checker.signedness.qual", + "org.testcontainers.shaded.org.checkerframework.checker.tainting.qual", + "org.testcontainers.shaded.org.checkerframework.checker.units.qual", + "org.testcontainers.shaded.org.checkerframework.common.aliasing.qual", + "org.testcontainers.shaded.org.checkerframework.common.initializedfields.qual", + "org.testcontainers.shaded.org.checkerframework.common.reflection.qual", + "org.testcontainers.shaded.org.checkerframework.common.returnsreceiver.qual", + "org.testcontainers.shaded.org.checkerframework.common.subtyping.qual", + "org.testcontainers.shaded.org.checkerframework.common.util.count.report.qual", + "org.testcontainers.shaded.org.checkerframework.common.value.qual", + "org.testcontainers.shaded.org.checkerframework.dataflow.qual", + "org.testcontainers.shaded.org.checkerframework.framework.qual", + "org.testcontainers.shaded.org.hamcrest", + "org.testcontainers.shaded.org.hamcrest.beans", + "org.testcontainers.shaded.org.hamcrest.collection", + "org.testcontainers.shaded.org.hamcrest.comparator", + "org.testcontainers.shaded.org.hamcrest.core", + "org.testcontainers.shaded.org.hamcrest.internal", + "org.testcontainers.shaded.org.hamcrest.io", + "org.testcontainers.shaded.org.hamcrest.number", + "org.testcontainers.shaded.org.hamcrest.object", + "org.testcontainers.shaded.org.hamcrest.text", + "org.testcontainers.shaded.org.hamcrest.xml", + "org.testcontainers.shaded.org.yaml.snakeyaml", + "org.testcontainers.shaded.org.yaml.snakeyaml.comments", + "org.testcontainers.shaded.org.yaml.snakeyaml.composer", + "org.testcontainers.shaded.org.yaml.snakeyaml.constructor", + "org.testcontainers.shaded.org.yaml.snakeyaml.emitter", + "org.testcontainers.shaded.org.yaml.snakeyaml.env", + "org.testcontainers.shaded.org.yaml.snakeyaml.error", + "org.testcontainers.shaded.org.yaml.snakeyaml.events", + "org.testcontainers.shaded.org.yaml.snakeyaml.extensions.compactnotation", + "org.testcontainers.shaded.org.yaml.snakeyaml.external.biz.base64Coder", + "org.testcontainers.shaded.org.yaml.snakeyaml.external.com.google.gdata.util.common.base", + "org.testcontainers.shaded.org.yaml.snakeyaml.introspector", + "org.testcontainers.shaded.org.yaml.snakeyaml.nodes", + "org.testcontainers.shaded.org.yaml.snakeyaml.parser", + "org.testcontainers.shaded.org.yaml.snakeyaml.reader", + "org.testcontainers.shaded.org.yaml.snakeyaml.representer", + "org.testcontainers.shaded.org.yaml.snakeyaml.resolver", + "org.testcontainers.shaded.org.yaml.snakeyaml.scanner", + "org.testcontainers.shaded.org.yaml.snakeyaml.serializer", + "org.testcontainers.shaded.org.yaml.snakeyaml.tokens", + "org.testcontainers.shaded.org.yaml.snakeyaml.util", + "org.testcontainers.shaded.org.zeroturnaround.exec", + "org.testcontainers.shaded.org.zeroturnaround.exec.close", + "org.testcontainers.shaded.org.zeroturnaround.exec.listener", + "org.testcontainers.shaded.org.zeroturnaround.exec.stop", + "org.testcontainers.shaded.org.zeroturnaround.exec.stream", + "org.testcontainers.shaded.org.zeroturnaround.exec.stream.slf4j", + "org.testcontainers.utility" + ], + "org.threeten:threetenbp": [ + "org.threeten.bp", + "org.threeten.bp.chrono", + "org.threeten.bp.format", + "org.threeten.bp.jdk8", + "org.threeten.bp.temporal", + "org.threeten.bp.zone" + ] + }, + "repositories": { + "https://repo1.maven.org/maven2/": [ + "com.fasterxml.jackson.core:jackson-annotations", + "com.fasterxml.jackson.core:jackson-annotations:jar:sources", + "com.github.docker-java:docker-java-api", + "com.github.docker-java:docker-java-api:jar:sources", + "com.github.docker-java:docker-java-transport", + "com.github.docker-java:docker-java-transport-zerodep", + "com.github.docker-java:docker-java-transport-zerodep:jar:sources", + "com.github.docker-java:docker-java-transport:jar:sources", + "com.google.android:annotations", + "com.google.android:annotations:jar:sources", + "com.google.api.grpc:proto-google-cloud-pubsub-v1", + "com.google.api.grpc:proto-google-cloud-pubsub-v1:jar:sources", + "com.google.api.grpc:proto-google-common-protos", + "com.google.api.grpc:proto-google-common-protos:jar:sources", + "com.google.api.grpc:proto-google-iam-v1", + "com.google.api.grpc:proto-google-iam-v1:jar:sources", + "com.google.api:api-common", + "com.google.api:api-common:jar:sources", + "com.google.api:gax", + "com.google.api:gax-grpc", + "com.google.api:gax-grpc:jar:sources", + "com.google.api:gax-httpjson", + "com.google.api:gax-httpjson:jar:sources", + "com.google.api:gax:jar:sources", + "com.google.auth:google-auth-library-credentials", + "com.google.auth:google-auth-library-credentials:jar:sources", + "com.google.auth:google-auth-library-oauth2-http", + "com.google.auth:google-auth-library-oauth2-http:jar:sources", + "com.google.auto.value:auto-value-annotations", + "com.google.auto.value:auto-value-annotations:jar:sources", + "com.google.cloud:google-cloud-pubsub", + "com.google.cloud:google-cloud-pubsub:jar:sources", + "com.google.errorprone:error_prone_annotations", + "com.google.errorprone:error_prone_annotations:jar:sources", + "com.google.guava:guava", + "com.google.guava:guava:jar:sources", + "com.google.guava:listenablefuture", + "com.google.http-client:google-http-client", + "com.google.http-client:google-http-client-gson", + "com.google.http-client:google-http-client-gson:jar:sources", + "com.google.http-client:google-http-client:jar:sources", + "com.google.protobuf:protobuf-java-util", + "com.google.protobuf:protobuf-java-util:jar:sources", + "com.google.re2j:re2j", + "com.google.re2j:re2j:jar:sources", + "io.grpc:grpc-alts", + "io.grpc:grpc-alts:jar:sources", + "io.grpc:grpc-api", + "io.grpc:grpc-api:jar:sources", + "io.grpc:grpc-auth", + "io.grpc:grpc-auth:jar:sources", + "io.grpc:grpc-context", + "io.grpc:grpc-context:jar:sources", + "io.grpc:grpc-core", + "io.grpc:grpc-core:jar:sources", + "io.grpc:grpc-googleapis", + "io.grpc:grpc-googleapis:jar:sources", + "io.grpc:grpc-grpclb", + "io.grpc:grpc-grpclb:jar:sources", + "io.grpc:grpc-inprocess", + "io.grpc:grpc-inprocess:jar:sources", + "io.grpc:grpc-netty-shaded", + "io.grpc:grpc-netty-shaded:jar:sources", + "io.grpc:grpc-protobuf", + "io.grpc:grpc-protobuf-lite", + "io.grpc:grpc-protobuf-lite:jar:sources", + "io.grpc:grpc-protobuf:jar:sources", + "io.grpc:grpc-services", + "io.grpc:grpc-services:jar:sources", + "io.grpc:grpc-stub", + "io.grpc:grpc-stub:jar:sources", + "io.grpc:grpc-util", + "io.grpc:grpc-util:jar:sources", + "io.grpc:grpc-xds", + "io.grpc:grpc-xds:jar:sources", + "io.opencensus:opencensus-api", + "io.opencensus:opencensus-api:jar:sources", + "io.opencensus:opencensus-contrib-http-util", + "io.opencensus:opencensus-contrib-http-util:jar:sources", + "io.opentelemetry:opentelemetry-api", + "io.opentelemetry:opentelemetry-api:jar:sources", + "io.opentelemetry:opentelemetry-context", + "io.opentelemetry:opentelemetry-context:jar:sources", + "io.opentelemetry:opentelemetry-semconv", + "io.opentelemetry:opentelemetry-semconv:jar:sources", + "io.perfmark:perfmark-api", + "io.perfmark:perfmark-api:jar:sources", + "javax.annotation:javax.annotation-api", + "javax.annotation:javax.annotation-api:jar:sources", + "junit:junit", + "junit:junit:jar:sources", + "net.java.dev.jna:jna", + "net.java.dev.jna:jna:jar:sources", + "org.apache.commons:commons-compress", + "org.apache.commons:commons-compress:jar:sources", + "org.checkerframework:checker-qual", + "org.checkerframework:checker-qual:jar:sources", + "org.codehaus.mojo:animal-sniffer-annotations", + "org.codehaus.mojo:animal-sniffer-annotations:jar:sources", + "org.conscrypt:conscrypt-openjdk-uber", + "org.conscrypt:conscrypt-openjdk-uber:jar:sources", + "org.hamcrest:hamcrest-core", + "org.hamcrest:hamcrest-core:jar:sources", + "org.jetbrains:annotations", + "org.jetbrains:annotations:jar:sources", + "org.rnorth.duct-tape:duct-tape", + "org.rnorth.duct-tape:duct-tape:jar:sources", + "org.testcontainers:gcloud", + "org.testcontainers:gcloud:jar:sources", + "org.testcontainers:testcontainers", + "org.testcontainers:testcontainers:jar:sources", + "org.threeten:threetenbp", + "org.threeten:threetenbp:jar:sources" + ] + }, + "services": { + "io.grpc:grpc-core": { + "io.grpc.LoadBalancerProvider": [ + "io.grpc.internal.PickFirstLoadBalancerProvider" + ], + "io.grpc.NameResolverProvider": [ + "io.grpc.internal.DnsNameResolverProvider" + ] + }, + "io.grpc:grpc-core:jar:sources": { + "io.grpc.LoadBalancerProvider": [ + "io.grpc.internal.PickFirstLoadBalancerProvider" + ], + "io.grpc.NameResolverProvider": [ + "io.grpc.internal.DnsNameResolverProvider" + ] + }, + "io.grpc:grpc-googleapis": { + "io.grpc.NameResolverProvider": [ + "io.grpc.googleapis.GoogleCloudToProdExperimentalNameResolverProvider", + "io.grpc.googleapis.GoogleCloudToProdNameResolverProvider" + ] + }, + "io.grpc:grpc-googleapis:jar:sources": { + "io.grpc.NameResolverProvider": [ + "io.grpc.googleapis.GoogleCloudToProdExperimentalNameResolverProvider", + "io.grpc.googleapis.GoogleCloudToProdNameResolverProvider" + ] + }, + "io.grpc:grpc-grpclb": { + "io.grpc.LoadBalancerProvider": [ + "io.grpc.grpclb.GrpclbLoadBalancerProvider" + ], + "io.grpc.NameResolverProvider": [ + "io.grpc.grpclb.SecretGrpclbNameResolverProvider$Provider" + ] + }, + "io.grpc:grpc-grpclb:jar:sources": { + "io.grpc.LoadBalancerProvider": [ + "io.grpc.grpclb.GrpclbLoadBalancerProvider" + ], + "io.grpc.NameResolverProvider": [ + "io.grpc.grpclb.SecretGrpclbNameResolverProvider$Provider" + ] + }, + "io.grpc:grpc-netty-shaded": { + "io.grpc.ManagedChannelProvider": [ + "io.grpc.netty.shaded.io.grpc.netty.NettyChannelProvider", + "io.grpc.netty.shaded.io.grpc.netty.UdsNettyChannelProvider" + ], + "io.grpc.NameResolverProvider": [ + "io.grpc.netty.shaded.io.grpc.netty.UdsNameResolverProvider" + ], + "io.grpc.ServerProvider": [ + "io.grpc.netty.shaded.io.grpc.netty.NettyServerProvider" + ], + "reactor.blockhound.integration.BlockHoundIntegration": [ + "io.grpc.netty.shaded.io.netty.util.internal.Hidden$NettyBlockHoundIntegration" + ] + }, + "io.grpc:grpc-services": { + "io.grpc.LoadBalancerProvider": [ + "io.grpc.protobuf.services.internal.HealthCheckingRoundRobinLoadBalancerProvider" + ] + }, + "io.grpc:grpc-services:jar:sources": { + "io.grpc.LoadBalancerProvider": [ + "io.grpc.protobuf.services.internal.HealthCheckingRoundRobinLoadBalancerProvider" + ] + }, + "io.grpc:grpc-util": { + "io.grpc.LoadBalancerProvider": [ + "io.grpc.util.OutlierDetectionLoadBalancerProvider", + "io.grpc.util.SecretRoundRobinLoadBalancerProvider$Provider" + ] + }, + "io.grpc:grpc-util:jar:sources": { + "io.grpc.LoadBalancerProvider": [ + "io.grpc.util.OutlierDetectionLoadBalancerProvider", + "io.grpc.util.SecretRoundRobinLoadBalancerProvider$Provider" + ] + }, + "io.grpc:grpc-xds": { + "io.grpc.LoadBalancerProvider": [ + "io.grpc.xds.CdsLoadBalancerProvider", + "io.grpc.xds.ClusterImplLoadBalancerProvider", + "io.grpc.xds.ClusterManagerLoadBalancerProvider", + "io.grpc.xds.ClusterResolverLoadBalancerProvider", + "io.grpc.xds.LeastRequestLoadBalancerProvider", + "io.grpc.xds.PriorityLoadBalancerProvider", + "io.grpc.xds.RingHashLoadBalancerProvider", + "io.grpc.xds.WeightedRoundRobinLoadBalancerProvider", + "io.grpc.xds.WeightedTargetLoadBalancerProvider", + "io.grpc.xds.WrrLocalityLoadBalancerProvider" + ], + "io.grpc.NameResolverProvider": [ + "io.grpc.xds.XdsNameResolverProvider" + ], + "io.grpc.xds.XdsCredentialsProvider": [ + "io.grpc.xds.internal.GoogleDefaultXdsCredentialsProvider", + "io.grpc.xds.internal.InsecureXdsCredentialsProvider", + "io.grpc.xds.internal.TlsXdsCredentialsProvider" + ] + }, + "io.grpc:grpc-xds:jar:sources": { + "io.grpc.LoadBalancerProvider": [ + "io.grpc.xds.CdsLoadBalancerProvider", + "io.grpc.xds.ClusterImplLoadBalancerProvider", + "io.grpc.xds.ClusterManagerLoadBalancerProvider", + "io.grpc.xds.ClusterResolverLoadBalancerProvider", + "io.grpc.xds.LeastRequestLoadBalancerProvider", + "io.grpc.xds.PriorityLoadBalancerProvider", + "io.grpc.xds.RingHashLoadBalancerProvider", + "io.grpc.xds.WeightedRoundRobinLoadBalancerProvider", + "io.grpc.xds.WeightedTargetLoadBalancerProvider", + "io.grpc.xds.WrrLocalityLoadBalancerProvider" + ], + "io.grpc.NameResolverProvider": [ + "io.grpc.xds.XdsNameResolverProvider" + ], + "io.grpc.xds.XdsCredentialsProvider": [ + "io.grpc.xds.internal.GoogleDefaultXdsCredentialsProvider", + "io.grpc.xds.internal.InsecureXdsCredentialsProvider", + "io.grpc.xds.internal.TlsXdsCredentialsProvider" + ] + }, + "org.testcontainers:testcontainers": { + "org.testcontainers.dockerclient.DockerClientProviderStrategy": [ + "org.testcontainers.dockerclient.DockerDesktopClientProviderStrategy", + "org.testcontainers.dockerclient.DockerMachineClientProviderStrategy", + "org.testcontainers.dockerclient.EnvironmentAndSystemPropertyClientProviderStrategy", + "org.testcontainers.dockerclient.NpipeSocketClientProviderStrategy", + "org.testcontainers.dockerclient.RootlessDockerClientProviderStrategy", + "org.testcontainers.dockerclient.TestcontainersHostPropertyClientProviderStrategy", + "org.testcontainers.dockerclient.UnixSocketClientProviderStrategy" + ], + "org.testcontainers.shaded.com.fasterxml.jackson.core.JsonFactory": [ + "org.testcontainers.shaded.com.fasterxml.jackson.core.JsonFactory" + ], + "org.testcontainers.shaded.com.fasterxml.jackson.core.ObjectCodec": [ + "org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper" + ] + }, + "org.threeten:threetenbp": { + "org.threeten.bp.zone.ZoneRulesProvider": [ + "org.threeten.bp.zone.TzdbZoneRulesProvider" + ] + } + }, + "version": "3" +}
diff --git a/high-availability-gcp_third_party_runtime_jars.allowlist.txt b/high-availability-gcp_third_party_runtime_jars.allowlist.txt new file mode 100644 index 0000000..eadbd99 --- /dev/null +++ b/high-availability-gcp_third_party_runtime_jars.allowlist.txt
@@ -0,0 +1,45 @@ +animal-sniffer-annotations +annotations +api-common +auto-value-annotations +checker-qual +conscrypt-openjdk-uber +error_prone_annotations +gax +gax-grpc +gax-httpjson +google-auth-library-credentials +google-auth-library-oauth2-http +google-cloud-pubsub +google-http-client +google-http-client-gson +grpc-alts +grpc-api +grpc-auth +grpc-context +grpc-core +grpc-googleapis +grpc-grpclb +grpc-inprocess +grpc-netty-shaded +grpc-protobuf +grpc-protobuf-lite +grpc-services +grpc-stub +grpc-util +grpc-xds +guava-33.4.0-jre +javax.annotation-api +listenablefuture-9999.0-empty-to-avoid-conflict-with-guava +opencensus-api +opencensus-contrib-http-util +opentelemetry-api +opentelemetry-context +opentelemetry-semconv-1.26.0-alpha +perfmark-api +proto-google-cloud-pubsub-v1 +proto-google-common-protos +proto-google-iam-v1 +protobuf-java-util +re2j +threetenbp
diff --git a/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/Configuration.java b/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/Configuration.java new file mode 100644 index 0000000..0a0fe9a --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/Configuration.java
@@ -0,0 +1,80 @@ +// Copyright (C) 2026 The Android Open Source Project +// +// 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 com.googlesource.gerrit.plugins.highavailability.gcp; + +import static java.util.concurrent.TimeUnit.MILLISECONDS; + +import com.google.gerrit.server.config.ConfigUtil; +import java.time.Duration; +import org.eclipse.jgit.lib.Config; + +public record Configuration( + Identity identity, Topics topics, Limits limits, Retention retention, Retry retry) { + + public record Identity(String project, String keyLocation) {} + + public record Topics(String main, String streamEvents) {} + + public record Limits( + Duration subscriptionTimeout, + Duration shutdownTimeout, + int publisherThreads, + int subscriberThreads) {} + + public record Retention(Duration ackDeadline, Duration duration, boolean retainAcked) {} + + public record Retry(int maxDeliveryAttempts, Duration minimumBackoff, Duration maximumBackoff) {} + + private static final String PUBSUB = "pubsub"; + private static final String TOPICS = "topics"; + private static final String LIMITS = "limits"; + private static final String RETENTION = "retention"; + private static final String RETRY = "retry"; + + public Configuration(Config cfg) { + this( + new Identity( + cfg.getString(PUBSUB, null, "projectId"), + cfg.getString(PUBSUB, null, "privateKeyLocation")), + new Topics( + getString(cfg, PUBSUB, TOPICS, "main", "default"), + getString(cfg, PUBSUB, TOPICS, "streamEvents", "stream-events")), + new Limits( + duration(cfg, PUBSUB, LIMITS, "subscriptionTimeout", Duration.ofSeconds(10)), + duration(cfg, PUBSUB, LIMITS, "shutdownTimeout", Duration.ofSeconds(10)), + cfg.getInt(PUBSUB, LIMITS, "publisherThreadPoolSize", 4), + cfg.getInt(PUBSUB, LIMITS, "subscriberThreadPoolSize", 4)), + new Retention( + duration(cfg, PUBSUB, RETENTION, "ackDeadline", Duration.ofSeconds(10)), + duration(cfg, PUBSUB, RETENTION, "messageRetentionDuration", Duration.ofDays(7)), + cfg.getBoolean(PUBSUB, RETENTION, "retainAckedMessages", false)), + new Retry( + cfg.getInt(PUBSUB, RETRY, "maxDeliveryAttempts", 5), + duration(cfg, PUBSUB, RETRY, "minimumBackoff", Duration.ofSeconds(10)), + duration(cfg, PUBSUB, RETRY, "maximumBackoff", Duration.ofMinutes(10)))); + } + + private static Duration duration( + Config c, String section, String subsection, String key, Duration defaultValue) { + return Duration.ofMillis( + ConfigUtil.getTimeUnit(c, section, subsection, key, defaultValue.toMillis(), MILLISECONDS)); + } + + private static String getString( + Config cfg, String section, String subsection, String field, String def) { + String value = cfg.getString(section, subsection, field); + return value == null ? def : value; + } +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/DefaultTopic.java b/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/DefaultTopic.java new file mode 100644 index 0000000..0fa3ea1 --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/DefaultTopic.java
@@ -0,0 +1,24 @@ +// Copyright (C) 2023 The Android Open Source Project +// +// 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 com.googlesource.gerrit.plugins.highavailability.gcp; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import com.google.inject.BindingAnnotation; +import java.lang.annotation.Retention; + +@BindingAnnotation +@Retention(RUNTIME) +public @interface DefaultTopic {}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/GCPPublisherFactory.java b/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/GCPPublisherFactory.java new file mode 100644 index 0000000..ad16215 --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/GCPPublisherFactory.java
@@ -0,0 +1,49 @@ +// Copyright (C) 2024 The Android Open Source Project +// +// 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 com.googlesource.gerrit.plugins.highavailability.gcp; + +import com.google.api.gax.core.CredentialsProvider; +import com.google.api.gax.core.ExecutorProvider; +import com.google.cloud.pubsub.v1.Publisher; +import com.google.gerrit.server.StartupException; +import com.google.inject.Inject; +import com.google.inject.Singleton; +import com.google.pubsub.v1.TopicName; +import java.io.IOException; + +@Singleton +public class GCPPublisherFactory implements PublisherFactory { + private final CredentialsProvider credentials; + private final ExecutorProvider executor; + + @Inject + public GCPPublisherFactory( + CredentialsProvider credentials, @PublisherExecutorProvider ExecutorProvider executor) { + this.credentials = credentials; + this.executor = executor; + } + + @Override + public Publisher create(TopicName topic) { + try { + return Publisher.newBuilder(topic) + .setExecutorProvider(executor) + .setCredentialsProvider(credentials) + .build(); + } catch (IOException e) { + throw new StartupException("Failed to create publisher for PubSub.", e); + } + } +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/GCPSubscriberFactory.java b/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/GCPSubscriberFactory.java new file mode 100644 index 0000000..56f1b4b --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/GCPSubscriberFactory.java
@@ -0,0 +1,48 @@ +// Copyright (C) 2024 The Android Open Source Project +// +// 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 com.googlesource.gerrit.plugins.highavailability.gcp; + +import com.google.api.gax.core.CredentialsProvider; +import com.google.api.gax.core.ExecutorProvider; +import com.google.cloud.pubsub.v1.MessageReceiver; +import com.google.cloud.pubsub.v1.Subscriber; +import com.google.inject.Inject; +import com.google.inject.Singleton; +import com.google.pubsub.v1.ProjectSubscriptionName; + +@Singleton +public class GCPSubscriberFactory implements SubscriberFactory { + private final CredentialsProvider credentials; + private final MessageReceiver messageReceiver; + private final ExecutorProvider executor; + + @Inject + public GCPSubscriberFactory( + CredentialsProvider credentials, + MessageReceiver messageReceiver, + @SubscriberExecutorProvider ExecutorProvider executor) { + this.credentials = credentials; + this.messageReceiver = messageReceiver; + this.executor = executor; + } + + @Override + public Subscriber create(ProjectSubscriptionName subscriptionName) { + return Subscriber.newBuilder(subscriptionName.toString(), messageReceiver) + .setExecutorProvider(executor) + .setCredentialsProvider(credentials) + .build(); + } +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/GcpProjectId.java b/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/GcpProjectId.java new file mode 100644 index 0000000..3a2bb32 --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/GcpProjectId.java
@@ -0,0 +1,24 @@ +// Copyright (C) 2025 The Android Open Source Project +// +// 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 com.googlesource.gerrit.plugins.highavailability.gcp; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import com.google.inject.BindingAnnotation; +import java.lang.annotation.Retention; + +@BindingAnnotation +@Retention(RUNTIME) +public @interface GcpProjectId {}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/GcpPubSubForwarderModule.java b/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/GcpPubSubForwarderModule.java new file mode 100644 index 0000000..36f9dd2 --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/GcpPubSubForwarderModule.java
@@ -0,0 +1,223 @@ +// Copyright (C) 2023 The Android Open Source Project +// +// 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 com.googlesource.gerrit.plugins.highavailability.gcp; + +import com.ericsson.gerrit.plugins.highavailability.forwarder.Forwarder; +import com.google.api.gax.core.CredentialsProvider; +import com.google.api.gax.core.ExecutorProvider; +import com.google.api.gax.core.FixedCredentialsProvider; +import com.google.api.gax.core.InstantiatingExecutorProvider; +import com.google.api.gax.core.NoCredentialsProvider; +import com.google.api.gax.grpc.GrpcTransportChannel; +import com.google.api.gax.rpc.FixedTransportChannelProvider; +import com.google.api.gax.rpc.TransportChannelProvider; +import com.google.auth.oauth2.ServiceAccountCredentials; +import com.google.cloud.pubsub.v1.MessageReceiver; +import com.google.cloud.pubsub.v1.Publisher; +import com.google.cloud.pubsub.v1.Subscriber; +import com.google.cloud.pubsub.v1.SubscriptionAdminClient; +import com.google.cloud.pubsub.v1.SubscriptionAdminSettings; +import com.google.cloud.pubsub.v1.TopicAdminClient; +import com.google.cloud.pubsub.v1.TopicAdminSettings; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Strings; +import com.google.gerrit.lifecycle.LifecycleModule; +import com.google.inject.AbstractModule; +import com.google.inject.Module; +import com.google.inject.Provides; +import com.google.inject.Singleton; +import com.google.inject.util.Modules; +import com.google.pubsub.v1.TopicName; +import io.grpc.ManagedChannel; +import io.grpc.ManagedChannelBuilder; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Collection; +import java.util.List; +import java.util.Optional; + +public class GcpPubSubForwarderModule extends LifecycleModule { + public static final String PUBSUB_EMULATOR_HOST = "PUBSUB_EMULATOR_HOST"; + + public static Module create() { + String hostPort = getEmulatorHost(); + if (Strings.isNullOrEmpty(hostPort)) { + return createForRealPubSub(); + } + return createForEmulator(hostPort); + } + + public static Module createForEmulator(String hostPort) { + return Modules.override(new GcpPubSubForwarderModule()).with(new EmulatorModule(hostPort)); + } + + public static GcpPubSubForwarderModule createForRealPubSub() { + return new GcpPubSubForwarderModule(); + } + + @VisibleForTesting + GcpPubSubForwarderModule() {} + + @Override + protected void configure() { + bind(PubSubNamingFactory.class); + bind(PubSubInitializer.class); + bind(MessageReceiver.class).toProvider(MessageReceiverProvider.class); + bind(PublisherFactory.class).to(GCPPublisherFactory.class); + bind(SubscriberFactory.class).to(GCPSubscriberFactory.class); + bind(PubSubMessageProcessor.class); + bind(Forwarder.class).to(PubSubForwarder.class); + listener().to(OnStartStop.class); + } + + @Provides + @Singleton + @GcpProjectId + public String getGcpProjectId(Configuration config) { + return config.identity().project(); + } + + @Provides + @Singleton + @PublisherExecutorProvider + @VisibleForTesting + public static ExecutorProvider buildPublisherExecutorProvider(Configuration config) { + return InstantiatingExecutorProvider.newBuilder() + .setExecutorThreadCount(config.limits().publisherThreads()) + .build(); + } + + @Provides + @Singleton + @SubscriberExecutorProvider + @VisibleForTesting + public static ExecutorProvider buildSubscriberExecutorProvider(Configuration config) { + return InstantiatingExecutorProvider.newBuilder() + .setExecutorThreadCount(config.limits().subscriberThreads()) + .build(); + } + + @Provides + @Singleton + @DefaultTopic + Publisher createDefaultTopicPublisher( + PublisherFactory factory, @GcpProjectId String projectId, TopicNames topicNames) { + return factory.create(TopicName.of(projectId, topicNames.defaultTopic())); + } + + @Provides + @Singleton + @StreamEventsTopic + Publisher createStreamEventsTopicPublisher( + PublisherFactory factory, @GcpProjectId String projectId, TopicNames topicNames) { + return factory.create(TopicName.of(projectId, topicNames.streamEventsTopic())); + } + + @Provides + @Singleton + Collection<Publisher> allPublishers( + @DefaultTopic Publisher defaultPublisher, + @StreamEventsTopic Publisher streamEventsPublisher) { + return List.of(defaultPublisher, streamEventsPublisher); + } + + @Provides + @Singleton + Collection<Subscriber> allSubscribers( + TopicNames topicNames, + PubSubNamingFactory namingFactory, + SubscriberFactory subscriberFactory) { + return topicNames.all().stream() + .map(name -> namingFactory.createFor(name)) + .map(names -> subscriberFactory.create(names.subscription())) + .toList(); + } + + private static String getEmulatorHost() { + return Optional.ofNullable(System.getenv(PUBSUB_EMULATOR_HOST)) + .orElse(System.getProperty(PUBSUB_EMULATOR_HOST)); + } + + @Provides + @Singleton + CredentialsProvider getCredentialsProvider(Configuration config) + throws FileNotFoundException, IOException { + return FixedCredentialsProvider.create( + ServiceAccountCredentials.fromStream(new FileInputStream(config.identity().keyLocation()))); + } + + @Provides + @Singleton + SubscriptionAdminClient createSubscriptionAdminClient(CredentialsProvider credentials) + throws IOException { + return SubscriptionAdminClient.create( + SubscriptionAdminSettings.newBuilder().setCredentialsProvider(credentials).build()); + } + + @Provides + @Singleton + TopicAdminClient createTopicAdminClient(CredentialsProvider credentials) throws IOException { + TopicAdminSettings settings = + TopicAdminSettings.newBuilder().setCredentialsProvider(credentials).build(); + return TopicAdminClient.create(settings); + } + + static class EmulatorModule extends AbstractModule { + private final String hostPort; + + EmulatorModule(String hostPort) { + this.hostPort = hostPort; + } + + @Override + protected void configure() { + bind(CredentialsProvider.class).toInstance(NoCredentialsProvider.create()); + bind(PublisherFactory.class).to(LocalPublisherFactory.class); + bind(SubscriberFactory.class).to(LocalSubscriberFactory.class); + } + + @Provides + @Singleton + TransportChannelProvider createTransportChannelProvider() { + ManagedChannel channel = ManagedChannelBuilder.forTarget(hostPort).usePlaintext().build(); + return FixedTransportChannelProvider.create(GrpcTransportChannel.create(channel)); + } + + @Provides + @Singleton + SubscriptionAdminClient createSubscriptionAdminClient(TransportChannelProvider channelProvider) + throws IOException { + SubscriptionAdminSettings settings = + SubscriptionAdminSettings.newBuilder() + .setTransportChannelProvider(channelProvider) + .setCredentialsProvider(NoCredentialsProvider.create()) + .build(); + return SubscriptionAdminClient.create(settings); + } + + @Provides + @Singleton + TopicAdminClient createTopicAdminClient(TransportChannelProvider channelProvider) + throws IOException { + TopicAdminSettings settings = + TopicAdminSettings.newBuilder() + .setTransportChannelProvider(channelProvider) + .setCredentialsProvider(NoCredentialsProvider.create()) + .build(); + return TopicAdminClient.create(settings); + } + } +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/LocalPublisherFactory.java b/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/LocalPublisherFactory.java new file mode 100644 index 0000000..e6b6ece --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/LocalPublisherFactory.java
@@ -0,0 +1,55 @@ +// Copyright (C) 2024 The Android Open Source Project +// +// 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 com.googlesource.gerrit.plugins.highavailability.gcp; + +import com.google.api.gax.core.CredentialsProvider; +import com.google.api.gax.core.ExecutorProvider; +import com.google.api.gax.rpc.TransportChannelProvider; +import com.google.cloud.pubsub.v1.Publisher; +import com.google.gerrit.server.StartupException; +import com.google.inject.Inject; +import com.google.inject.Singleton; +import com.google.pubsub.v1.TopicName; +import java.io.IOException; + +@Singleton +public class LocalPublisherFactory implements PublisherFactory { + private final CredentialsProvider credentials; + private final TransportChannelProvider transportChannelProvider; + private final ExecutorProvider executor; + + @Inject + public LocalPublisherFactory( + CredentialsProvider credentials, + TransportChannelProvider transportChannelProvider, + @PublisherExecutorProvider ExecutorProvider executor) { + this.credentials = credentials; + this.transportChannelProvider = transportChannelProvider; + this.executor = executor; + } + + @Override + public Publisher create(TopicName topic) { + try { + return Publisher.newBuilder(topic) + .setExecutorProvider(executor) + .setChannelProvider(transportChannelProvider) + .setCredentialsProvider(credentials) + .build(); + } catch (IOException e) { + throw new StartupException("Failed to create publisher for PubSub.", e); + } + } +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/LocalSubscriberFactory.java b/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/LocalSubscriberFactory.java new file mode 100644 index 0000000..7e88acf --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/LocalSubscriberFactory.java
@@ -0,0 +1,53 @@ +// Copyright (C) 2024 The Android Open Source Project +// +// 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 com.googlesource.gerrit.plugins.highavailability.gcp; + +import com.google.api.gax.core.CredentialsProvider; +import com.google.api.gax.core.ExecutorProvider; +import com.google.api.gax.rpc.TransportChannelProvider; +import com.google.cloud.pubsub.v1.MessageReceiver; +import com.google.cloud.pubsub.v1.Subscriber; +import com.google.inject.Inject; +import com.google.inject.Singleton; +import com.google.pubsub.v1.ProjectSubscriptionName; + +@Singleton +public class LocalSubscriberFactory implements SubscriberFactory { + private final CredentialsProvider credentials; + private TransportChannelProvider transportChannelProvider; + private final MessageReceiver messageReceiver; + private final ExecutorProvider executor; + + @Inject + public LocalSubscriberFactory( + CredentialsProvider credentials, + TransportChannelProvider transportChannelProvider, + MessageReceiver messageReceiver, + @SubscriberExecutorProvider ExecutorProvider executor) { + this.credentials = credentials; + this.transportChannelProvider = transportChannelProvider; + this.messageReceiver = messageReceiver; + this.executor = executor; + } + + @Override + public Subscriber create(ProjectSubscriptionName subscriptionName) { + return Subscriber.newBuilder(subscriptionName.toString(), messageReceiver) + .setExecutorProvider(executor) + .setChannelProvider(transportChannelProvider) + .setCredentialsProvider(credentials) + .build(); + } +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/MessageReceiverProvider.java b/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/MessageReceiverProvider.java new file mode 100644 index 0000000..22ed9f8 --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/MessageReceiverProvider.java
@@ -0,0 +1,65 @@ +// Copyright (C) 2024 The Android Open Source Project +// +// 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 com.googlesource.gerrit.plugins.highavailability.gcp; + +import com.google.cloud.pubsub.v1.AckReplyConsumer; +import com.google.cloud.pubsub.v1.MessageReceiver; +import com.google.common.flogger.FluentLogger; +import com.google.gerrit.server.config.GerritInstanceId; +import com.google.inject.Inject; +import com.google.inject.Provider; +import com.google.inject.Singleton; +import com.google.pubsub.v1.PubsubMessage; + +@Singleton +public class MessageReceiverProvider implements Provider<MessageReceiver> { + private static final FluentLogger logger = FluentLogger.forEnclosingClass(); + + private final PubSubMessageProcessor processor; + private final String instanceId; + + @Inject + public MessageReceiverProvider( + PubSubMessageProcessor processor, @GerritInstanceId String instanceId) { + this.processor = processor; + this.instanceId = instanceId; + } + + @Override + public MessageReceiver get() { + return (PubsubMessage message, AckReplyConsumer consumer) -> { + try { + logger.atFine().log("Received message: %s", message); + if (instanceId.equals(message.getAttributesMap().get("instanceId"))) { + logger.atWarning().log( + "Skipping message %s since it was sent by this instance %s", + message.getMessageId(), instanceId); + consumer.ack(); + return; + } + if (processor.handle(message)) { + consumer.ack(); + } else { + consumer.nack(); + } + } catch (Exception e) { + consumer.nack(); + logger.atSevere().withCause(e).log( + "Exception when consuming message %s [message: %s]", + message.getMessageId(), message.getData().toStringUtf8()); + } + }; + } +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/Module.java b/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/Module.java new file mode 100644 index 0000000..c61d073 --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/Module.java
@@ -0,0 +1,35 @@ +// Copyright (C) 2026 The Android Open Source Project +// +// 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 com.googlesource.gerrit.plugins.highavailability.gcp; + +import com.ericsson.gerrit.plugins.highavailability.forwarder.Forwarder; +import com.google.gerrit.extensions.annotations.PluginName; +import com.google.gerrit.extensions.registration.DynamicItem; +import com.google.gerrit.server.config.PluginConfigFactory; +import com.google.inject.AbstractModule; +import com.google.inject.Provides; + +public class Module extends AbstractModule { + @Override + protected void configure() { + DynamicItem.bind(binder(), Forwarder.class).to(PubSubForwarder.class); + install(GcpPubSubForwarderModule.create()); + } + + @Provides + Configuration createPubSub(PluginConfigFactory cfg, @PluginName String pluginName) { + return new Configuration(cfg.getGlobalPluginConfig(pluginName)); + } +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/OnStartStop.java b/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/OnStartStop.java new file mode 100644 index 0000000..663f66b --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/OnStartStop.java
@@ -0,0 +1,97 @@ +// Copyright (C) 2024 The Android Open Source Project +// +// 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 com.googlesource.gerrit.plugins.highavailability.gcp; + +import com.google.cloud.pubsub.v1.Publisher; +import com.google.cloud.pubsub.v1.Subscriber; +import com.google.common.flogger.FluentLogger; +import com.google.gerrit.extensions.events.LifecycleListener; +import com.google.inject.Inject; +import com.google.inject.Provider; +import com.google.inject.Singleton; +import java.util.Collection; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +@Singleton +public class OnStartStop implements LifecycleListener { + private static final FluentLogger logger = FluentLogger.forEnclosingClass(); + + private final PubSubInitializer initializer; + private final Configuration config; + + private final Provider<Collection<Publisher>> publishers; + private final Provider<Collection<Subscriber>> subscribers; + + @Inject + public OnStartStop( + PubSubInitializer initializer, + Configuration config, + Provider<Collection<Publisher>> publishers, + Provider<Collection<Subscriber>> subscribers) { + this.initializer = initializer; + this.config = config; + this.publishers = publishers; + this.subscribers = subscribers; + } + + @Override + public void start() { + initializer.initialize(); + startSubscribers(); + } + + private void startSubscribers() { + for (Subscriber subscriber : subscribers.get()) { + try { + subscriber + .startAsync() + .awaitRunning(config.limits().subscriptionTimeout().getSeconds(), TimeUnit.SECONDS); + } catch (TimeoutException e) { + throw new IllegalStateException("Timeout while subscribing to PubSub topic", e); + } + } + } + + @Override + public void stop() { + shutdownPublishers(); + stopSubscribers(); + } + + private void shutdownPublishers() { + for (Publisher publisher : publishers.get()) { + try { + publisher.shutdown(); + publisher.awaitTermination( + config.limits().shutdownTimeout().getSeconds(), TimeUnit.SECONDS); + } catch (InterruptedException e) { + logger.atSevere().withCause(e).log("Could not close the MessageDispatcher"); + } + } + } + + private void stopSubscribers() { + for (Subscriber subscriber : subscribers.get()) { + try { + subscriber + .stopAsync() + .awaitTerminated(config.limits().shutdownTimeout().getSeconds(), TimeUnit.SECONDS); + } catch (TimeoutException e) { + logger.atSevere().withCause(e).log("Timeout during stopping of subscription."); + } + } + } +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/PubSubForwarder.java b/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/PubSubForwarder.java new file mode 100644 index 0000000..87947b9 --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/PubSubForwarder.java
@@ -0,0 +1,161 @@ +// Copyright (C) 2024 The Android Open Source Project +// +// 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 com.googlesource.gerrit.plugins.highavailability.gcp; + +import com.ericsson.gerrit.plugins.highavailability.forwarder.Forwarder; +import com.ericsson.gerrit.plugins.highavailability.forwarder.IndexEvent; +import com.ericsson.gerrit.plugins.highavailability.forwarder.commands.AddToProjectList; +import com.ericsson.gerrit.plugins.highavailability.forwarder.commands.Command; +import com.ericsson.gerrit.plugins.highavailability.forwarder.commands.CommandsGson; +import com.ericsson.gerrit.plugins.highavailability.forwarder.commands.DeleteAllProjectChangesFromIndex; +import com.ericsson.gerrit.plugins.highavailability.forwarder.commands.EvictCache; +import com.ericsson.gerrit.plugins.highavailability.forwarder.commands.IndexAccount; +import com.ericsson.gerrit.plugins.highavailability.forwarder.commands.IndexChange; +import com.ericsson.gerrit.plugins.highavailability.forwarder.commands.IndexGroup; +import com.ericsson.gerrit.plugins.highavailability.forwarder.commands.IndexProject; +import com.ericsson.gerrit.plugins.highavailability.forwarder.commands.PostEvent; +import com.ericsson.gerrit.plugins.highavailability.forwarder.commands.RemoveFromProjectList; +import com.google.api.core.ApiFutureCallback; +import com.google.api.core.ApiFutures; +import com.google.cloud.pubsub.v1.Publisher; +import com.google.common.flogger.FluentLogger; +import com.google.common.util.concurrent.MoreExecutors; +import com.google.gerrit.entities.Project; +import com.google.gerrit.server.config.GerritInstanceId; +import com.google.gerrit.server.events.Event; +import com.google.gson.Gson; +import com.google.inject.Inject; +import com.google.inject.Singleton; +import com.google.protobuf.ByteString; +import com.google.pubsub.v1.PubsubMessage; +import java.time.Instant; +import java.util.concurrent.CompletableFuture; + +@Singleton +public class PubSubForwarder implements Forwarder { + private static final FluentLogger log = FluentLogger.forEnclosingClass(); + + private final Publisher defaultPublisher; + private final Publisher streamEventsPublisher; + private final Gson gson; + private final String instanceId; + + @Inject + PubSubForwarder( + @DefaultTopic Publisher defaultPublisher, + @StreamEventsTopic Publisher streamEventsPublisher, + @CommandsGson Gson gson, + @GerritInstanceId String instanceId) { + this.defaultPublisher = defaultPublisher; + this.streamEventsPublisher = streamEventsPublisher; + this.gson = gson; + this.instanceId = instanceId; + } + + @Override + public CompletableFuture<Result> indexAccount(int accountId, IndexEvent indexEvent) { + return execute(new IndexAccount(accountId, indexEvent.eventCreatedOn), defaultPublisher); + } + + @Override + public CompletableFuture<Result> indexChange( + String projectName, int changeId, IndexEvent indexEvent) { + return execute( + new IndexChange.Update(projectName, changeId, indexEvent), defaultPublisher); + } + + @Override + public CompletableFuture<Result> batchIndexChange( + String projectName, int changeId, IndexEvent indexEvent) { + return execute( + new IndexChange.BatchUpdate(projectName, changeId, indexEvent), + defaultPublisher); + } + + @Override + public CompletableFuture<Result> deleteChangeFromIndex( + String projectName, int changeId, IndexEvent indexEvent) { + return execute( + new IndexChange.Delete(projectName, changeId, indexEvent), defaultPublisher); + } + + @Override + public CompletableFuture<Result> indexGroup(String uuid, IndexEvent indexEvent) { + return execute(new IndexGroup(uuid, indexEvent.eventCreatedOn), defaultPublisher); + } + + @Override + public CompletableFuture<Result> indexProject(String projectName, IndexEvent indexEvent) { + return execute(new IndexProject(projectName, indexEvent.eventCreatedOn), defaultPublisher); + } + + @Override + public CompletableFuture<Result> send(Event event) { + return execute( + new PostEvent(event, Instant.ofEpochSecond(event.eventCreatedOn)), streamEventsPublisher); + } + + @Override + public CompletableFuture<Result> evict(String cacheName, Object key) { + return execute(new EvictCache(cacheName, gson.toJson(key), Instant.now()), defaultPublisher); + } + + @Override + public CompletableFuture<Result> addToProjectList(String projectName) { + return execute(new AddToProjectList(projectName, Instant.now()), defaultPublisher); + } + + @Override + public CompletableFuture<Result> removeFromProjectList(String projectName) { + return execute(new RemoveFromProjectList(projectName, Instant.now()), defaultPublisher); + } + + @Override + public CompletableFuture<Result> deleteAllChangesForProject(Project.NameKey projectName) { + return execute( + new DeleteAllProjectChangesFromIndex(projectName, Instant.now()), defaultPublisher); + } + + private PubsubMessage buildMessage(Command cmd) { + return PubsubMessage.newBuilder() + .putAttributes("instanceId", instanceId) + .setData(ByteString.copyFromUtf8(gson.toJson(cmd))) + .build(); + } + + private CompletableFuture<Result> execute(Command cmd, Publisher publisher) { + CompletableFuture<Result> future = new CompletableFuture<>(); + String msg = gson.toJson(cmd); + log.atFine().log("Publishing message: %s", msg); + ApiFutures.addCallback( + publisher.publish(buildMessage(cmd)), + new ApiFutureCallback<String>() { + + @Override + public void onFailure(Throwable t) { + log.atSevere().withCause(t).log("Failed to publish message: %s", msg); + future.complete(new Result(cmd.type, false)); + } + + @Override + public void onSuccess(String result) { + log.atFine().log("Published message (id: %s): %s", result, msg); + future.complete(new Result(cmd.type, true)); + } + }, + MoreExecutors.directExecutor()); + return future; + } +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/PubSubInitializer.java b/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/PubSubInitializer.java new file mode 100644 index 0000000..990829f --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/PubSubInitializer.java
@@ -0,0 +1,165 @@ +// Copyright (C) 2025 The Android Open Source Project +// +// 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 com.googlesource.gerrit.plugins.highavailability.gcp; + +import com.google.api.gax.rpc.AlreadyExistsException; +import com.google.api.gax.rpc.NotFoundException; +import com.google.cloud.pubsub.v1.SubscriptionAdminClient; +import com.google.cloud.pubsub.v1.TopicAdminClient; +import com.google.common.flogger.FluentLogger; +import com.google.gerrit.server.config.GerritInstanceId; +import com.google.inject.Inject; +import com.google.inject.Singleton; +import com.google.protobuf.FieldMask; +import com.google.pubsub.v1.DeadLetterPolicy; +import com.google.pubsub.v1.RetryPolicy; +import com.google.pubsub.v1.Subscription; +import com.google.pubsub.v1.TopicName; +import com.google.pubsub.v1.UpdateSubscriptionRequest; + +/** Initialize topic(s) and subscription(s) for Pub/Sub if they do not already exist. */ +@Singleton +public class PubSubInitializer { + private static final FluentLogger logger = FluentLogger.forEnclosingClass(); + + private final TopicAdminClient topicAdminClient; + private final SubscriptionAdminClient subscriptionAdminClient; + private final Configuration config; + private final String instanceId; + private final TopicNames topicNames; + private final PubSubNamingFactory namingFactory; + + @Inject + PubSubInitializer( + TopicAdminClient topicAdminClient, + SubscriptionAdminClient subscriptionAdminClient, + Configuration config, + @GerritInstanceId String instanceId, + TopicNames topicNames, + PubSubNamingFactory namingFactory) { + this.topicAdminClient = topicAdminClient; + this.subscriptionAdminClient = subscriptionAdminClient; + this.config = config; + this.instanceId = instanceId; + this.topicNames = topicNames; + this.namingFactory = namingFactory; + } + + public void initialize() { + for (String name : topicNames.all()) { + PubSubNames names = namingFactory.createFor(name); + initializeTopic(names.topic()); + initializeSubscription(names); + } + } + + private void initializeTopic(TopicName topic) { + try { + topicAdminClient.createTopic(topic); + logger.atInfo().log("Created topic %s", topic.getTopic()); + } catch (AlreadyExistsException e) { + logger.atInfo().log("Topic %s already exists", topic.getTopic()); + } + } + + private void initializeSubscription(PubSubNames names) { + // Initialize the DLT topic and its default subscription + initializeTopic(names.deadLetterTopic()); + initializeDltSubscription(names); + + // Initialize the main subscription + Subscription subscription; + try { + subscription = subscriptionAdminClient.getSubscription(names.subscription()); + logger.atInfo().log("Subscription for topic %s already exists", names.topic()); + } catch (NotFoundException e) { + logger.atInfo().log("Creating subscription for topic %s", names.topic()); + String filter = String.format("attributes.instanceId!=\"%s\"", instanceId); + subscription = + Subscription.newBuilder() + .setName(names.subscription().toString()) + .setTopic(names.topic().toString()) + .setFilter(filter) + .build(); + subscriptionAdminClient.createSubscription(subscription); + } + + updateSubscriptionSettings(subscription, names.deadLetterTopic()); + } + + private void initializeDltSubscription(PubSubNames names) { + try { + subscriptionAdminClient.getSubscription(names.deadLetterSubscription()); + logger.atInfo().log("DLT Subscription for topic %s already exists", names.deadLetterTopic()); + } catch (NotFoundException e) { + logger.atInfo().log("Creating DLT subscription for topic %s", names.deadLetterTopic()); + Subscription subscription = + Subscription.newBuilder() + .setName(names.deadLetterSubscription().toString()) + .setTopic(names.deadLetterTopic().toString()) + .build(); + subscriptionAdminClient.createSubscription(subscription); + } + } + + private void updateSubscriptionSettings(Subscription subscription, TopicName dltTopicName) { + DeadLetterPolicy deadLetterPolicy = + DeadLetterPolicy.newBuilder() + .setDeadLetterTopic(dltTopicName.toString()) + .setMaxDeliveryAttempts(config.retry().maxDeliveryAttempts()) + .build(); + + RetryPolicy retryPolicy = + RetryPolicy.newBuilder() + .setMinimumBackoff( + com.google.protobuf.Duration.newBuilder() + .setSeconds(config.retry().minimumBackoff().getSeconds()) + .build()) + .setMaximumBackoff( + com.google.protobuf.Duration.newBuilder() + .setSeconds(config.retry().maximumBackoff().getSeconds()) + .build()) + .build(); + + Subscription desired = + subscription.toBuilder() + .setAckDeadlineSeconds((int) config.retention().ackDeadline().getSeconds()) + .setMessageRetentionDuration( + com.google.protobuf.Duration.newBuilder() + .setSeconds(config.retention().duration().getSeconds()) + .build()) + .setRetainAckedMessages(config.retention().retainAcked()) + .setDeadLetterPolicy(deadLetterPolicy) + .setRetryPolicy(retryPolicy) + .build(); + + FieldMask fieldMask = + FieldMask.newBuilder() + .addPaths("ack_deadline_seconds") + .addPaths("message_retention_duration") + .addPaths("retain_acked_messages") + .addPaths("dead_letter_policy") + .addPaths("retry_policy") + .build(); + + UpdateSubscriptionRequest updateRequest = + UpdateSubscriptionRequest.newBuilder() + .setSubscription(desired) + .setUpdateMask(fieldMask) + .build(); + + subscriptionAdminClient.updateSubscription(updateRequest); + } +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/PubSubMessageProcessor.java b/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/PubSubMessageProcessor.java new file mode 100644 index 0000000..c3fcada --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/PubSubMessageProcessor.java
@@ -0,0 +1,54 @@ +// Copyright (C) 2024 The Android Open Source Project +// +// 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 com.googlesource.gerrit.plugins.highavailability.gcp; + +import com.ericsson.gerrit.plugins.highavailability.forwarder.commands.Command; +import com.ericsson.gerrit.plugins.highavailability.forwarder.commands.CommandProcessor; +import com.ericsson.gerrit.plugins.highavailability.forwarder.commands.CommandsGson; +import com.google.common.flogger.FluentLogger; +import com.google.gerrit.extensions.registration.DynamicItem; +import com.google.gson.Gson; +import com.google.inject.Inject; +import com.google.inject.Singleton; +import com.google.pubsub.v1.PubsubMessage; + +@Singleton +public class PubSubMessageProcessor { + private static final FluentLogger log = FluentLogger.forEnclosingClass(); + + private final Gson gson; + private final DynamicItem<CommandProcessor> processor; + + @Inject + PubSubMessageProcessor(@CommandsGson Gson gson, DynamicItem<CommandProcessor> processor) { + this.gson = gson; + this.processor = processor; + } + + public boolean handle(PubsubMessage msg) { + log.atFine().log("Processing message: %s", msg); + return processor.get().handle(getCommand(msg)); + } + + private Command getCommand(PubsubMessage msg) { + try { + String s = msg.getData().toStringUtf8(); + return gson.fromJson(s, Command.class); + } catch (RuntimeException e) { + log.atSevere().withCause(e).log("Error parsing message %s", msg); + throw e; + } + } +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/PubSubNames.java b/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/PubSubNames.java new file mode 100644 index 0000000..510ca31 --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/PubSubNames.java
@@ -0,0 +1,24 @@ +// Copyright (C) 2026 The Android Open Source Project +// +// 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 com.googlesource.gerrit.plugins.highavailability.gcp; + +import com.google.pubsub.v1.ProjectSubscriptionName; +import com.google.pubsub.v1.TopicName; + +public record PubSubNames( + TopicName topic, + ProjectSubscriptionName subscription, + TopicName deadLetterTopic, + ProjectSubscriptionName deadLetterSubscription) {}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/PubSubNamingFactory.java b/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/PubSubNamingFactory.java new file mode 100644 index 0000000..36dd418 --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/PubSubNamingFactory.java
@@ -0,0 +1,49 @@ +// Copyright (C) 2026 The Android Open Source Project +// +// 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 com.googlesource.gerrit.plugins.highavailability.gcp; + +import com.google.gerrit.server.config.GerritInstanceId; +import com.google.inject.Inject; +import com.google.inject.Singleton; +import com.google.pubsub.v1.ProjectSubscriptionName; +import com.google.pubsub.v1.TopicName; + +@Singleton +public class PubSubNamingFactory { + + private final String projectId; + private final String instanceId; + + @Inject + public PubSubNamingFactory(@GcpProjectId String projectId, @GerritInstanceId String instanceId) { + this.projectId = projectId; + this.instanceId = instanceId; + } + + public PubSubNames createFor(String topicId) { + TopicName topic = TopicName.of(projectId, topicId); + + String subId = String.format("%s.%s", topicId, instanceId); + ProjectSubscriptionName sub = ProjectSubscriptionName.of(projectId, subId); + + String dltTopicId = String.format("%s.dlt", subId); + TopicName dltTopic = TopicName.of(projectId, dltTopicId); + + String dltSubId = String.format("%s.monitoring", dltTopicId); + ProjectSubscriptionName dltSub = ProjectSubscriptionName.of(projectId, dltSubId); + + return new PubSubNames(topic, sub, dltTopic, dltSub); + } +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/PublisherExecutorProvider.java b/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/PublisherExecutorProvider.java new file mode 100644 index 0000000..d5e76e6 --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/PublisherExecutorProvider.java
@@ -0,0 +1,24 @@ +// Copyright (C) 2024 The Android Open Source Project +// +// 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 com.googlesource.gerrit.plugins.highavailability.gcp; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import com.google.inject.BindingAnnotation; +import java.lang.annotation.Retention; + +@BindingAnnotation +@Retention(RUNTIME) +public @interface PublisherExecutorProvider {}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/PublisherFactory.java b/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/PublisherFactory.java new file mode 100644 index 0000000..2a5e381 --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/PublisherFactory.java
@@ -0,0 +1,22 @@ +// Copyright (C) 2025 The Android Open Source Project +// +// 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 com.googlesource.gerrit.plugins.highavailability.gcp; + +import com.google.cloud.pubsub.v1.Publisher; +import com.google.pubsub.v1.TopicName; + +public interface PublisherFactory { + Publisher create(TopicName topic); +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/StreamEventsTopic.java b/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/StreamEventsTopic.java new file mode 100644 index 0000000..7304b45 --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/StreamEventsTopic.java
@@ -0,0 +1,24 @@ +// Copyright (C) 2023 The Android Open Source Project +// +// 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 com.googlesource.gerrit.plugins.highavailability.gcp; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import com.google.inject.BindingAnnotation; +import java.lang.annotation.Retention; + +@BindingAnnotation +@Retention(RUNTIME) +public @interface StreamEventsTopic {}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/SubscriberExecutorProvider.java b/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/SubscriberExecutorProvider.java new file mode 100644 index 0000000..a047912 --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/SubscriberExecutorProvider.java
@@ -0,0 +1,24 @@ +// Copyright (C) 2024 The Android Open Source Project +// +// 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 com.googlesource.gerrit.plugins.highavailability.gcp; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import com.google.inject.BindingAnnotation; +import java.lang.annotation.Retention; + +@BindingAnnotation +@Retention(RUNTIME) +public @interface SubscriberExecutorProvider {}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/SubscriberFactory.java b/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/SubscriberFactory.java new file mode 100644 index 0000000..2e85f9e --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/SubscriberFactory.java
@@ -0,0 +1,22 @@ +// Copyright (C) 2025 The Android Open Source Project +// +// 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 com.googlesource.gerrit.plugins.highavailability.gcp; + +import com.google.cloud.pubsub.v1.Subscriber; +import com.google.pubsub.v1.ProjectSubscriptionName; + +public interface SubscriberFactory { + Subscriber create(ProjectSubscriptionName subscription); +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/SubscriptionNames.java b/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/SubscriptionNames.java new file mode 100644 index 0000000..6d8f12b --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/SubscriptionNames.java
@@ -0,0 +1,56 @@ +// Copyright (C) 2025 The Android Open Source Project +// +// 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 com.googlesource.gerrit.plugins.highavailability.gcp; + +import com.google.gerrit.server.config.GerritInstanceId; +import com.google.inject.Inject; +import com.google.inject.Singleton; +import com.google.pubsub.v1.TopicName; +import java.util.List; + +@Singleton +public class SubscriptionNames { + + private final List<String> names; + private final String projectId; + private final String instanceId; + + public static String getSubscriptionName(String instanceId, String topic) { + return instanceId + "-" + topic; + } + + @Inject + SubscriptionNames( + @GerritInstanceId String instanceId, @GcpProjectId String projectId, TopicNames topicNames) { + this.instanceId = instanceId; + this.projectId = projectId; + this.names = initializeNames(topicNames); + } + + public String nameFor(TopicName topic) { + return instanceId + "-" + topic.getTopic(); + } + + public List<String> all() { + return names; + } + + private List<String> initializeNames(TopicNames topicNames) { + return topicNames.all().stream() + .map(name -> TopicName.of(projectId, name)) + .map(topic -> instanceId + "-" + topic.getTopic()) + .toList(); + } +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/TopicNames.java b/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/TopicNames.java new file mode 100644 index 0000000..b7a4578 --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/highavailability/gcp/TopicNames.java
@@ -0,0 +1,41 @@ +// Copyright (C) 2025 The Android Open Source Project +// +// 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 com.googlesource.gerrit.plugins.highavailability.gcp; + +import com.google.inject.Inject; +import com.google.inject.Singleton; +import java.util.List; + +@Singleton +public class TopicNames { + private final Configuration.Topics topics; + + @Inject + public TopicNames(Configuration config) { + this.topics = config.topics(); + } + + public String defaultTopic() { + return topics.main(); + } + + public String streamEventsTopic() { + return topics.streamEvents(); + } + + public List<String> all() { + return List.of(topics.main(), topics.streamEvents()); + } +}
diff --git a/src/main/resources/Documentation/about.md b/src/main/resources/Documentation/about.md new file mode 100644 index 0000000..4488f5e --- /dev/null +++ b/src/main/resources/Documentation/about.md
@@ -0,0 +1,45 @@ +# High Availability for Google Cloud Pub/Sub + +This plugin extends the Gerrit High Availability plugin by using Google Cloud Pub/Sub +as the message broker for forwarding events between Gerrit instances instead of HTTP-based +direct communication. + +## Google Cloud Pub/Sub as Message Broker + +Google Cloud Pub/Sub provides: + +* **Decoupling**: Instances do not need to know about each other directly or maintain HTTP connections +* **Scalability**: Automatically scales to handle high message volume +* **Reliability**: Messages are persisted and retried automatically +* **Ordering**: Preserves message ordering per topic +* **Dead-letter queues**: Messages that exceed max receive count are moved to a dead-letter topic for debugging + +## Prerequisites + +* Google Cloud Project with Pub/Sub API enabled +* Google Cloud service account with the following roles: + - `roles/pubsub.editor` - To create and manage topics and subscriptions + - `roles/pubsub.publisher` - To publish messages + - `roles/pubsub.subscriber` - To consume messages +* Service account credentials (JSON key file or Application Default Credentials) + +## How It Works + +Instead of the base plugin's HTTP-based peer communication model, this GCP extension: + +1. Publishes all HA events (cache evictions, index updates, stream events) to a Google Cloud Pub/Sub topic +2. Each Gerrit instance subscribes to the topic via its own subscription +3. Messages are processed asynchronously by each instance's subscription handler +4. Failed messages are automatically retried per Pub/Sub's retry policy +5. Messages exceeding the max receive count are moved to a dead-letter topic + +## Monitoring and Debugging + +Failed messages can be investigated via the dead-letter topic. Use Google Cloud Console or +`gcloud pubsub` CLI commands to: + +* View subscription lag and processing rates +* Inspect dead-lettered messages +* Monitor message throughput and latency + +For troubleshooting steps, refer to [config.md](config.md) and Google Cloud Pub/Sub documentation.
diff --git a/src/main/resources/Documentation/build.md b/src/main/resources/Documentation/build.md new file mode 100644 index 0000000..424ea8d --- /dev/null +++ b/src/main/resources/Documentation/build.md
@@ -0,0 +1,42 @@ +Build +===== + +This plugin can be built with Bazel in the Gerrit tree. + +Clone or symlink this plugin repository to the `plugins/` directory +of Gerrit's source tree. Edit the external dependency Bazel build file +`gerrit/plugins/external_plugin_deps.MODULE.bazel` and add a line +to import the plugin's external dependencies. + +If you also want to build the high-availability plugin (which +the @PLUGIN@ depends on) from source code also clone or symlink +its repository to the `plugins/` directory and add another line +to the `external_plugin_deps.MODULE.bazel`: + +``` +include("//plugins/high-availability:external_plugin_deps.MODULE.bazel") +include("//plugins/high-availability-aws:external_plugin_deps.MODULE.bazel") +``` + +From Gerrit source tree issue the command: + +``` + bazel build plugins/@PLUGIN@ +``` + +The output is created in + +``` + bazel-bin/plugins/@PLUGIN@/@PLUGIN@.jar +``` + +To execute the tests run either one of: + +``` + bazel test --test_tag_filters=@PLUGIN@ //... + bazel test plugins/@PLUGIN@:@PLUGIN@_tests +``` + +[Back to @PLUGIN@ documentation index][index] + +[index]: index.html
diff --git a/src/main/resources/Documentation/config.md b/src/main/resources/Documentation/config.md new file mode 100644 index 0000000..fa165c9 --- /dev/null +++ b/src/main/resources/Documentation/config.md
@@ -0,0 +1,83 @@ + @PLUGIN@ Configuration +========================= + +Overview of all available configuration options: +``` +[pubsub] + projectId = my-gcp-project + privateKeyLocation = /path/to/private/key/my-gcp-project-6f0d80a3f655.json +[pubsub "topics"] + main = default + streamEvents = stream-events +[pubsub "limits"] + subscriptionTimeout = 10s + shutdownTimeout = 10s + publisherThreadPoolSize = 4 + subscriberThreadPoolSize = 4 +[pubsub "retention"] + ackDeadline = 10s + messageRetentionDuration = 7d + retainAckedMessages = false +[pubsub "retry"] + maxDeliveryAttempts = 5 + minimumBackoff = 10s + maximumBackoff = 10m +``` + +```pubsub.projectId``` +: The name of the GCP project containing the PubSub topic to be used. + +```pubsub.privateKeyLocation``` +: The location of the file containing the service account key of the service + account that Gerrit can use to create subscriptions for the topics configured in + pubsub.topics and to subscribe to it. + +```pubsub.topics.main``` +: The name of the main PubSub topic used for publishing events. + Default: `default` + +```pubsub.topics.streamEvents``` +: The name of the PubSub topic used for publishing stream events. + Default: `stream-events` + +```pubsub.limits.subscriptionTimeout``` +: The timeout duration for subscription operations. + Default: `10s` + +```pubsub.limits.shutdownTimeout``` +: The timeout duration for graceful shutdown of the plugin. + Default: `10s` + +```pubsub.limits.publisherThreadPoolSize``` +: The number of threads used by the publisher thread pool for publishing messages. + Default: `4` + +```pubsub.limits.subscriberThreadPoolSize``` +: The number of threads used by the subscriber thread pool for consuming messages. + Default: `4` + +```pubsub.retention.ackDeadline``` +: The amount of time the Pub/Sub service will wait for an acknowledgement before + redelivering the message. + Default: `10s` + +```pubsub.retention.messageRetentionDuration``` +: The length of time that messages in the topic should be retained. + Default: `7d` + +```pubsub.retention.retainAckedMessages``` +: Whether to retain messages that have already been acknowledged. + Default: `false` + +```pubsub.retry.maxDeliveryAttempts``` +: The maximum number of delivery attempts for a message before it is sent to + the dead letter topic. + Default: `5` + +```pubsub.retry.minimumBackoff``` +: The minimum backoff duration for retrying failed message deliveries. + Default: `10s` + +```pubsub.retry.maximumBackoff``` +: The maximum backoff duration for retrying failed message deliveries. + Default: `10m`
diff --git a/src/test/java/com/googlesource/gerrit/plugins/highavailability/gcp/EmulatedPubSub.java b/src/test/java/com/googlesource/gerrit/plugins/highavailability/gcp/EmulatedPubSub.java new file mode 100644 index 0000000..7ec900f --- /dev/null +++ b/src/test/java/com/googlesource/gerrit/plugins/highavailability/gcp/EmulatedPubSub.java
@@ -0,0 +1,92 @@ +// Copyright (C) 2023 The Android Open Source Project +// +// 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 com.googlesource.gerrit.plugins.highavailability.gcp; + +import com.google.api.gax.core.CredentialsProvider; +import com.google.api.gax.core.NoCredentialsProvider; +import com.google.api.gax.grpc.GrpcTransportChannel; +import com.google.api.gax.rpc.FixedTransportChannelProvider; +import com.google.pubsub.v1.TopicName; +import io.grpc.ManagedChannel; +import io.grpc.ManagedChannelBuilder; +import org.junit.Ignore; +import org.testcontainers.containers.PubSubEmulatorContainer; +import org.testcontainers.utility.DockerImageName; + +@Ignore +public class EmulatedPubSub extends PubSubTestSystem { + public static final String PROJECT_ID = "test"; + public static final String DEFAULT_TOPIC = "gerrit"; + public static final String STREAM_EVENTS_TOPIC = "stream-events"; + public static final TopicName TOPIC_NAME = TopicName.of(PROJECT_ID, DEFAULT_TOPIC); + public static final TopicName STREAM_EVENTS_TOPIC_NAME = + TopicName.of(PROJECT_ID, STREAM_EVENTS_TOPIC); + + private final PubSubEmulatorContainer container; + private final FixedTransportChannelProvider channelProvider; + private final String hostPort; + + public EmulatedPubSub() throws Exception { + container = + new PubSubEmulatorContainer( + DockerImageName.parse("gcr.io/google.com/cloudsdktool/cloud-sdk:550.0.0-emulators")); + container.start(); + hostPort = container.getEmulatorEndpoint(); + ManagedChannel channel = ManagedChannelBuilder.forTarget(hostPort).usePlaintext().build(); + channelProvider = FixedTransportChannelProvider.create(GrpcTransportChannel.create(channel)); + System.out.println( + String.format( + "PubSub emulator container started and is listening on %s", + container.getEmulatorEndpoint())); + } + + @Override + public void reset() throws Exception {} + + @Override + public void cleanup() throws Exception { + container.stop(); + container.close(); + } + + public String getHostPort() { + return hostPort; + } + + @Override + String getProjectId() { + return PROJECT_ID; + } + + @Override + String getTopicName() { + return DEFAULT_TOPIC; + } + + @Override + String getStreamEventsTopicName() { + return STREAM_EVENTS_TOPIC; + } + + @Override + String getPrivateKeyFilePath() { + return null; + } + + @Override + CredentialsProvider getCredentials() throws Exception { + return NoCredentialsProvider.create(); + } +}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/highavailability/gcp/PubSubForwarderTest.java b/src/test/java/com/googlesource/gerrit/plugins/highavailability/gcp/PubSubForwarderTest.java new file mode 100644 index 0000000..de2d894 --- /dev/null +++ b/src/test/java/com/googlesource/gerrit/plugins/highavailability/gcp/PubSubForwarderTest.java
@@ -0,0 +1,96 @@ +// Copyright (C) 2023 The Android Open Source Project +// +// 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 com.googlesource.gerrit.plugins.highavailability.gcp; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.ericsson.gerrit.plugins.highavailability.forwarder.Forwarder.Result; +import com.ericsson.gerrit.plugins.highavailability.forwarder.IndexEvent; +import com.google.pubsub.v1.ReceivedMessage; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class PubSubForwarderTest { + private PubSubTestSystem testSystem; + + TestApp app1; + TestApp app2; + + @BeforeClass + public static void startTestSystem() throws Exception {} + + @Before + public void setUp() throws Exception { + testSystem = PubSubTestSystem.create(); + app1 = new TestApp("inst-1", testSystem); + app1.start(); + + app2 = new TestApp("inst-2", testSystem); + app2.start(); + } + + @After + public void tearDown() throws Exception { + app1.stop(); + app2.stop(); + testSystem.cleanup(); + } + + @AfterClass + public static void stopTestSystem() throws Exception {} + + @Test + public void indexAccount_publishSubscribe_OK() throws Exception { + when(app2.cmdProcessor.handle(any())).thenReturn(true); + CompletableFuture<Result> result = app1.forwarder.indexAccount(100, new IndexEvent()); + assertThat(result.get().result()).isTrue(); + verify(app2.cmdProcessor, timeout(10000).times(1)).handle(any()); + } + + @Test + public void indexAccount_publishSubscribe_Retries() throws Exception { + when(app2.cmdProcessor.handle(any())).thenReturn(false, true); + CompletableFuture<Result> result = app1.forwarder.indexAccount(100, new IndexEvent()); + assertThat(result.get().result()).isTrue(); + verify(app2.cmdProcessor, timeout(10000).atLeast(2)).handle(any()); + } + + @Test + public void maxReceiveCount_exceeded_movedToDlt() throws Exception { + app2.drainDltMessages(); + + when(app2.cmdProcessor.handle(any())).thenReturn(false); + CompletableFuture<Result> result = app1.forwarder.indexAccount(100, new IndexEvent()); + assertThat(result.get().result()).isTrue(); + + // wait long enough so that max delivery attempts is exhausted and message moved to DLT + verify(app2.cmdProcessor, timeout(20000).atLeast(5)).handle(any()); + + List<ReceivedMessage> messages = app2.receiveMessagesFromDlt(); + assertThat(messages).hasSize(1); + } +}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/highavailability/gcp/PubSubTestSystem.java b/src/test/java/com/googlesource/gerrit/plugins/highavailability/gcp/PubSubTestSystem.java new file mode 100644 index 0000000..eb56c4f --- /dev/null +++ b/src/test/java/com/googlesource/gerrit/plugins/highavailability/gcp/PubSubTestSystem.java
@@ -0,0 +1,47 @@ +// Copyright (C) 2024 The Android Open Source Project +// +// 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 com.googlesource.gerrit.plugins.highavailability.gcp; + +import com.google.api.gax.core.CredentialsProvider; +import org.junit.Ignore; + +@Ignore +public abstract class PubSubTestSystem { + + public static PubSubTestSystem create() throws Exception { + PubSubTestSystem testSystem; + if (System.getenv("TEST_REAL_PUBSUB") == null) { + testSystem = new EmulatedPubSub(); + } else { + testSystem = RealPubSub.create(); + } + testSystem.reset(); + return testSystem; + } + + abstract String getProjectId(); + + abstract String getTopicName(); + + abstract String getStreamEventsTopicName(); + + abstract String getPrivateKeyFilePath(); + + abstract CredentialsProvider getCredentials() throws Exception; + + abstract void reset() throws Exception; + + abstract void cleanup() throws Exception; +}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/highavailability/gcp/RealPubSub.java b/src/test/java/com/googlesource/gerrit/plugins/highavailability/gcp/RealPubSub.java new file mode 100644 index 0000000..d4748b5 --- /dev/null +++ b/src/test/java/com/googlesource/gerrit/plugins/highavailability/gcp/RealPubSub.java
@@ -0,0 +1,101 @@ +// Copyright (C) 2024 The Android Open Source Project +// +// 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 com.googlesource.gerrit.plugins.highavailability.gcp; + +import com.google.api.client.util.Strings; +import com.google.api.gax.core.CredentialsProvider; +import com.google.api.gax.core.FixedCredentialsProvider; +import com.google.auth.oauth2.ServiceAccountCredentials; +import com.google.pubsub.v1.TopicName; +import java.io.FileInputStream; +import org.junit.Ignore; + +@Ignore +public class RealPubSub extends PubSubTestSystem { + private static RealPubSub INSTANCE; + + public static RealPubSub create() { + if (INSTANCE == null) { + String keyPath = getEnv("GOOGLE_APPLICATION_CREDENTIALS"); + String project = getEnv("GCP_PROJECT"); + String topic = System.getenv("PUBSUB_TOPIC"); + String streamEventsTopic = System.getenv("PUBSUB_STREAM_EVENTS_TOPIC"); + if (Strings.isNullOrEmpty(topic)) { + INSTANCE = new RealPubSub(keyPath, project); + } else { + INSTANCE = new RealPubSub(keyPath, project, topic, streamEventsTopic); + } + } + return INSTANCE; + } + + private static String getEnv(String name) { + String value = System.getenv(name); + if (Strings.isNullOrEmpty(value)) { + throw new IllegalStateException("Environment variable " + name + " is not set"); + } + return value; + } + + private final String keyPath; + private final TopicName topicName; + private final TopicName streamEventsTopicName; + + private RealPubSub(String keyPath, String project) { + this( + keyPath, + project, + "gerrit-pubsub-" + System.currentTimeMillis(), + "gerrit-stream-events-" + System.currentTimeMillis()); + } + + private RealPubSub(String keyPath, String project, String topic, String streamEventsTopic) { + this.keyPath = keyPath; + this.topicName = TopicName.of(project, topic); + this.streamEventsTopicName = TopicName.of(project, streamEventsTopic); + } + + @Override + public void reset() throws Exception {} + + @Override + void cleanup() throws Exception {} + + @Override + String getProjectId() { + return topicName.getProject(); + } + + @Override + String getTopicName() { + return topicName.getTopic(); + } + + @Override + String getStreamEventsTopicName() { + return streamEventsTopicName.getTopic(); + } + + @Override + String getPrivateKeyFilePath() { + return keyPath; + } + + @Override + CredentialsProvider getCredentials() throws Exception { + return FixedCredentialsProvider.create( + ServiceAccountCredentials.fromStream(new FileInputStream(keyPath))); + } +}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/highavailability/gcp/TestApp.java b/src/test/java/com/googlesource/gerrit/plugins/highavailability/gcp/TestApp.java new file mode 100644 index 0000000..c765375 --- /dev/null +++ b/src/test/java/com/googlesource/gerrit/plugins/highavailability/gcp/TestApp.java
@@ -0,0 +1,130 @@ +package com.googlesource.gerrit.plugins.highavailability.gcp; + +import static org.mockito.Mockito.mock; + +import com.ericsson.gerrit.plugins.highavailability.ApiModule; +import com.ericsson.gerrit.plugins.highavailability.forwarder.commands.CommandProcessor; +import com.google.cloud.pubsub.v1.SubscriptionAdminClient; +import com.google.gerrit.extensions.registration.DynamicItem; +import com.google.gerrit.server.config.GerritInstanceId; +import com.google.gerrit.server.events.EventGson; +import com.google.gerrit.server.events.EventGsonProvider; +import com.google.gson.Gson; +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.Inject; +import com.google.inject.Injector; +import com.google.inject.Module; +import com.google.inject.util.Modules; +import com.google.pubsub.v1.AcknowledgeRequest; +import com.google.pubsub.v1.PullRequest; +import com.google.pubsub.v1.PullResponse; +import com.google.pubsub.v1.ReceivedMessage; +import com.googlesource.gerrit.plugins.highavailability.gcp.Configuration.Identity; +import com.googlesource.gerrit.plugins.highavailability.gcp.Configuration.Limits; +import com.googlesource.gerrit.plugins.highavailability.gcp.Configuration.Retention; +import com.googlesource.gerrit.plugins.highavailability.gcp.Configuration.Retry; +import com.googlesource.gerrit.plugins.highavailability.gcp.Configuration.Topics; +import java.time.Duration; +import java.util.List; +import java.util.stream.Collectors; +import org.junit.Ignore; + +@Ignore +public class TestApp { + private final PubSubTestSystem testSystem; + private final String instanceId; + private PubSubNames names; + + CommandProcessor cmdProcessor = mock(CommandProcessor.class); + + @Inject PubSubForwarder forwarder; + @Inject private OnStartStop onStartStop; + @Inject private SubscriptionAdminClient subscriptionAdminClient; + @Inject private @GcpProjectId String projectId; + @Inject private PubSubNamingFactory namingFactory; + + TestApp(String instanceId, PubSubTestSystem testSystem) { + this.instanceId = instanceId; + this.testSystem = testSystem; + } + + public void start() throws Exception { + Configuration cfg = + new Configuration( + new Identity(testSystem.getProjectId(), testSystem.getPrivateKeyFilePath()), + new Topics(testSystem.getTopicName(), testSystem.getStreamEventsTopicName()), + new Limits(Duration.ofSeconds(30), Duration.ofSeconds(30), 4, 4), + new Retention(Duration.ofSeconds(10), Duration.ofMinutes(10), false), + new Retry(5, Duration.ofSeconds(1), Duration.ofSeconds(10))); + + Module testSupportModule = + new AbstractModule() { + @Override + protected void configure() { + bind(Configuration.class).toInstance(cfg); + bind(Gson.class) + .annotatedWith(EventGson.class) + .toInstance(new EventGsonProvider().get()); + bind(String.class).annotatedWith(GerritInstanceId.class).toInstance(instanceId); + install(new ApiModule()); + DynamicItem.bind(binder(), CommandProcessor.class).toInstance(cmdProcessor); + } + }; + + Module gcpModule; + if (testSystem instanceof EmulatedPubSub) { + gcpModule = + GcpPubSubForwarderModule.createForEmulator(((EmulatedPubSub) testSystem).getHostPort()); + } else { + gcpModule = GcpPubSubForwarderModule.createForRealPubSub(); + } + + Module testModule = Modules.combine(testSupportModule, gcpModule); + Injector injector = Guice.createInjector(testModule); + injector.injectMembers(this); + + names = namingFactory.createFor(testSystem.getTopicName()); + onStartStop.start(); + } + + public List<ReceivedMessage> receiveMessagesFromDlt() { + PullRequest pullRequest = + PullRequest.newBuilder() + .setSubscription(names.deadLetterSubscription().toString()) + .setMaxMessages(10) + .build(); + + PullResponse pullResponse = subscriptionAdminClient.pullCallable().call(pullRequest); + List<ReceivedMessage> messages = pullResponse.getReceivedMessagesList(); + ack(messages); + return messages; + } + + private void ack(List<ReceivedMessage> messages) { + List<String> ackIds = + messages.stream().map(ReceivedMessage::getAckId).collect(Collectors.toList()); + if (ackIds.isEmpty()) { + return; + } + AcknowledgeRequest acknowledgeRequest = + AcknowledgeRequest.newBuilder() + .setSubscription(names.deadLetterSubscription().toString()) + .addAllAckIds(ackIds) + .build(); + subscriptionAdminClient.acknowledge(acknowledgeRequest); + } + + public void drainDltMessages() { + List<ReceivedMessage> messages; + do { + messages = receiveMessagesFromDlt(); + ack(messages); + } while (!messages.isEmpty()); + } + + public void stop() throws Exception { + onStartStop.stop(); + testSystem.cleanup(); + } +}