diff --git a/LICENSES/vendor/io.envoyproxy.controlplane/api/LICENSE b/LICENSES/vendor/io.envoyproxy.controlplane/api/LICENSE new file mode 100644 index 0000000000..7a4a3ea242 --- /dev/null +++ b/LICENSES/vendor/io.envoyproxy.controlplane/api/LICENSE @@ -0,0 +1,202 @@ + + 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. \ No newline at end of file diff --git a/LICENSES/vendor/io.grpc/grpc-netty/LICENSE b/LICENSES/vendor/io.grpc/grpc-netty/LICENSE new file mode 100644 index 0000000000..7a4a3ea242 --- /dev/null +++ b/LICENSES/vendor/io.grpc/grpc-netty/LICENSE @@ -0,0 +1,202 @@ + + 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. \ No newline at end of file diff --git a/LICENSES/vendor/io.grpc/grpc-protobuf/LICENSE b/LICENSES/vendor/io.grpc/grpc-protobuf/LICENSE new file mode 100644 index 0000000000..7a4a3ea242 --- /dev/null +++ b/LICENSES/vendor/io.grpc/grpc-protobuf/LICENSE @@ -0,0 +1,202 @@ + + 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. \ No newline at end of file diff --git a/NOTICE b/NOTICE index 9c4b4fb1d7..81c381c2e9 100644 --- a/NOTICE +++ b/NOTICE @@ -98,7 +98,10 @@ mockito-inline (org.mockito:mockito-inline) - https://github.com/mockito/mockito Project Lombok (org.projectlombok:lombok) - https://projectlombok.org JUL to SLF4J bridge (org.slf4j:jul-to-slf4j) - http://www.slf4j.org SLF4J API Module (org.slf4j:slf4j-api) - http://www.slf4j.org - +api (io.envoyproxy.controlplane:api) - https://github.com/envoyproxy/java-control-plane +grpc-netty (io.grpc:grpc-netty) https://github.com/grpc/grpc-java +grpc-stub (io.grpc:grpc-stub) https://github.com/grpc/grpc-java +grpc-protobuf (io.grpc:grpc-protobuf) https://github.com/grpc/grpc-java - The works of other projects included in this project. diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/XdsCoreService.java b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/XdsCoreService.java new file mode 100644 index 0000000000..bd6aee232d --- /dev/null +++ b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/XdsCoreService.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2024-2024 Sermant Authors. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.sermant.core.service.xds; + +import io.sermant.core.service.BaseService; + +/** + * xDS core service interface, to start or stop xDS service, or get the specific xDS capability implementation class + * + * @author daizhenyu + * @since 2024-05-21 + **/ +public interface XdsCoreService extends BaseService { + /** + * get xDS service discovery + * + * @return XdsServiceDiscovery + */ + XdsServiceDiscovery getXdsServiceDiscovery(); +} diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/XdsServiceDiscovery.java b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/XdsServiceDiscovery.java new file mode 100644 index 0000000000..e65f204f57 --- /dev/null +++ b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/XdsServiceDiscovery.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2024-2024 Sermant Authors. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.sermant.core.service.xds; + +import io.sermant.core.service.xds.entity.ServiceInstance; +import io.sermant.core.service.xds.listener.XdsServiceDiscoveryListener; + +import java.util.Set; + +/** + * xds service discovery interface + * + * @author daizhenyu + * @since 2024-05-21 + **/ +public interface XdsServiceDiscovery { + /** + * get service instance by service name + * + * @param serviceName service name + * @return service instances + */ + Set getServiceInstance(String serviceName); + + /** + * subscribe service instance by service name, the listener will be triggered when the service instance changes + * + * @param serviceName service name + * @param listener listener + */ + void subscribeServiceInstance(String serviceName, XdsServiceDiscoveryListener listener); +} diff --git a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/XdsCoreServiceImpl.java b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/XdsCoreServiceImpl.java new file mode 100644 index 0000000000..5da9eee44b --- /dev/null +++ b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/XdsCoreServiceImpl.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2024-2024 Sermant Authors. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.sermant.implement.service.xds; + +import io.sermant.core.common.LoggerFactory; +import io.sermant.core.service.xds.XdsCoreService; +import io.sermant.core.service.xds.XdsServiceDiscovery; +import io.sermant.implement.service.xds.client.XdsClient; +import io.sermant.implement.service.xds.discovery.XdsServiceDiscoveryImpl; + +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * XdsCoreService impl + * + * @author daizhenyu + * @since 2024-05-21 + **/ +public class XdsCoreServiceImpl implements XdsCoreService { + private static final Logger LOGGER = LoggerFactory.getLogger(); + + private XdsServiceDiscovery xdsServiceDiscovery; + + private XdsClient client; + + @Override + public void start() { + client = new XdsClient(); + xdsServiceDiscovery = new XdsServiceDiscoveryImpl(client); + } + + @Override + public void stop() { + try { + client.close(); + } catch (IOException e) { + LOGGER.log(Level.SEVERE, "Occur error when close the XdsClient.", e); + } + } + + @Override + public XdsServiceDiscovery getXdsServiceDiscovery() { + return xdsServiceDiscovery; + } +} diff --git a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/cache/XdsDataCache.java b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/cache/XdsDataCache.java index 572adbe9f3..777ae595b3 100644 --- a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/cache/XdsDataCache.java +++ b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/cache/XdsDataCache.java @@ -21,10 +21,12 @@ import io.sermant.core.service.xds.entity.ServiceInstance; import io.sermant.core.service.xds.listener.XdsServiceDiscoveryListener; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -38,19 +40,19 @@ public class XdsDataCache { /** * key:service name value:instances */ - public static final Map> SERVICE_INSTANCES = + private static final Map> SERVICE_INSTANCES = new ConcurrentHashMap<>(); /** * key:service name value:listener list */ - public static final Map> SERVICE_DISCOVER_LISTENER = + private static final Map> SERVICE_DISCOVER_LISTENER = new ConcurrentHashMap<>(); /** * request StreamObserver */ - public static final Map> REQUEST_OBSERVERS = new ConcurrentHashMap<>(); + private static final Map> REQUEST_OBSERVERS = new ConcurrentHashMap<>(); /** * key:service name value:cluster map @@ -60,6 +62,113 @@ public class XdsDataCache { private XdsDataCache() { } + /** + * update ServiceInstance + * + * @param serviceName service name + * @param instances service instance + */ + public static void updateServiceInstance(String serviceName, Set instances) { + SERVICE_INSTANCES.put(serviceName, instances); + } + + /** + * get ServiceInstance + * + * @param serviceName service name + * @return ServiceInstance + */ + public static Set getServiceInstance(String serviceName) { + return SERVICE_INSTANCES.getOrDefault(serviceName, Collections.EMPTY_SET); + } + + /** + * remove ServiceInstance + * + * @param serviceName service name + */ + public static void removeServiceInstance(String serviceName) { + SERVICE_INSTANCES.remove(serviceName); + } + + /** + * add ServiceDiscoveryListener + * + * @param serviceName service name + * @param listener listener + */ + public static void addServiceDiscoveryListener(String serviceName, XdsServiceDiscoveryListener listener) { + SERVICE_DISCOVER_LISTENER.computeIfAbsent(serviceName, value -> new ArrayList<>()) + .add(listener); + } + + /** + * get ServiceDiscoveryListeners + * + * @param serviceName service name + * @return ServiceDiscoveryListeners + */ + public static List getServiceDiscoveryListeners(String serviceName) { + return SERVICE_DISCOVER_LISTENER.getOrDefault(serviceName, Collections.EMPTY_LIST); + } + + /** + * remove ServiceDiscoveryListeners + * + * @param serviceName service name + */ + public static void removeServiceDiscoveryListeners(String serviceName) { + SERVICE_DISCOVER_LISTENER.remove(serviceName); + } + + /** + * update RequestObserver + * + * @param serviceName service name + * @param requestObserver request observer + */ + public static void updateRequestObserver(String serviceName, StreamObserver requestObserver) { + REQUEST_OBSERVERS.put(serviceName, requestObserver); + } + + /** + * Whether the service's request observer exists + * + * @param serviceName service name + * @return boolean + */ + public static boolean isContainsRequestObserver(String serviceName) { + return REQUEST_OBSERVERS.containsKey(serviceName); + } + + /** + * get request observers entry + * + * @return request observer + */ + public static Set>> getRequestObserversEntry() { + return REQUEST_OBSERVERS.entrySet(); + } + + /** + * get request observer by service name + * + * @param serviceName service name + * @return request observer + */ + public static StreamObserver getRequestObserver(String serviceName) { + return REQUEST_OBSERVERS.get(serviceName); + } + + /** + * remove request observer by service name + * + * @param serviceName service name + */ + public static void removeRequestObserver(String serviceName) { + REQUEST_OBSERVERS.remove(serviceName); + } + /** * update the mapping between service and cluster * diff --git a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/discovery/XdsServiceDiscoveryImpl.java b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/discovery/XdsServiceDiscoveryImpl.java new file mode 100644 index 0000000000..6c1c53f46d --- /dev/null +++ b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/discovery/XdsServiceDiscoveryImpl.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2024-2024 Sermant Authors. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.sermant.implement.service.xds.discovery; + +import io.sermant.core.common.LoggerFactory; +import io.sermant.core.service.xds.XdsServiceDiscovery; +import io.sermant.core.service.xds.entity.ServiceInstance; +import io.sermant.core.service.xds.listener.XdsServiceDiscoveryListener; +import io.sermant.implement.service.xds.cache.XdsDataCache; +import io.sermant.implement.service.xds.client.XdsClient; +import io.sermant.implement.service.xds.env.XdsConstant; +import io.sermant.implement.service.xds.handler.CdsHandler; +import io.sermant.implement.service.xds.handler.EdsHandler; + +import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantLock; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * xDS service discovery service, Used by the plugin to obtain service instances through the xDS protocol + * + * @author daizhenyu + * @since 2024-05-08 + **/ +public class XdsServiceDiscoveryImpl implements XdsServiceDiscovery { + private static final Logger LOGGER = LoggerFactory.getLogger(); + + private static final int TIMEOUT = 5; + + private static final ReentrantLock LOCK = new ReentrantLock(); + + private final XdsClient client; + + private EdsHandler edsHandler; + + /** + * constructor + * + * @param client xds client + */ + public XdsServiceDiscoveryImpl(XdsClient client) { + this.client = client; + CdsHandler cdsHandler = new CdsHandler(client); + edsHandler = new EdsHandler(client); + cdsHandler.subscribe(XdsConstant.CDS_ALL_RESOURCE, null); + } + + /** + * subscribe service instance by service name, the listener will be triggered when the service instance changes + * + * @param serviceName service name + * @return service instances + */ + @Override + public Set getServiceInstance(String serviceName) { + CountDownLatch countDownLatch = new CountDownLatch(1); + + // first check the cache and return if service instance exists + if (XdsDataCache.isContainsRequestObserver(serviceName)) { + return XdsDataCache.getServiceInstance(serviceName); + } + + // locking ensures that a service only creates one stream + LOCK.lock(); + try { + // check the cache again after locking and return if service instance exists + if (XdsDataCache.isContainsRequestObserver(serviceName)) { + return XdsDataCache.getServiceInstance(serviceName); + } + edsHandler.subscribe(serviceName, countDownLatch); + } finally { + LOCK.unlock(); + } + try { + countDownLatch.await(TIMEOUT, TimeUnit.SECONDS); + } catch (InterruptedException e) { + LOGGER.log(Level.WARNING, "Occur InterruptedException when wait server send message.", e); + } + return XdsDataCache.getServiceInstance(serviceName); + } + + /** + * subscribe service instance by service name, the listener will be triggered when the service instance changes + * + * @param serviceName service name + * @param listener listener + */ + @Override + public void subscribeServiceInstance(String serviceName, XdsServiceDiscoveryListener listener) { + // cache listener + XdsDataCache.addServiceDiscoveryListener(serviceName, listener); + + // first check the cache and return if service instance exists + if (XdsDataCache.isContainsRequestObserver(serviceName)) { + listener.process(XdsDataCache.getServiceInstance(serviceName)); + return; + } + + // locking ensures that a service only creates one stream + LOCK.lock(); + try { + // check the cache again after locking and notify listener if service instance exists + if (XdsDataCache.isContainsRequestObserver(serviceName)) { + listener.process(XdsDataCache.getServiceInstance(serviceName)); + return; + } + + // subscribe service instance + edsHandler.subscribe(serviceName, null); + } finally { + LOCK.unlock(); + } + } +} \ No newline at end of file diff --git a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/env/XdsConstant.java b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/env/XdsConstant.java index bf95a13330..d859e1953b 100644 --- a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/env/XdsConstant.java +++ b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/env/XdsConstant.java @@ -28,11 +28,6 @@ public class XdsConstant { */ public static final String POD_NAME_ENV = "HOSTNAME"; - /** - * rsa key size - */ - public static final int RSA_KEY_SIZE = 2048; - /** * eds resource type */ diff --git a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/handler/CdsHandler.java b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/handler/CdsHandler.java new file mode 100644 index 0000000000..e2a93e03ab --- /dev/null +++ b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/handler/CdsHandler.java @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + +/* + * Based on dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/AbstractProtocol.java + * from the Apache dubbo project. + */ + +package io.sermant.implement.service.xds.handler; + +import com.google.protobuf.Any; +import com.google.protobuf.InvalidProtocolBufferException; + +import io.envoyproxy.envoy.config.cluster.v3.Cluster; +import io.envoyproxy.envoy.service.discovery.v3.DiscoveryRequest; +import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse; +import io.grpc.stub.StreamObserver; +import io.sermant.implement.service.xds.cache.XdsDataCache; +import io.sermant.implement.service.xds.client.XdsClient; +import io.sermant.implement.service.xds.env.XdsConstant; +import io.sermant.implement.service.xds.utils.XdsProtocolTransformer; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.logging.Level; + +/** + * CdsHandler + * + * @author daizhenyu + * @since 2024-05-13 + **/ +public class CdsHandler extends XdsHandler { + /** + * construction method + * + * @param client xds client + */ + public CdsHandler(XdsClient client) { + super(client); + this.resourceType = XdsConstant.CDS_RESOURCE_TYPE; + } + + @Override + protected void handleResponse(String requestKey, DiscoveryResponse response) { + Map> oldMapping = XdsDataCache.getServiceNameMapping(); + + // The contents of the Cds protocol were resolved, + // and the mapping between the service name and the cluster was updated + Map> newMapping = XdsProtocolTransformer + .getService2ClusterMapping(decodeResource2Cluster(response)); + XdsDataCache.updateServiceNameMapping(newMapping); + + // send ack + StreamObserver requestObserver = XdsDataCache.getRequestObserver(requestKey); + requestObserver.onNext(builtAckDiscoveryRequest(response, Collections.EMPTY_SET)); + + // Eds is updated based on the new mapping relationship + for (Entry> entry : XdsDataCache.getRequestObserversEntry()) { + String key = entry.getKey(); + if (XdsConstant.CDS_ALL_RESOURCE.equals(key)) { + continue; + } + + // There is no need to resubscribe when the cluster resources corresponding to the service have not changed. + if (newMapping.getOrDefault(key, Collections.EMPTY_SET) + .equals(oldMapping.getOrDefault(key, Collections.EMPTY_SET))) { + continue; + } + StreamObserver requestStreamObserver = entry.getValue(); + requestStreamObserver.onNext(buildDiscoveryRequest(XdsConstant.EDS_RESOURCE_TYPE, null, null, + XdsDataCache.getClustersByServiceName(key))); + } + } + + private List decodeResource2Cluster(DiscoveryResponse response) { + List clusters = new ArrayList<>(); + for (Any any : response.getResourcesList()) { + try { + clusters.add(any.unpack(Cluster.class)); + } catch (InvalidProtocolBufferException e) { + LOGGER.log(Level.SEVERE, "Decode resource to cluster failed.", e); + } + } + return clusters; + } + + @Override + public void subscribe(String requestKey, CountDownLatch countDownLatch) { + StreamObserver requestStreamObserver = client + .getDiscoveryRequestObserver(getResponseStreamObserver(requestKey, countDownLatch)); + requestStreamObserver.onNext(buildDiscoveryRequest(resourceType, null, null, Collections.EMPTY_SET)); + XdsDataCache.updateRequestObserver(requestKey, requestStreamObserver); + } +} \ No newline at end of file diff --git a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/handler/EdsHandler.java b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/handler/EdsHandler.java new file mode 100644 index 0000000000..cdc79ce7e3 --- /dev/null +++ b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/handler/EdsHandler.java @@ -0,0 +1,118 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + +/* + * Based on dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/impl/EdsProtocol.java + * from the Apache dubbo project. + */ + +package io.sermant.implement.service.xds.handler; + +import com.google.protobuf.Any; +import com.google.protobuf.InvalidProtocolBufferException; + +import io.envoyproxy.envoy.config.endpoint.v3.ClusterLoadAssignment; +import io.envoyproxy.envoy.service.discovery.v3.DiscoveryRequest; +import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse; +import io.grpc.stub.StreamObserver; +import io.sermant.core.service.xds.entity.ServiceInstance; +import io.sermant.core.service.xds.listener.XdsServiceDiscoveryListener; +import io.sermant.core.utils.CollectionUtils; +import io.sermant.implement.service.xds.cache.XdsDataCache; +import io.sermant.implement.service.xds.client.XdsClient; +import io.sermant.implement.service.xds.env.XdsConstant; +import io.sermant.implement.service.xds.utils.XdsProtocolTransformer; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.logging.Level; + +/** + * EdsHandler + * + * @author daizhenyu + * @since 2024-05-13 + **/ +public class EdsHandler extends XdsHandler { + /** + * construction method + * + * @param client xds client + */ + public EdsHandler(XdsClient client) { + super(client); + this.resourceType = XdsConstant.EDS_RESOURCE_TYPE; + } + + @Override + protected void handleResponse(String requestKey, DiscoveryResponse response) { + // The contents of the Cds protocol were resolved, + // and the mapping between the service name and the cluster was updated + Set newInstances = XdsProtocolTransformer + .getServiceInstances(decodeResource2ClusterLoadAssignment(response)); + + // send ack + StreamObserver requestObserver = XdsDataCache.getRequestObserver(requestKey); + requestObserver.onNext(builtAckDiscoveryRequest(response, XdsDataCache.getClustersByServiceName(requestKey))); + + // check whether the service instance has changed + if (!isInstanceChanged(XdsDataCache.getServiceInstance(requestKey), newInstances)) { + return; + } + + XdsDataCache.updateServiceInstance(requestKey, newInstances); + + // invoke the listener corresponding to service + List listeners = XdsDataCache.getServiceDiscoveryListeners(requestKey); + for (XdsServiceDiscoveryListener listener : listeners) { + listener.process(newInstances); + } + } + + private List decodeResource2ClusterLoadAssignment(DiscoveryResponse response) { + List assignments = new ArrayList<>(); + for (Any any : response.getResourcesList()) { + try { + assignments.add(any.unpack(ClusterLoadAssignment.class)); + } catch (InvalidProtocolBufferException e) { + LOGGER.log(Level.SEVERE, "Decode resource to ClusterLoadAssignment failed.", e); + } + } + return assignments; + } + + @Override + public void subscribe(String resourceKey, CountDownLatch countDownLatch) { + StreamObserver requestStreamObserver = client + .getDiscoveryRequestObserver(getResponseStreamObserver(resourceKey, countDownLatch)); + requestStreamObserver.onNext(buildDiscoveryRequest(resourceType, null, null, + XdsDataCache.getClustersByServiceName(resourceKey))); + XdsDataCache.updateRequestObserver(resourceKey, requestStreamObserver); + } + + private boolean isInstanceChanged(Set oldInstances, Set newInstances) { + if (CollectionUtils.isEmpty(oldInstances) && CollectionUtils.isEmpty(newInstances)) { + return false; + } + if (oldInstances == null || newInstances == null) { + return true; + } + return oldInstances.size() != newInstances.size() || !oldInstances.equals(newInstances); + } +} \ No newline at end of file diff --git a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/handler/XdsHandler.java b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/handler/XdsHandler.java index 5d941912cb..5a950ba227 100644 --- a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/handler/XdsHandler.java +++ b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/handler/XdsHandler.java @@ -49,7 +49,7 @@ public abstract class XdsHandler implements XdsServiceAction { protected final XdsClient client; - protected final String resourceType; + protected String resourceType; protected Node node; @@ -57,16 +57,14 @@ public abstract class XdsHandler implements XdsServiceAction { * construction method * * @param client xds client - * @param resourceType xds resource type */ - public XdsHandler(XdsClient client, String resourceType) { + public XdsHandler(XdsClient client) { this.client = client; - this.resourceType = resourceType; createNode(); } /** - * built DiscoveryRequest + * build DiscoveryRequest * * @param type resource type * @param version resource version @@ -74,7 +72,7 @@ public XdsHandler(XdsClient client, String resourceType) { * @param resourceName resource name * @return DiscoveryRequest */ - protected DiscoveryRequest builtDiscoveryRequest(String type, String version, String nonce, + protected DiscoveryRequest buildDiscoveryRequest(String type, String version, String nonce, Set resourceName) { DiscoveryRequest.Builder builder = DiscoveryRequest.newBuilder() .setNode(node) @@ -97,7 +95,7 @@ protected DiscoveryRequest builtDiscoveryRequest(String type, String version, St * @return DiscoveryRequest */ protected DiscoveryRequest builtAckDiscoveryRequest(DiscoveryResponse response, Set resourceName) { - return builtDiscoveryRequest(response.getTypeUrl(), response.getVersionInfo(), response.getNonce(), + return buildDiscoveryRequest(response.getTypeUrl(), response.getVersionInfo(), response.getNonce(), resourceName); } @@ -135,14 +133,14 @@ protected StreamObserver getResponseStreamObserver(String req @Override public void onNext(DiscoveryResponse response) { handleResponse(requestKey, response); - if (countDownLatch != null && countDownLatch.getCount() == 1) { + if (countDownLatch != null) { countDownLatch.countDown(); } } @Override public void onError(Throwable throwable) { - if (countDownLatch != null && countDownLatch.getCount() == 1) { + if (countDownLatch != null) { countDownLatch.countDown(); } client.updateChannel(); @@ -152,7 +150,7 @@ public void onError(Throwable throwable) { @Override public void onCompleted() { - if (countDownLatch != null && countDownLatch.getCount() == 1) { + if (countDownLatch != null) { countDownLatch.countDown(); } subscribe(requestKey, null); diff --git a/sermant-agentcore/sermant-agentcore-implement/src/main/resources/META-INF/services/io.sermant.core.service.BaseService b/sermant-agentcore/sermant-agentcore-implement/src/main/resources/META-INF/services/io.sermant.core.service.BaseService index 4d8a3b162c..6212b5ecea 100644 --- a/sermant-agentcore/sermant-agentcore-implement/src/main/resources/META-INF/services/io.sermant.core.service.BaseService +++ b/sermant-agentcore/sermant-agentcore-implement/src/main/resources/META-INF/services/io.sermant.core.service.BaseService @@ -2,4 +2,5 @@ io.sermant.implement.service.heartbeat.HeartbeatServiceImpl io.sermant.implement.service.send.netty.NettyGatewayClient io.sermant.implement.service.dynamicconfig.BufferedDynamicConfigService io.sermant.implement.service.tracing.TracingServiceImpl -io.sermant.implement.service.inject.InjectServiceImpl \ No newline at end of file +io.sermant.implement.service.inject.InjectServiceImpl +io.sermant.implement.service.xds.XdsCoreServiceImpl \ No newline at end of file diff --git a/sermant-agentcore/sermant-agentcore-implement/src/test/java/io/sermant/implement/service/xds/BaseXdsTest.java b/sermant-agentcore/sermant-agentcore-implement/src/test/java/io/sermant/implement/service/xds/BaseXdsTest.java new file mode 100644 index 0000000000..c3173b60e8 --- /dev/null +++ b/sermant-agentcore/sermant-agentcore-implement/src/test/java/io/sermant/implement/service/xds/BaseXdsTest.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2024-2024 Sermant Authors. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.sermant.implement.service.xds; + +import io.sermant.core.config.ConfigManager; +import io.sermant.core.plugin.config.ServiceMeta; +import io.sermant.implement.service.xds.client.XdsClient; +import io.sermant.implement.service.xds.handler.StreamObserverRequestImpl; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.mockito.MockedStatic; +import org.mockito.Mockito; + +/** + * @author daizhenyu + * @since 2024-05-25 + **/ +public abstract class BaseXdsTest { + private static MockedStatic mockedConfigManager; + + protected static XdsClient client; + + protected static StreamObserverRequestImpl requestStreamObserver; + + @BeforeClass + public static void before() { + requestStreamObserver = new StreamObserverRequestImpl(); + client = Mockito.mock(XdsClient.class); + Mockito.doReturn(requestStreamObserver).when(client).getDiscoveryRequestObserver(Mockito.any()); + + mockedConfigManager = Mockito.mockStatic(ConfigManager.class); + ServiceMeta meta = new ServiceMeta(); + meta.setProject("default"); + mockedConfigManager.when(() -> ConfigManager.getConfig(ServiceMeta.class)).thenReturn(meta); + } + + @AfterClass + public static void after() { + Mockito.clearAllCaches(); + if (mockedConfigManager != null) { + mockedConfigManager.close(); + } + } +} diff --git a/sermant-agentcore/sermant-agentcore-implement/src/test/java/io/sermant/implement/service/xds/cache/XdsDataCacheTest.java b/sermant-agentcore/sermant-agentcore-implement/src/test/java/io/sermant/implement/service/xds/cache/XdsDataCacheTest.java index 32bfeb4302..01871f3d56 100644 --- a/sermant-agentcore/sermant-agentcore-implement/src/test/java/io/sermant/implement/service/xds/cache/XdsDataCacheTest.java +++ b/sermant-agentcore/sermant-agentcore-implement/src/test/java/io/sermant/implement/service/xds/cache/XdsDataCacheTest.java @@ -16,12 +16,20 @@ package io.sermant.implement.service.xds.cache; +import io.envoyproxy.envoy.service.discovery.v3.DiscoveryRequest; +import io.grpc.stub.StreamObserver; +import io.sermant.core.service.xds.entity.ServiceInstance; +import io.sermant.core.service.xds.listener.XdsServiceDiscoveryListener; +import io.sermant.implement.service.xds.entity.XdsServiceInstance; +import io.sermant.implement.service.xds.handler.StreamObserverRequestImpl; +import io.sermant.implement.service.xds.handler.XdsServiceDiscoveryListenerImpl; + import org.junit.Assert; import org.junit.Test; -import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; @@ -32,28 +40,67 @@ * @since 2024-05-24 **/ public class XdsDataCacheTest { + @Test + public void testServiceInstance() { + XdsServiceInstance instance = new XdsServiceInstance(); + Set instanceSet = new HashSet<>(); + instanceSet.add(instance); + XdsDataCache.updateServiceInstance("serviceA", instanceSet); + Set serviceA = XdsDataCache.getServiceInstance("serviceA"); + Set serviceB = XdsDataCache.getServiceInstance("serviceB"); + Assert.assertEquals(1, serviceA.size()); + Assert.assertEquals(0, serviceB.size()); + XdsDataCache.removeServiceInstance("serviceA"); + serviceA = XdsDataCache.getServiceInstance("serviceA"); + Assert.assertEquals(0, serviceA.size()); + } + + @Test + public void testServiceListener() { + // clean data + XdsDataCache.removeServiceDiscoveryListeners("serviceA"); + + XdsServiceDiscoveryListener listener = new XdsServiceDiscoveryListenerImpl(); + XdsDataCache.addServiceDiscoveryListener("serviceA", listener); + List listenerA = XdsDataCache.getServiceDiscoveryListeners("serviceA"); + List listenerB = XdsDataCache.getServiceDiscoveryListeners("serviceB"); + Assert.assertEquals(1, listenerA.size()); + Assert.assertEquals(0, listenerB.size()); + XdsDataCache.removeServiceDiscoveryListeners("serviceA"); + listenerA = XdsDataCache.getServiceDiscoveryListeners("serviceA"); + Assert.assertEquals(0, listenerA.size()); + } + + @Test + public void testRequestObserver() { + StreamObserver requestObserver = new StreamObserverRequestImpl(); + XdsDataCache.updateRequestObserver("serviceA", requestObserver); + Assert.assertTrue(XdsDataCache.isContainsRequestObserver("serviceA")); + } + @Test public void testGetClustersByServiceName() { Map> mapping = new HashMap<>(); Set clusters = new HashSet<>(); clusters.add("cluster"); - mapping.put("service-A", clusters); + mapping.put("serviceA", clusters); Set result; - // serviceNameMapping is null - result = XdsDataCache.getClustersByServiceName("service-A"); + // serviceNameMapping is empty + XdsDataCache.updateServiceNameMapping(new HashMap<>()); + result = XdsDataCache.getClustersByServiceName("serviceA"); Assert.assertNotNull(result); Assert.assertEquals(0, result.size()); - // serviceNameMapping is not null, get un cached service + // serviceNameMapping is not empty, get un cached service XdsDataCache.updateServiceNameMapping(mapping); - result = XdsDataCache.getClustersByServiceName("service-B"); + result = XdsDataCache.getClustersByServiceName("serviceB"); Assert.assertNotNull(result); Assert.assertEquals(0, result.size()); // serviceNameMapping is not null, get cached service XdsDataCache.updateServiceNameMapping(mapping); - result = XdsDataCache.getClustersByServiceName("service-A"); + result = XdsDataCache.getClustersByServiceName("serviceA"); Assert.assertNotNull(result); Assert.assertEquals(1, result.size()); Assert.assertTrue(result.contains("cluster")); diff --git a/sermant-agentcore/sermant-agentcore-implement/src/test/java/io/sermant/implement/service/xds/discovery/XdsServiceDiscoveryImplTest.java b/sermant-agentcore/sermant-agentcore-implement/src/test/java/io/sermant/implement/service/xds/discovery/XdsServiceDiscoveryImplTest.java new file mode 100644 index 0000000000..10b1b807d4 --- /dev/null +++ b/sermant-agentcore/sermant-agentcore-implement/src/test/java/io/sermant/implement/service/xds/discovery/XdsServiceDiscoveryImplTest.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2024-2024 Sermant Authors. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.sermant.implement.service.xds.discovery; + +import io.sermant.core.service.xds.entity.ServiceInstance; +import io.sermant.implement.service.xds.BaseXdsTest; +import io.sermant.implement.service.xds.cache.XdsDataCache; +import io.sermant.implement.service.xds.entity.XdsServiceInstance; +import io.sermant.implement.service.xds.handler.XdsServiceDiscoveryListenerImpl; + +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.mockito.Mockito; + +import java.util.HashSet; +import java.util.Set; + +/** + * XdsServiceDiscoveryImplTest + * + * @author daizhenyu + * @since 2024-05-24 + **/ +public class XdsServiceDiscoveryImplTest extends BaseXdsTest { + private static String serviceName = "serviceA"; + + private static XdsServiceDiscoveryImpl xdsServiceDiscovery; + + @BeforeClass + public static void setUp() { + xdsServiceDiscovery = new XdsServiceDiscoveryImpl(client); + } + + @AfterClass + public static void tearDown() { + XdsDataCache.removeServiceInstance(serviceName); + XdsDataCache.removeRequestObserver(serviceName); + XdsDataCache.removeServiceDiscoveryListeners(serviceName); + } + + @Test + public void getServiceInstance() { + // clear data + XdsDataCache.removeServiceInstance(serviceName); + XdsDataCache.removeRequestObserver(serviceName); + + // no service instance in cache + Set result = xdsServiceDiscovery.getServiceInstance(serviceName); + Assert.assertNotNull(XdsDataCache.getRequestObserver(serviceName)); + + // service instance in cache + Set instances = new HashSet<>(); + instances.add(new XdsServiceInstance()); + XdsDataCache.updateServiceInstance(serviceName, instances); + result = xdsServiceDiscovery.getServiceInstance(serviceName); + Assert.assertEquals(1, result.size()); + } + + @Test + public void subscribeServiceInstance() { + // clear data + XdsDataCache.removeServiceInstance(serviceName); + XdsDataCache.removeServiceDiscoveryListeners(serviceName); + XdsDataCache.removeRequestObserver(serviceName); + + // no service instance in cache + XdsServiceDiscoveryListenerImpl xdsServiceDiscoveryListener = new XdsServiceDiscoveryListenerImpl(); + xdsServiceDiscovery.subscribeServiceInstance(serviceName, xdsServiceDiscoveryListener); + Assert.assertEquals(1, XdsDataCache.getServiceDiscoveryListeners(serviceName).size()); + Assert.assertEquals(0, xdsServiceDiscoveryListener.getCount()); + + // service instance in cache + Set instances = new HashSet<>(); + instances.add(new XdsServiceInstance()); + XdsDataCache.updateServiceInstance(serviceName, instances); + xdsServiceDiscovery.subscribeServiceInstance(serviceName, xdsServiceDiscoveryListener); + Assert.assertEquals(1, xdsServiceDiscoveryListener.getCount()); + } +} \ No newline at end of file diff --git a/sermant-agentcore/sermant-agentcore-implement/src/test/java/io/sermant/implement/service/xds/handler/CdsXdsTest.java b/sermant-agentcore/sermant-agentcore-implement/src/test/java/io/sermant/implement/service/xds/handler/CdsXdsTest.java new file mode 100644 index 0000000000..c761e6a4f4 --- /dev/null +++ b/sermant-agentcore/sermant-agentcore-implement/src/test/java/io/sermant/implement/service/xds/handler/CdsXdsTest.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2024-2024 Sermant Authors. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.sermant.implement.service.xds.handler; + +import com.google.protobuf.Any; + +import io.envoyproxy.envoy.config.cluster.v3.Cluster; +import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse; +import io.sermant.implement.service.xds.BaseXdsTest; +import io.sermant.implement.service.xds.cache.XdsDataCache; +import io.sermant.implement.service.xds.env.XdsConstant; + +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.mockito.Mockito; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Set; + +/** + * @author daizhenyu + * @since 2024-05-24 + **/ +public class CdsXdsTest extends BaseXdsTest { + private static CdsHandler handler; + + private static String serviceName = "serviceA"; + + @BeforeClass + public static void setUp() { + handler = new CdsHandler(client); + Mockito.doReturn(requestStreamObserver).when(client).getDiscoveryRequestObserver(handler + .getResponseStreamObserver(XdsConstant.CDS_ALL_RESOURCE, null)); + + handler.subscribe(XdsConstant.CDS_ALL_RESOURCE, null); + XdsDataCache.updateRequestObserver(serviceName, requestStreamObserver); + } + + @AfterClass + public static void tearDown() { + Mockito.clearAllCaches(); + XdsDataCache.removeRequestObserver(serviceName); + } + + @Test + public void testHandleResponse() { + // cluster is empty + handler.handleResponse(XdsConstant.CDS_ALL_RESOURCE, + buildDiscoveryResponse(new ArrayList<>())); + Assert.assertEquals(0, XdsDataCache.getServiceNameMapping().size()); + + // service with one cluster + handler.handleResponse(XdsConstant.CDS_ALL_RESOURCE, + buildDiscoveryResponse(Arrays.asList("outbound|8080||serviceA.default.svc.cluster.local"))); + Set clusterNames = XdsDataCache.getClustersByServiceName(serviceName); + Assert.assertNotNull(clusterNames); + Assert.assertEquals(1, clusterNames.size()); + Assert.assertTrue(clusterNames.contains("outbound|8080||serviceA.default.svc.cluster.local")); + + // service with many cluster + handler.handleResponse(XdsConstant.CDS_ALL_RESOURCE, + buildDiscoveryResponse(Arrays.asList( + "outbound|8080|subset1|serviceA.default.svc.cluster.local", + "outbound|8080|subset2|serviceA.default.svc.cluster.local" + ))); + clusterNames = XdsDataCache.getClustersByServiceName(serviceName); + Assert.assertEquals(2, clusterNames.size()); + Assert.assertTrue(clusterNames.contains("outbound|8080|subset1|serviceA.default.svc.cluster.local")); + Assert.assertTrue(clusterNames.contains("outbound|8080|subset2|serviceA.default.svc.cluster.local")); + } + + private DiscoveryResponse buildDiscoveryResponse(List clusterNames) { + List resources = new ArrayList<>(); + for (String clusterName : clusterNames) { + Cluster cluster = Cluster.newBuilder().setName(clusterName).build(); + resources.add(Any.pack(cluster)); + } + return DiscoveryResponse.newBuilder().addAllResources(resources).build(); + } +} \ No newline at end of file diff --git a/sermant-agentcore/sermant-agentcore-implement/src/test/java/io/sermant/implement/service/xds/handler/EdsXdsTest.java b/sermant-agentcore/sermant-agentcore-implement/src/test/java/io/sermant/implement/service/xds/handler/EdsXdsTest.java new file mode 100644 index 0000000000..a41c9215a3 --- /dev/null +++ b/sermant-agentcore/sermant-agentcore-implement/src/test/java/io/sermant/implement/service/xds/handler/EdsXdsTest.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2024-2024 Sermant Authors. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.sermant.implement.service.xds.handler; + +import com.google.protobuf.Any; + +import io.envoyproxy.envoy.config.core.v3.Address; +import io.envoyproxy.envoy.config.core.v3.SocketAddress; +import io.envoyproxy.envoy.config.endpoint.v3.ClusterLoadAssignment; +import io.envoyproxy.envoy.config.endpoint.v3.Endpoint; +import io.envoyproxy.envoy.config.endpoint.v3.LbEndpoint; +import io.envoyproxy.envoy.config.endpoint.v3.LocalityLbEndpoints; +import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse; +import io.sermant.core.service.xds.entity.ServiceInstance; +import io.sermant.core.service.xds.listener.XdsServiceDiscoveryListener; +import io.sermant.implement.service.xds.BaseXdsTest; +import io.sermant.implement.service.xds.cache.XdsDataCache; +import io.sermant.implement.service.xds.env.XdsConstant; + +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.mockito.Mockito; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +/** + * EdsHandler UT + * + * @author daizhenyu + * @since 2024-05-25 + **/ +public class EdsXdsTest extends BaseXdsTest { + private static String serviceName = "serviceA"; + + private String clusterName = "outbound|8080||serviceA.default.svc.cluster.local"; + + private static EdsHandler handler; + + @BeforeClass + public static void setUp() throws Exception { + handler = new EdsHandler(client); + Mockito.doReturn(requestStreamObserver).when(client) + .getDiscoveryRequestObserver(handler.getResponseStreamObserver(serviceName, null)); + + handler.subscribe(serviceName, null); + XdsDataCache.addServiceDiscoveryListener(serviceName, new XdsServiceDiscoveryListenerImpl()); + } + + @AfterClass + public static void tearDown() throws Exception { + Mockito.clearAllCaches(); + XdsDataCache.removeRequestObserver(serviceName); + XdsDataCache.removeServiceDiscoveryListeners(serviceName); + } + + @Test + public void testHandleResponse() { + // first update service instance of serviceA + handler.handleResponse(serviceName, buildDiscoveryResponse("127.0.0.1", 8080)); + assertHandleResponseLogic(1, 1, "127.0.0.1", 8080); + + // second update service instance of serviceA, service instance not changed + handler.handleResponse(serviceName, buildDiscoveryResponse("127.0.0.1", 8080)); + assertHandleResponseLogic(1, 1, "127.0.0.1", 8080); + + // third update service instance of serviceA, service instance changed + handler.handleResponse(serviceName, buildDiscoveryResponse("127.0.0.1", 8082)); + assertHandleResponseLogic(1, 2, "127.0.0.1", 8082); + } + + private void assertHandleResponseLogic(int expectedInstanceSize, int expectedListenerStatus, String expectedIp, + int expectedPort) { + Set serviceInstances = XdsDataCache.getServiceInstance(serviceName); + List listeners = XdsDataCache.getServiceDiscoveryListeners(serviceName); + XdsServiceDiscoveryListenerImpl listenerImpl = (XdsServiceDiscoveryListenerImpl) listeners.get(0); + Assert.assertEquals(expectedInstanceSize, serviceInstances.size()); + Assert.assertEquals(expectedListenerStatus, listenerImpl.getCount()); + Iterator iterator = serviceInstances.iterator(); + if (iterator.hasNext()) { + ServiceInstance next = iterator.next(); + Assert.assertEquals(expectedIp, next.getHost()); + Assert.assertEquals(expectedPort, next.getPort()); + } + } + + private DiscoveryResponse buildDiscoveryResponse(String ip, int port) { + List resources = new ArrayList<>(); + resources.add(Any.pack(createLoadAssignment(clusterName, ip, port))); + return DiscoveryResponse.newBuilder().addAllResources(resources).build(); + } + + private ClusterLoadAssignment createLoadAssignment(String clusterName, String ip, int port) { + ClusterLoadAssignment.Builder assignmentBuilder = ClusterLoadAssignment.newBuilder(); + LocalityLbEndpoints.Builder localityBuilder = LocalityLbEndpoints.newBuilder(); + LbEndpoint.Builder lbEndpointBuilder = LbEndpoint.newBuilder(); + Endpoint.Builder endpointBuilder = Endpoint.newBuilder(); + Address.Builder addressBuilder = Address.newBuilder(); + SocketAddress.Builder sockAddressBuilder = SocketAddress.newBuilder(); + + sockAddressBuilder.setAddress(ip); + sockAddressBuilder.setPortValue(port); + addressBuilder.setSocketAddress(sockAddressBuilder.build()); + endpointBuilder.setAddress(addressBuilder.build()); + lbEndpointBuilder.setEndpoint(endpointBuilder.build()); + localityBuilder.addLbEndpoints(lbEndpointBuilder.build()); + assignmentBuilder.setClusterName(clusterName); + assignmentBuilder.addEndpoints(localityBuilder.build()); + + return assignmentBuilder.build(); + } +} \ No newline at end of file diff --git a/sermant-agentcore/sermant-agentcore-implement/src/test/java/io/sermant/implement/service/xds/handler/StreamObserverRequestImpl.java b/sermant-agentcore/sermant-agentcore-implement/src/test/java/io/sermant/implement/service/xds/handler/StreamObserverRequestImpl.java new file mode 100644 index 0000000000..c502920876 --- /dev/null +++ b/sermant-agentcore/sermant-agentcore-implement/src/test/java/io/sermant/implement/service/xds/handler/StreamObserverRequestImpl.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2024-2024 Sermant Authors. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.sermant.implement.service.xds.handler; + +import io.envoyproxy.envoy.service.discovery.v3.DiscoveryRequest; +import io.grpc.stub.StreamObserver; + +/** + * StreamObserver impl for test + * + * @author daizhenyu + * @since 2024-05-24 + **/ +public class StreamObserverRequestImpl implements StreamObserver { + @Override + public void onNext(DiscoveryRequest value) { + } + + @Override + public void onError(Throwable t) { + } + + @Override + public void onCompleted() { + } +} diff --git a/sermant-agentcore/sermant-agentcore-implement/src/test/java/io/sermant/implement/service/xds/handler/XdsServiceDiscoveryListenerImpl.java b/sermant-agentcore/sermant-agentcore-implement/src/test/java/io/sermant/implement/service/xds/handler/XdsServiceDiscoveryListenerImpl.java new file mode 100644 index 0000000000..74f510a2dc --- /dev/null +++ b/sermant-agentcore/sermant-agentcore-implement/src/test/java/io/sermant/implement/service/xds/handler/XdsServiceDiscoveryListenerImpl.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2024-2024 Sermant Authors. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.sermant.implement.service.xds.handler; + +import io.sermant.core.service.xds.entity.ServiceInstance; +import io.sermant.core.service.xds.listener.XdsServiceDiscoveryListener; + +import java.util.Set; + +/** + * XdsServiceDiscoveryListener impl for test + * + * @author daizhenyu + * @since 2024-05-25 + **/ +public class XdsServiceDiscoveryListenerImpl implements XdsServiceDiscoveryListener { + private int count = 0; + + @Override + public void process(Set instances) { + count++; + } + + public int getCount() { + return count; + } + + public void setCount(int count) { + this.count = count; + } +}