From e0636ecfb30058b28f3a1434bcff9c7b92d60340 Mon Sep 17 00:00:00 2001 From: Steve Rao Date: Tue, 10 Jan 2023 15:16:46 +0800 Subject: [PATCH] Merge microservices governance to main branch (#3066) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feature:microservices governance modules --- pom.xml | 9 +- spring-cloud-alibaba-dependencies/pom.xml | 21 +- .../src/main/asciidoc-zh/governance.adoc | 123 +++++ .../src/main/asciidoc-zh/pic/auth-process.png | Bin 0 -> 147681 bytes .../asciidoc-zh/pic/governance-module.png | Bin 0 -> 67624 bytes .../asciidoc-zh/pic/resource-transform.png | Bin 0 -> 159203 bytes .../src/main/asciidoc/governance.adoc | 111 +++++ .../src/main/asciidoc/pic/auth-process.png | Bin 0 -> 169097 bytes .../main/asciidoc/pic/governance-module.png | Bin 0 -> 70731 bytes .../main/asciidoc/pic/resource-transform.png | Bin 0 -> 159203 bytes .../pom.xml | 38 ++ .../cloud/examples/AuthApplication.java | 29 ++ .../cloud/examples/AuthMvcController.java | 38 ++ .../src/main/resources/application.yml | 15 + .../pom.xml | 46 ++ .../cloud/examples/AuthWebFluxController.java | 40 ++ .../examples/ScaAuthReactiveApplication.java | 29 ++ .../src/main/resources/application.yml | 15 + .../authentication-example/readme-zh.md | 207 ++++++++ .../authentication-example/readme.md | 209 +++++++++ .../consumer-example/pom.xml | 40 ++ .../cloud/examples/ConsumerApplication.java | 178 +++++++ .../src/main/resources/application.properties | 8 + .../default-provider-version-example/pom.xml | 36 ++ .../cloud/examples/ProviderApplication.java | 55 +++ .../src/main/resources/application.properties | 12 + .../istio-consumer-example/pom.xml | 45 ++ .../examples/IstioConsumerApplication.java | 65 +++ .../src/main/resources/application.yml | 25 + .../opensergo-consumer-example/pom.xml | 45 ++ .../OpenSergoConsumerApplication.java | 65 +++ .../src/main/resources/application.properties | 8 + .../provider-version-example/pom.xml | 36 ++ .../cloud/examples/ProviderApplication.java | 55 +++ .../src/main/resources/application.properties | 11 + .../label-routing-example/readme-zh.md | 440 ++++++++++++++++++ .../label-routing-example/readme.md | 355 ++++++++++++++ spring-cloud-alibaba-examples/pom.xml | 8 +- spring-cloud-alibaba-starters/pom.xml | 4 + .../spring-cloud-alibaba-commons/pom.xml | 12 + .../auth/condition/AuthCondition.java | 80 ++++ .../governance/auth/rule/AuthRule.java | 112 +++++ .../governance/auth/rule/AuthRules.java | 52 +++ .../commons/governance/auth/rule/JwtRule.java | 89 ++++ .../event/AuthDataChangedEvent.java | 41 ++ .../governance/event/GovernanceEvent.java | 36 ++ .../event/LabelRoutingDataChangedEvent.java | 44 ++ .../event/TargetServiceChangedEvent.java | 27 ++ .../labelrouting/LabelRouteRule.java | 71 +++ .../governance/labelrouting/MatchService.java | 95 ++++ .../UnifiedRouteDataStructure.java | 50 ++ .../labelrouting/rule/HeaderRule.java | 100 ++++ .../labelrouting/rule/RouteRule.java | 44 ++ .../governance/labelrouting/rule/UrlRule.java | 179 +++++++ .../cloud/commons/matcher/IpMatcher.java | 108 +++++ .../cloud/commons/matcher/Matcher.java | 27 ++ .../cloud/commons/matcher/PortMatcher.java | 47 ++ .../cloud/commons/matcher/StringMatcher.java | 119 +++++ .../commons/matcher/StringMatcherType.java | 64 +++ .../pom.xml | 94 ++++ .../cloud/governance/istio/NodeBuilder.java | 81 ++++ .../governance/istio/PilotExchanger.java | 100 ++++ .../istio/XdsAutoConfiguration.java | 141 ++++++ .../cloud/governance/istio/XdsChannel.java | 135 ++++++ .../governance/istio/XdsConfigProperties.java | 120 +++++ .../istio/XdsScheduledThreadPool.java | 35 ++ .../istio/constant/IstioConstants.java | 99 ++++ .../filter/AbstractXdsResolveFilter.java | 72 +++ .../istio/filter/XdsResolveFilter.java | 29 ++ .../filter/impl/AuthXdsResolveFilter.java | 377 +++++++++++++++ .../impl/LabelRoutingXdsResolveFilter.java | 176 +++++++ .../istio/protocol/AbstractXdsProtocol.java | 301 ++++++++++++ .../istio/protocol/XdsProtocol.java | 35 ++ .../istio/protocol/impl/CdsProtocol.java | 79 ++++ .../istio/protocol/impl/EdsProtocol.java | 63 +++ .../istio/protocol/impl/LdsProtocol.java | 105 +++++ .../istio/protocol/impl/RdsProtocol.java | 70 +++ .../cloud/governance/istio/util/ConvUtil.java | 156 +++++++ .../main/resources/META-INF/spring.factories | 2 + .../cloud/governance/istio/XdsRulesTests.java | 143 ++++++ .../src/test/resources/LdsResponse.in | Bin 0 -> 128649 bytes .../src/test/resources/RdsResponse.in | Bin 0 -> 18190 bytes .../pom.xml | 65 +++ .../opensergo/OpenSergoAutoConfig.java | 64 +++ .../opensergo/OpenSergoConfigProperties.java | 59 +++ .../opensergo/OpenSergoTrafficExchanger.java | 109 +++++ .../OpenSergoTrafficRouterParser.java | 253 ++++++++++ .../TargetServiceChangedListener.java | 53 +++ .../governance/opensergo/util/ConvUtils.java | 132 ++++++ .../main/resources/META-INF/spring.factories | 2 + .../opensergo/OpenSergoRuleTests.java | 72 +++ .../pom.xml | 64 +++ .../auth/AuthValidatorAutoConfiguration.java | 39 ++ .../auth/AuthenticationAutoConfiguration.java | 54 +++ .../auth/XdsWebAutoConfiguration.java | 53 +++ .../auth/XdsWebFluxAutoConfiguration.java | 46 ++ .../governance/auth/XdsWebMvcConfigurer.java | 44 ++ .../auth/listener/AuthListener.java | 52 +++ .../auth/repository/AuthRepository.java | 73 +++ .../cloud/governance/auth/util/IpUtil.java | 73 +++ .../cloud/governance/auth/util/JwtUtil.java | 147 ++++++ .../auth/validator/AuthValidator.java | 366 +++++++++++++++ .../auth/webflux/AuthWebFluxFilter.java | 90 ++++ .../auth/webmvc/AuthWebInterceptor.java | 114 +++++ .../main/resources/META-INF/spring.factories | 5 + .../governance/auth/AuthenticationTest.java | 145 ++++++ .../src/test/resources/from-ip-allow.json | 47 ++ .../src/test/resources/method-allow.json | 68 +++ .../pom.xml | 52 +++ .../router/LabelRoutingAutoConfiguration.java | 55 +++ .../cloud/router/RouterProperties.java | 45 ++ .../RouterPropertiesAutoConfiguration.java | 44 ++ .../cloud/router/feign/FeignInterceptor.java | 49 ++ .../FeignInterceptorAutoConfiguration.java | 35 ++ .../listener/LabelRouteDataListener.java | 76 +++ .../TargetServiceChangedPublisher.java | 48 ++ .../router/repository/FilterService.java | 81 ++++ .../repository/RouteDataRepository.java | 167 +++++++ .../cloud/router/ribbon/LabelRouteLBRule.java | 405 ++++++++++++++++ .../LabelRouteLBRuleAutoConfiguration.java | 35 ++ .../cloud/router/util/ConditionMatchUtil.java | 94 ++++ .../cloud/router/util/LoadBalanceUtil.java | 101 ++++ .../cloud/router/util/RequestContext.java | 48 ++ .../router/web/WebMvcAutoConfiguration.java | 41 ++ .../cloud/router/web/WebMvcConfig.java | 36 ++ .../cloud/router/web/WebMvcInterceptor.java | 45 ++ .../main/resources/META-INF/spring.factories | 6 + 127 files changed, 9980 insertions(+), 3 deletions(-) create mode 100644 spring-cloud-alibaba-docs/src/main/asciidoc-zh/governance.adoc create mode 100644 spring-cloud-alibaba-docs/src/main/asciidoc-zh/pic/auth-process.png create mode 100644 spring-cloud-alibaba-docs/src/main/asciidoc-zh/pic/governance-module.png create mode 100644 spring-cloud-alibaba-docs/src/main/asciidoc-zh/pic/resource-transform.png create mode 100644 spring-cloud-alibaba-docs/src/main/asciidoc/governance.adoc create mode 100644 spring-cloud-alibaba-docs/src/main/asciidoc/pic/auth-process.png create mode 100644 spring-cloud-alibaba-docs/src/main/asciidoc/pic/governance-module.png create mode 100644 spring-cloud-alibaba-docs/src/main/asciidoc/pic/resource-transform.png create mode 100644 spring-cloud-alibaba-examples/governance-example/authentication-example/istio-authentication-provider-mvc-example/pom.xml create mode 100644 spring-cloud-alibaba-examples/governance-example/authentication-example/istio-authentication-provider-mvc-example/src/main/java/com/alibaba/cloud/examples/AuthApplication.java create mode 100644 spring-cloud-alibaba-examples/governance-example/authentication-example/istio-authentication-provider-mvc-example/src/main/java/com/alibaba/cloud/examples/AuthMvcController.java create mode 100644 spring-cloud-alibaba-examples/governance-example/authentication-example/istio-authentication-provider-mvc-example/src/main/resources/application.yml create mode 100644 spring-cloud-alibaba-examples/governance-example/authentication-example/istio-authentication-provider-webflux-example/pom.xml create mode 100644 spring-cloud-alibaba-examples/governance-example/authentication-example/istio-authentication-provider-webflux-example/src/main/java/com/alibaba/cloud/examples/AuthWebFluxController.java create mode 100644 spring-cloud-alibaba-examples/governance-example/authentication-example/istio-authentication-provider-webflux-example/src/main/java/com/alibaba/cloud/examples/ScaAuthReactiveApplication.java create mode 100644 spring-cloud-alibaba-examples/governance-example/authentication-example/istio-authentication-provider-webflux-example/src/main/resources/application.yml create mode 100644 spring-cloud-alibaba-examples/governance-example/authentication-example/readme-zh.md create mode 100644 spring-cloud-alibaba-examples/governance-example/authentication-example/readme.md create mode 100644 spring-cloud-alibaba-examples/governance-example/label-routing-example/consumer-example/pom.xml create mode 100644 spring-cloud-alibaba-examples/governance-example/label-routing-example/consumer-example/src/main/java/com/alibaba/cloud/examples/ConsumerApplication.java create mode 100644 spring-cloud-alibaba-examples/governance-example/label-routing-example/consumer-example/src/main/resources/application.properties create mode 100644 spring-cloud-alibaba-examples/governance-example/label-routing-example/default-provider-version-example/pom.xml create mode 100644 spring-cloud-alibaba-examples/governance-example/label-routing-example/default-provider-version-example/src/main/java/com/alibaba/cloud/examples/ProviderApplication.java create mode 100644 spring-cloud-alibaba-examples/governance-example/label-routing-example/default-provider-version-example/src/main/resources/application.properties create mode 100644 spring-cloud-alibaba-examples/governance-example/label-routing-example/istio-consumer-example/pom.xml create mode 100644 spring-cloud-alibaba-examples/governance-example/label-routing-example/istio-consumer-example/src/main/java/com/alibaba/cloud/examples/IstioConsumerApplication.java create mode 100644 spring-cloud-alibaba-examples/governance-example/label-routing-example/istio-consumer-example/src/main/resources/application.yml create mode 100644 spring-cloud-alibaba-examples/governance-example/label-routing-example/opensergo-consumer-example/pom.xml create mode 100644 spring-cloud-alibaba-examples/governance-example/label-routing-example/opensergo-consumer-example/src/main/java/com/alibaba/cloud/examples/OpenSergoConsumerApplication.java create mode 100644 spring-cloud-alibaba-examples/governance-example/label-routing-example/opensergo-consumer-example/src/main/resources/application.properties create mode 100644 spring-cloud-alibaba-examples/governance-example/label-routing-example/provider-version-example/pom.xml create mode 100644 spring-cloud-alibaba-examples/governance-example/label-routing-example/provider-version-example/src/main/java/com/alibaba/cloud/examples/ProviderApplication.java create mode 100644 spring-cloud-alibaba-examples/governance-example/label-routing-example/provider-version-example/src/main/resources/application.properties create mode 100644 spring-cloud-alibaba-examples/governance-example/label-routing-example/readme-zh.md create mode 100644 spring-cloud-alibaba-examples/governance-example/label-routing-example/readme.md create mode 100644 spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/auth/condition/AuthCondition.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/auth/rule/AuthRule.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/auth/rule/AuthRules.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/auth/rule/JwtRule.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/event/AuthDataChangedEvent.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/event/GovernanceEvent.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/event/LabelRoutingDataChangedEvent.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/event/TargetServiceChangedEvent.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/labelrouting/LabelRouteRule.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/labelrouting/MatchService.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/labelrouting/UnifiedRouteDataStructure.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/labelrouting/rule/HeaderRule.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/labelrouting/rule/RouteRule.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/labelrouting/rule/UrlRule.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/matcher/IpMatcher.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/matcher/Matcher.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/matcher/PortMatcher.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/matcher/StringMatcher.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/matcher/StringMatcherType.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/pom.xml create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/NodeBuilder.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/PilotExchanger.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/XdsAutoConfiguration.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/XdsChannel.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/XdsConfigProperties.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/XdsScheduledThreadPool.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/constant/IstioConstants.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/filter/AbstractXdsResolveFilter.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/filter/XdsResolveFilter.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/filter/impl/AuthXdsResolveFilter.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/filter/impl/LabelRoutingXdsResolveFilter.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/protocol/AbstractXdsProtocol.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/protocol/XdsProtocol.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/protocol/impl/CdsProtocol.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/protocol/impl/EdsProtocol.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/protocol/impl/LdsProtocol.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/protocol/impl/RdsProtocol.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/util/ConvUtil.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/resources/META-INF/spring.factories create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/test/java/com/alibaba/cloud/governance/istio/XdsRulesTests.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/test/resources/LdsResponse.in create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/test/resources/RdsResponse.in create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-opensergo/pom.xml create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-opensergo/src/main/java/com/alibaba/cloud/governance/opensergo/OpenSergoAutoConfig.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-opensergo/src/main/java/com/alibaba/cloud/governance/opensergo/OpenSergoConfigProperties.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-opensergo/src/main/java/com/alibaba/cloud/governance/opensergo/OpenSergoTrafficExchanger.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-opensergo/src/main/java/com/alibaba/cloud/governance/opensergo/OpenSergoTrafficRouterParser.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-opensergo/src/main/java/com/alibaba/cloud/governance/opensergo/TargetServiceChangedListener.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-opensergo/src/main/java/com/alibaba/cloud/governance/opensergo/util/ConvUtils.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-opensergo/src/main/resources/META-INF/spring.factories create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-opensergo/src/test/java/com/alibaba/cloud/governance/opensergo/OpenSergoRuleTests.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/pom.xml create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/java/com/alibaba/cloud/governance/auth/AuthValidatorAutoConfiguration.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/java/com/alibaba/cloud/governance/auth/AuthenticationAutoConfiguration.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/java/com/alibaba/cloud/governance/auth/XdsWebAutoConfiguration.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/java/com/alibaba/cloud/governance/auth/XdsWebFluxAutoConfiguration.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/java/com/alibaba/cloud/governance/auth/XdsWebMvcConfigurer.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/java/com/alibaba/cloud/governance/auth/listener/AuthListener.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/java/com/alibaba/cloud/governance/auth/repository/AuthRepository.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/java/com/alibaba/cloud/governance/auth/util/IpUtil.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/java/com/alibaba/cloud/governance/auth/util/JwtUtil.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/java/com/alibaba/cloud/governance/auth/validator/AuthValidator.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/java/com/alibaba/cloud/governance/auth/webflux/AuthWebFluxFilter.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/java/com/alibaba/cloud/governance/auth/webmvc/AuthWebInterceptor.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/resources/META-INF/spring.factories create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/test/java/com/alibaba/cloud/governance/auth/AuthenticationTest.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/test/resources/from-ip-allow.json create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/test/resources/method-allow.json create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/pom.xml create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/LabelRoutingAutoConfiguration.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/RouterProperties.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/RouterPropertiesAutoConfiguration.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/feign/FeignInterceptor.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/feign/FeignInterceptorAutoConfiguration.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/listener/LabelRouteDataListener.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/publish/TargetServiceChangedPublisher.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/repository/FilterService.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/repository/RouteDataRepository.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/ribbon/LabelRouteLBRule.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/ribbon/LabelRouteLBRuleAutoConfiguration.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/util/ConditionMatchUtil.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/util/LoadBalanceUtil.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/util/RequestContext.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/web/WebMvcAutoConfiguration.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/web/WebMvcConfig.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/web/WebMvcInterceptor.java create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/resources/META-INF/spring.factories diff --git a/pom.xml b/pom.xml index ed93b5e6b..5803fc347 100644 --- a/pom.xml +++ b/pom.xml @@ -100,6 +100,12 @@ 1.1.0 1.6 0.8.3 + + 1.0.36 + + 0.8.0 + + 0.1.0-beta2 @@ -108,7 +114,7 @@ spring-cloud-alibaba-docs spring-cloud-alibaba-starters spring-cloud-alibaba-coverage - + @@ -148,6 +154,7 @@ rocketmq-acl ${rocketmq.version} + diff --git a/spring-cloud-alibaba-dependencies/pom.xml b/spring-cloud-alibaba-dependencies/pom.xml index d0e305854..58ef85883 100644 --- a/spring-cloud-alibaba-dependencies/pom.xml +++ b/spring-cloud-alibaba-dependencies/pom.xml @@ -231,7 +231,26 @@ spring-cloud-starter-alibaba-appactive ${revision} - + + com.alibaba.cloud + spring-cloud-starter-alibaba-controlplane-istio + ${revision} + + + com.alibaba.cloud + spring-cloud-starter-alibaba-controlplane-opensergo + ${revision} + + + com.alibaba.cloud + spring-cloud-starter-alibaba-governance-routing + ${revision} + + + com.alibaba.cloud + spring-cloud-starter-alibaba-governance-auth + ${revision} + com.alibaba.cloud spring-cloud-alibaba-commons diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/governance.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/governance.adoc new file mode 100644 index 000000000..0c7416d20 --- /dev/null +++ b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/governance.adoc @@ -0,0 +1,123 @@ +== Spring Cloud Alibaba Governance + +image::pic/governance-module.png[] + +Spring Cloud Alibaba Governance模块是Spring Cloud Alibaba推出的微服务治理子模块,提供了多种类型的微服务治理能力,包括标签路由,服务鉴权等。并且对接了多种控制面,比如Istio,OpenSergo,让用户无需改造Spring Cloud应用,也能实时感知到Istio等治理控制面下发的治理规则,并将此规则应用到Spring Cloud应用上,从而完成对Spring Cloud应用的治理。 + +== 如何使用 +=== 配置转换 +image::pic/resource-transform.png[] + +Spring Cloud Alibaba Governance的resource-transform模块会将不同控制面下发的配置进行统一的转换,将来自Istio,OpenSergo等控制面下发的配置统一转换为Spring Cloud Alibaba统一抽象出的数据结构以供后续使用 + +如果在您的项目中使用Istio来实现配置转换,需要使用group ID 为 `com.alibaba.cloud` 和artifact ID 为 `spring-cloud-starter-alibaba-controlplane-istio` 的starter +[source,xml,indent=0] +---- + + com.alibaba.cloud + spring-cloud-starter-alibaba-controlplane-istio + +---- + +之后,在application.yml配置文件中配置如下配置 + +[source,yaml,indent=0] +---- +server: + port: ${SERVER_PORT:80} +spring: + cloud: + governance: + auth: + enabled: ${ISTIO_AUTH_ENABLE:true} + istio: + config: + enabled: ${ISTIO_CONFIG_ENABLE:true} + host: ${ISTIOD_ADDR:127.0.0.1} + port: ${ISTIOD_PORT:15010} + polling-pool-size: ${POLLING_POOL_SIZE:10} + polling-time: ${POLLING_TIMEOUT:10} + istiod-token: ${ISTIOD_TOKEN:} + log-xds: ${LOG_XDS:true} +---- + +各字段的含义如下: +|=== +|配置项|key|默认值|说明 +|是否开启鉴权| spring.cloud.governance.auth.enabled|true| +|是否连接Istio获取鉴权配置| spring.cloud.istio.config.enabled|true| +|Istiod的地址| spring.cloud.istio.config.host|127.0.0.1| +|Istiod的端口| spring.cloud.istio.config.port|15012|注:连接15010端口无需TLS,连接15012端口需TLS认证 +|SCA去Istio拉取配置的线程池大小| spring.cloud.istio.config.polling-pool-size|10| +|SCA去Istio拉取配置的间隔时间| spring.cloud.istio.config.polling-time|30|单位为秒 +|连接Istio 15012端口时使用的JWT token| spring.cloud.istio.config.istiod-token|应用所在pod的 `/var/run/secrets/tokens/istio-token` 文件的内容| +|是否打印xDS相关日志| spring.cloud.istio.config.log-xds|true| +|=== + +=== 运行应用 +需要将应用运行在K8s环境中,并给运行的应用将K8s的一些元信息注入以下环境变量中: + +|=== +|环境变量名|K8s pod metadata name +|POD_NAME|metadata.name +|NAMESPACE_NAME|metadata.namespace +|=== + +=== 使用标签路由 +在引入配置转换模块后,我们就能获取到相应的治理规则来对Spring Cloud应用赋予相应的治理能力。标签路由模块可以实现对Spring Cloud应用根据请求头,请求参数等标签来路由到不同的服务 + +如果在您的项目中使用Spring Cloud Alibaba Governance 标签路由,需要使用需要使用group ID 为 `com.alibaba.cloud` 和artifact ID 为 `spring-cloud-starter-alibaba-governance-routing` 的starter + +=== 运行应用 +需要将应用运行在K8s环境中,并给运行的应用将K8s的一些元信息注入以下环境变量中: + +|=== +|环境变量名|K8s pod metadata name +|POD_NAME|metadata.name +|NAMESPACE_NAME|metadata.namespace +|=== + +=== 使用标签路由 +在引入配置转换模块后,我们就能获取到相应的治理规则来对Spring Cloud应用赋予相应的治理能力。标签路由模块可以实现对Spring Cloud应用根据请求头,请求参数等标签来路由到不同的服务 + +如果在您的项目中使用Spring Cloud Alibaba Governance 标签路由,需要使用需要使用group ID 为 `com.alibaba.cloud` 和artifact ID 为 `spring-cloud-starter-alibaba-governance-routing` 的starter +[source,xml,indent=0] +---- + + com.alibaba.cloud + spring-cloud-starter-alibaba-governance-routing + +---- + +在引入Istio配置转换模块的前提下,标签路由模块支持对以下几种请求的元信息做路由 + +* 请求路径 +* 请求头 +* 请求参数 + +我们使用Istio下发对应的 `DestinationRule` 以及 `VirtualService` ,即可配置对应的标签路由规则,具体的配置方法请参考以下文档与示例 + +* https://istio.io/latest/zh/docs/reference/config/networking/virtual-service/#VirtualService +* https://istio.io/latest/zh/docs/concepts/traffic-management/#destination-rules +* spring-cloud-alibaba-examples/governance-example/label-routing-example/istio-label-routing-consumer-example + +=== 使用服务鉴权 +image::pic/auth-process.png[] + +在引入配置转换模块后,我们就能获取到相应的治理规则来对Spring Cloud应用赋予相应的治理能力。服务鉴权模块给Spring Cloud应用提供多种鉴权方式,如IP黑白名单,JWT鉴权等 + +如果在您的项目中使用Spring Cloud Alibaba Governance 标签路由,需要使用需要使用group ID 为 `com.alibaba.cloud` 和artifact ID 为 `spring-cloud-starter-alibaba-governance-auth` 的starter +[source,xml,indent=0] +---- + + com.alibaba.cloud + spring-cloud-starter-alibaba-governance-auth + +---- + +我们使用Istio下发对应的 `AuthorizationPolicy` 以及 `RequestAuthentication` ,即可配置对应的鉴权规则,具体的配置方法请参考以下文档与示例 + +* https://istio.io/latest/zh/docs/reference/config/security/request_authentication/ +* https://istio.io/latest/zh/docs/reference/config/security/authorization-policy/ +* spring-cloud-alibaba-examples/governance-example/authentication-example/istio-authentication-provider-mvc-example +* spring-cloud-alibaba-examples/governance-example/authentication-example/istio-authentication-provider-webflux-example \ No newline at end of file diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/pic/auth-process.png b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/pic/auth-process.png new file mode 100644 index 0000000000000000000000000000000000000000..9e286b3560924545a99bad48b2ad69e0e595518b GIT binary patch literal 147681 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 DktQXS literal 0 HcmV?d00001 diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/pic/governance-module.png b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/pic/governance-module.png new file mode 100644 index 0000000000000000000000000000000000000000..3e981bd30b0f5d6d21272094a9a70781c6598714 GIT binary patch literal 67624 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 z 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

c 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 literal 0 HcmV?d00001 diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/pic/resource-transform.png b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/pic/resource-transform.png new file mode 100644 index 0000000000000000000000000000000000000000..3865e945876143ad60b1489415562febafda2859 GIT binary patch literal 159203 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%0Ds82`9#waV=|b7DU&bWbH6|#5chs2!<^7 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Û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 literal 0 HcmV?d00001 diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc/governance.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc/governance.adoc new file mode 100644 index 000000000..48748bac3 --- /dev/null +++ b/spring-cloud-alibaba-docs/src/main/asciidoc/governance.adoc @@ -0,0 +1,111 @@ +== Spring Cloud Alibaba Governance + +image::pic/governance-module.png[] + +Spring Cloud Alibaba Governance module is a micro-service governance sub-module launched by Spring Cloud Alibaba, which provides various types of micro-service governance capabilities, including label routing, service authentication, etc. Moreover, it supports various control planes, such as Istio and OpenSergo, so that users can get the governance rules in real time without modifying Spring Cloud applications, and apply these rules to Spring Cloud applications to govern the Spring Cloud application. + +== How to use + +=== Resource-Transform +image::pic/resource-transform.png[] + +The resource-transform module of Spring Cloud Alibaba Governance will uniformly transform the configurations published by different control planes, like Istio and OperSergo, into the unified abstract data structure of Spring Cloud Alibaba for subsequent use. + +If you use Istio in your project to transform the configuration, you need to use a starter with a group ID of `com.alibaba.cloud` and an artifact ID of `spring-cloud-starter-alibaba-controlplane-istio`. +[source,xml,indent=0] +---- + + com.alibaba.cloud + spring-cloud-starter-alibaba-controlplane-istio + +---- + +After that, configure the following configuration in the application.yml: + +[source,yaml,indent=0] +---- +server: + port: ${SERVER_PORT:80} +spring: + cloud: + governance: + auth: + enabled: ${ISTIO_AUTH_ENABLE:true} + istio: + config: + enabled: ${ISTIO_CONFIG_ENABLE:true} + host: ${ISTIOD_ADDR:127.0.0.1} + port: ${ISTIOD_PORT:15010} + polling-pool-size: ${POLLING_POOL_SIZE:10} + polling-time: ${POLLING_TIMEOUT:10} + istiod-token: ${ISTIOD_TOKEN:} + log-xds: ${LOG_XDS:true} +---- + +Here's an explanation of each field: +|=== +|Configuration Item|key|Default Value|Description +|Whether to enable authentication| spring.cloud.governance.auth.enabled|true| +|Whether to connect to Istio to obtain authentication configuration| spring.cloud.istio.config.enabled|true| +|Host of Istiod| spring.cloud.istio.config.host|127.0.0.1| +|Port of Istiod| spring.cloud.istio.config.port|15012|15010 port does not need TLS,but 15012 does +|Thread pool size for SCA to pull the config| spring.cloud.istio.config.polling-pool-size|10| +|Time interval for SCA to pull the config| spring.cloud.istio.config.polling-time|30|The unit is second +|JWT token for SCA to connect to 15012 port| spring.cloud.istio.config.istiod-token|Content of file `/var/run/secrets/tokens/istio-token` in the pod of application| +|Whether to print logs about xDS| spring.cloud.istio.config.log-xds|true| +|=== + +### Run the application +You need to run the application in the K8s environment and inject some meta information about K8s into the following environment variables for the running application. + +|=== +|Environment variable name|K8s pod metadata name +|POD_NAME|metadata.name +|NAMESPACE_NAME|metadata.namespace +|=== + +=== Use Label Routing +With the introduction of the configuration transformation module, we can obtain the governance rules to give the Spring Cloud application some governance capabilities. Label routing module can route the Spring Cloud application according to the request header, request parameters and other tags to route to different services. + +If you use Spring Cloud Alibaba Governance Label Routing in your project, You need to use a starter with a group ID of `com.alibaba.cloud` and an artifact ID of `spring-cloud-starter-alibaba-governance-routing`. +[source,xml,indent=0] +---- + + com.alibaba.cloud + spring-cloud-starter-alibaba-governance-routing + +---- + +With the introduction of Istio Resource Transform module, the label routing module supports routing of the following types of request meta-information: + +* Request Path +* Request Header +* Request Param + +We use Istio to publish corresponding `DestinationRule` and `VirtualService` to configure corresponding labeled routing rules. For details, see the following documents and examples: + +* https://istio.io/latest/zh/docs/reference/config/networking/virtual-service/#VirtualService +* https://istio.io/latest/zh/docs/concepts/traffic-management/#destination-rules +* spring-cloud-alibaba-examples/governance-example/label-routing-example/istio-label-routing-consumer-example + +=== Use Authentication +image::pic/auth-process.png[] + +With the introduction of the configuration transformation module, we can obtain the governance rules to give the Spring Cloud application some governance capabilities. The Authentication module provides various authentication modes for Spring Cloud applications, such as IP blacklist and whitelist and JWT authentication. + +If you use Istio in your project to transform the configuration, you need to use a starter with a group ID of `com.alibaba.cloud` and an artifact ID of `spring-cloud-starter-alibaba-governance-auth`. + +[source,xml,indent=0] +---- + + com.alibaba.cloud + spring-cloud-starter-alibaba-governance-auth + +---- + +We use Istio to publish corresponding `AuthorizationPolicy` and `RequestAuthentication` to configure corresponding Authentication rules. For details, see the following documents and examples + +* https://istio.io/latest/zh/docs/reference/config/security/request_authentication/ +* https://istio.io/latest/zh/docs/reference/config/security/authorization-policy/ +* spring-cloud-alibaba-examples/governance-example/authentication-example/istio-authentication-provider-mvc-example +* spring-cloud-alibaba-examples/governance-example/authentication-example/istio-authentication-provider-webflux-example \ No newline at end of file diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc/pic/auth-process.png b/spring-cloud-alibaba-docs/src/main/asciidoc/pic/auth-process.png new file mode 100644 index 0000000000000000000000000000000000000000..29638a551a4e7618fec5c7fdc74f3add07dad0a2 GIT binary patch literal 169097 zcmdSBdt8%OzW>|NR&*3;hutzvewT z4_~ipzpu5{^E@l-v)1?fd9SPQ$3(gQ+UwWPJoAj}@pq4Y_{=lg+QC0%J9mKpa`BH? zmuH^&%A{OfAz!_kGFc>5+!mR4wH3sR!!0g@oM2Z zN&ApK-kZbfb=)f2blF_++g!*y#S>

Z6UpY}D=UGY64S!~DRZ%}+*r$ec^(?T!j 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

pKbh%|b4LxeaRFXvZbKLI2&d@fO1fjj(Gcsik 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 KuKno5`Tqppxg7cc literal 0 HcmV?d00001 diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc/pic/resource-transform.png b/spring-cloud-alibaba-docs/src/main/asciidoc/pic/resource-transform.png new file mode 100644 index 0000000000000000000000000000000000000000..3865e945876143ad60b1489415562febafda2859 GIT binary patch literal 159203 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%0Ds82`9#waV=|b7DU&bWbH6|#5chs2!<^7 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ÛTHN%~1u8{DBn;>Tl6d<}T4SWfZ4ioA`@@uIln6ZD0!cYL(J@pyd&_cT~a6c>NvJ^z?cF#NNRhD{V#s$kB z*}I$St$Y5BYbIl>J|qs!?K->cykHx;NAFL^Rz_ zRVuklJ%_StUw{WCL?)arMK*P~8?g(!nx7~3vju5&LzafgDl(7)My5-L7z(Nr9I825 zX?nVe4^zB>7J2 zA$>*U!r_mvv}i&&2&3ypyS*^?LG^7ni?+%mRUiTl6b~SPbm35fcxG-}h)m!~5*lZH z^}psMlGRN;St`{{ffz{mv8^FgB>LGVC3cWe9I0(=J;N|$Uykt73HP%pP9r#}e|Q1) zFkOY21xB0~^te2tP>yI-n`JLZ&5ff4fwW2_D^(hD${H;93{YRwr^<%qjDmv=)>j}? zq>N2&4s{9c1FApe*eYYur!T0t|4VCcNLBSA@H+MA9!hU^MWoEtsaKgOZNE|x*b4f_ zG$i#r@p*V|(6o~ss-QIo!k%~NoRmU$W3Q!wIF%x9-=d* zGrTV^_BBeUZuHF`3&mtWDPEN0^>1`RMkrC^Fb>$WO~t9uqxUEosf z7T103o0BO|H=CbX$fz9IkCv)rJE32&pBST9imRtVsr16?%7|!-zr@R1=Tc_C-^zKJ zbc*-A?2q?#am3kvH0GBE%lwNPZ!6CoBFHCAH}Ky~7*1X%-q<-C?Kt~_NSc{fBtnDq zPn(@>2~j?0aJ$!(aSC$}wxrO9a2wU-btut@J%9{!XX}!h}iEI`C4^iyK)p-N<}iJi5;Kf~@SHE-aJP z_at?a?d1ap%GS<44?uwbw+sJ#ztjyHVfSl9??fnGR?15f9MrT@!anNER~{qdr}4ez zXisnBpobnrE!<8ypy>+m$#M6!9``6k#O_$O0n`(~TC_2FihdclyNlnEypIi ztz;~t8BAwsB)Q0T&;9{BO#$Y?I>T=TDM1vcpDhOf+%)F>TNfokU%+0n3xdEQP!Vyi zpFn$!FIsofOG=1E+csb#b^tKHLbDTB0ol%;CiT=L}E5C z`F~t9(OA?Q4Q7r42y)34YFf6=GMC1>WR~_Pv9)I-HUcs9n3Y4#X=h!U*h{OVm8ESK zQQBe!eN@DyirOBi@+Tf~?-Bm5S8QPwwJDl?lxt{56%)-rK#VG~_%>bABQU8))Pv>E znt_RB?3}(G>{kh(g|~O0xW+7T&8`WV3shqV*((31ZN&gkNj8=xW5zn8E66`sh#h(o z6Ire%6^aY2PF8K;vzFmnP#XXH*K76eaNqN{*5})j2odR$63jmPr5 z-m^jy#GP8BQ?OT8dT>V9XRC9D=UD(r+mul)cd<+d!Z}aY+I@mMD8-T4d6_!Na>ZJy z$tjeKn_`6lb>vWR-^409jZMLs^O!E2(Z*fAp>0iO?THx&&gm5IIDdkx`DGijicacg zPKkt)MkuFEyh>^s{MH3Z9Lk{_&pb&HE|bMN3pKL)bsq;wGjs05tF>_iQKIGrI6LjZ zDp_rssPT4FYl!+dT(wedBqqmP_kg$J68*@O>I!u&`2;1@I=FhJ^S&;oHM@`s%(`7o z^4vkD2uqXgb>kh7<`{W|C zSrOGZHi_sZm!jqN+`E^A8!Z8W7PrK7}*Q-sp^T8RO8(Wa%hOv zk`_R`rlNemToDS!l+bf;Am~-HJFMI6f8_EGefRHQ^&ooK7PLa0no#lnVv=K0hw& zotL+8G}&MT4|6Z52Ga>XC^vxQf*3f|fuB41u?mwhLZR32ucao>o%^?zUCA|k*2eu+ z{!6K=WcwvvmSWHsxE5h;zacPgZV0`w9+1i8Ad$J!LN+z8zN84tf<=0*8PmL)_dyT^=VpMbUf0jcK09 z8hW5NiPv0)oAd>(ia)VY`2=qSe3OsRqN<5^8nX(KxS&E>9*$j9{~x(2O3SaL=JnEi z2g?l4KtRGsJ+cgj>%_Qtuc*FRexXtzfJ5>4(G2wm=@k3I2H6~CZ z5P3Wzhp;;u4W~Q9#`iy@274ba?ugb-V((dl*Fk*6a2)YS8JQk*S93J5h%)3-oclxb zcgA#luTK%bd%(3$9o_5B(VdMzQjM(-SLc`TX;<^vZ`&$_*XNwz?WxnjT^!~%9rwMe z)<$W+zs8dG*Ou`kUT~AI&*C}iN174F)d(+QDc>(y#J?I4+~>yDs?=)%6j(AEXT3Yd zo34Ixnr@(bo_#ZwzV2kST9wmxie|?h913VX+j%g~Cg4s2Fs?q`D+DwMgkwH>DgTA; zX*Gz%-72G-u6k9{97(#d7e#@9DBHaS^0$jnw7AK1XN;)`&QpNLLtHx98E!0?uhI^iNjZK@`h$du46v zvc?q%v@vW#oia2vcxqFPNqqXv7V1#X>5^R_M>sJ5gw`L6={umNjQMiykj-nQUs@a0 zzpYUV!e-WElP7uZaYZ|}ejW&K20pH6X?_Ma}G&EDsrT&5qPtGj-aX&IN za$*<4*NPuS3Do=<;9U^h=lFJ9?F7PSg!eo+7B;l#*MzE`%usI~c%XEm17999bS1z4 zUwoDw^UKr08J9Fd4dz|cZ>?R7EHTgYuF<6~9F`p`^YbL%QK$EI$CB{i1PV@=IBLi* zSQacy2tH*sapr%~hDD3it&-x=h<=a4G8#!jEOaOLX~EDp>{7QMoc23!LQq+v2!!y` zNaVABYgy}D)Yrm`DOE5BQ|1MjE1{b03k+DY>@U*i$dPNZ2teH2t>=1_LZ z+ddS43BA-~EE@W<4r$x1oa>19!E zV(hy^h~*^(XpfnDE7Cf0a_%&`a1QPh^u@H56Kn-Rl_8gp3#lJih0IeRFdoF2uc?+K zhA)8eB7ME0b|Q*3x|SqO-e6qciz9m{7R=9#?u#Pec#?m8uT!7w@$*!A5ec$&N0x$T z8OHq{ePyOh6~6DF#$OS|Yg(_HSOrI>7bXtHlh(5F<3XreamBQH+dsSewyU6Fe;Ftc z?~ZK@=PgCW{RO<6Dm~}4^#*9{3F4$T(>&=q{D}V5O4#zMk9Wx2QdBe^cAOhZgZBuF zKwnQZezYWWv_BSzGnZ74TWt4wi2amE`pF-DrYxMKXrt=QzP;}{jMd4m1F?zYL8urQwBC2LvGwctlFO8!fXc%3?h zIXyNY{YHvSwhwBU$8|~^uO}ww-@1q0h5z7j3b%kl>k6g(#PcLD9I_>a9fQaq7;2*Zqwn{yaLu}&Bmb~|YFEJrou@{=b zEA;%G%ttumTWw{0?x8)(iTMw&OriU=AlMdUY8P*2mowSkK(BTXqoJ8(D^H*0^>0RJAr0vgE>47L18G ztloh{B+J^Oi8mHLOPKvEKPdZTP`1|Sn<)kV`>|jlfROHT8c#BOQEhbaKH^}z}IClXKvmRls(9A^0-jxw^EyOUxR%5oK)!Whf zc;nzoOH<1-yR3QDqCqp?+|g2lY;L2S*Mh&&MPzDO*wiv-o;jLlp2{Lyvj~1Z>hb;R z@e%JulQ%EPc4s7%axJGqq*1BuanZu#j!Gc9>C2r(->q*#I)3vp{0T~ySPHZW z^6+#{+xp)96+!pr+sTgh(Rdi^TdTH2HZiv$ycS*%@RMUX>^Y)7x+ouf5)v5GM-9T0 z#fv*DoE&$)X^q}V*S6o_1Sg6dsi_W579qq_K;N#0Ya;!pwUKGgBGf)(mH`EWti?y=CsY4A+scXif-|a**)cnQ&+D#&hRp zKQsHX9{J)OvtVlepkcS4Xl81?#TB@5b@sO3@V3tu{xFac|H3CS+d|>8#YPEpsE;`$ z*UZ!4*5=2|d}?-nxTd+q-W-vI6|e7WlvJ` zGrvPp^QN4nb}*@Yk~*M^qkyV#joISi_s+otY#uQ?ikAe6mJKrh?b-Pl4xDr17c8RZ zbhIWumq{=G0GyEe3=)|-p7<2@K^BnO2D{Jr(IE z#|nX}e&k3}41sV2I81oKY}L&wP=|y!ZkW(#zD%RbTs588ggp)OP>9(C`TbhfybeAy z_{rFr?-5gl-@+q5*4!!_%<=onWuEIA__IIG5oS*jvS%3I?`3>1ckQ!5pPw9+wB+&p z+WCH1&Eo>~W7G0qEuMwHZ>1%ExxSP6^4BK43g19r{ZWKSQU??bxRD{1?yYG{3<;fnBQ+J7XY^UA!R%6C0RpT) zD&W$h->065`Sg4Uhqf0~-$l|(3I;n8Ro;r9DR1#6ymq2KsW4vG`}js$!F&n*A*f7U z4bYUkKzy$YWET7Zk1=_TAOyqIRi&5vwg%)>#vW_|B4dsT{4nqLSsL-)tAB7E2XFuL zMSJd|(YYr3`!}I3UunxUaz=*G7LdOT=Q@fOO;eE31`!f&u{zH)I-?@vgFW>O6U(qS z*St43Thhmn$crFjyRgP28Zb<1mz+@VdA#*TZqUb9L72=@Rw`ZVX{9gX{7%B*QwUMbyy;pK*iHq_A~Lc zi}4^7PtrcFk1T*tHLwmRG^Z-u6d)6q&7tk{2n25l#9as-TSe++Q=ja|%ICi>fs&c* z%)EMF^})xZ&40PDNECq%THJ9rYq>z#bu9PF72g<55;w7G+<)v$qHJ!RI>Iqmvp6RY zCrvM341p6a%EF_iEf5y#=UHeRQtOAj7Qe z!eBH9&F`=?zG26S(}&Nn(Vqn)$AWx+jP)D2?L30KhnT?cPqzAOF=gR&Bxp zCl{~jSB2gA_#6qn9od`Sz(4yltCI~H2c$w3UjRhje3>9FX>*%~Opf3FH{X0;za zju#1(-K~R5!nBx=r4)^)er0kO8+5YTOg=_Y0Z28W*8%5Tek9c3>$j9>YtK?cikj*p z$f0Cn0O*Y6jYg>H9^to~LwAESMfp$OClWW~QLjuATb-?z9tNGZwW!+sE5!8^Q$Th} zXeI?d1MGl)ZF}Qgc(v5ZGFn-4rb~{i570zm5JH^Zok7k#a4PhZE$B51`XbMaB$YMT z6A%7KbKR`f$l5ytnd0Cu2W{_$5cmxF(PKtp8<_spGSx2y(|C?fp5rV}yegcm@nniK zupHh-vmGQcxE!gUb~P3M-Cb9w!QA-ZG2=$>kG5()S#Mx`a@uwck@FoNUa=4{eZqn8 z{_Rtx3`%=5Cwxd*9s~&h)%`#HTDHz(yg#~`tY#hn2aBm?%|2xvo;(0e_bJnQwt4bS z(jur&4O(jR>qWiY0sXoKr`bK=B)6plZ9Xp9xOBh=;^jj@5UMhpOiTq1Eo;%!0lv6# zL&6sUMIrc;^+@qwr5)#_XHFq5%*l{ozyD9#yq2i3Jqiemc+5!nLomw)*_H(rIjtv% z`@z>2KEt%jOxEC2GOUyDnP8>ubYyAg>U;%P#H zYx6rgzj)E==)H#c+2Ju?d_B1k6^JeP8qh2d(%RmFt=m24g-EgMHpkW9%)`>v`Io_O zWS}_AXj`7eQ1rs~|G$t3avuSS;?P_xnpcl*|DO*Pa+%g`aw}6DzLX-}(rpU4(09!G z)SZUDPqkYTcT!|9ek%J!n(RekfHw0}4;zM)HUP-#0bqCFwwJ>4CyZ;TDE+{Ni04F* zY4EqM!Ls8EHB(71N%one+Pne%=g)HA-?%lzDUFs^ZJu)y@>X1@gLN?F!Iie9gi-aAu zU3(v{Gae7-eIs+Nfjz!*rp5=K8eN?~oGop3u_5E1wQXZPA4Tst?&mojbyhqcD;}Sa zDYBR#rkjhUu1=jZ%yia3toYL~@6(4d*FYlIK^LzI2|NA0f|36ktl+A_ZL)j3F%g#b2mGHhWqj5XIjjEJ5Km#xlk?as%x#J(1+>}2*)nSB*7Hn$|X z+Z^Ke^7Qjhmf?q&d9}bBF?Vh2SKC&-2g)1?vv=+5^D%UFPiIj$PBzBPHM!=RW~htX zyl1u%qG98!ekD?4+c2NU3j_lcUirwK4YG4ZF)z_fnMu=kULEFbk-Z1Wc8|H0yJ@tQ zK~jo*;&7wPV|#@#Abk zWc3hr-lE*DjwekfRJYH|OC8YVnuimrl$>Stoq~xdF-0;7{&$D zICC+7+Y<77MMAI1JlcrJk436&8Ram45w^QXL2%r-2b4B6GgBpGTL~fI&kLwY$mm-w z9V3hnWtzQHpYIhuG9`4#x=SVDrSWsQi%l%2+zdBf2l#)Kh5zoDcDisGtX!Q>kz!xy z-0H0lF}}-jUFFEQ0jGI?RQ3efE&s{}>VgeLxrJelwlD@m5oRk6qcL8}WPR1!qj|JR z{m8`r?xdx#trPFp?gTR4F^}`kxp;gD@7A&STF#pCI!ocD)xt>eSjHFC{w8&5iLrC}G|i7!+@65=$yP`qS=w6@nr z11kC2s$!CLe~ZZSg`m&icb?zKly|pn*V^)!zY4oZaxsC2t z!n18;G~ncL{}1W+#GBe;bRpXdymS#H-x&6*K@X*`@yZn8wRlNzjXo8)1r(Ej z@j$(TlIM<706&Ae!0l<@ao@uS*mjYzu?>mLvW$3@%3oLD+ou%(u@t-}AjfHXxfjl2 z+ktf3LLIhle9IS7*BjT6^4@46K9Z;> zB@k2u{z!zF4E)xwOQU$y5UKHt;6a-))8Kgi)~C67V7w;x3*v_Ggt?N`eY_-2pCwR| zGul}XTp<3FZ|*!YXw0Z>e7M#!_iAMpwhr;Npr8r-2yhkAQIGmmfz2A=i)mH3lT_`Q&fAoz z_G8kHebSaayo7SC-%VLaLL5Z?TuL;{x@jG+CcFaWW`reNgfY|k*hM8v{DGDt{FdrD zd*pa-(RS!dVD%EL1nXpzTo|A5Eid=$urR}nviL7JMFHbyW@n}3yINz_Ul)fNzk9+k@fMxn@4IZP_J9&) z?!vh*sh-ns!Iudg#jaan4k#3yw{pF|M1K@lK9#8onKtYMd)~g8!9UnqD^h7%-ZW3C-A$V#SjcbQR?TfdWG7-b?mS&g3>M zW0IXiDK@yUrb`7ZU*dxp=F}|CX=T*yItFUnkWYD;Ci553jMLrpm!1w_4N@Hiqel*V zOC#x=-Rh?f4DegiP)ZJ>9tYC#DNkd`Lq?-<)}v#f7Y}Tmg3k-h64C{Vw={Z82I^$R z(l*YUcyB?-;^AD@9hpr#fn2)uYS>2;97J-9xWKr<5MP+*huQfs)OFEdFKo%+;QWqC ztO%6vZX0{BW04{%+dZ!~^zIVCks8D#STnEm_AkmRiGPnfDU_LF!>SKs@x4sKhj3l zWfu;Dw%6Nm$SA|%+d@6g8*p}_E;z}We+RFgIcZtr@%Jt;?5W8}7mKuOD9?N&nE4xX z&7lz_id@_o^o`B^JWE+G@&9q}XBHUL;OV_GIWWD_vl%XBH5n5X`&D~R!uLRP?q?fe zv$Y*J0x7^8%cFlgXwee$Y}ir{B@%MkeUXwCd;yiQJ#QM#pgS))_p2>O-;ueD0}_N;B?@H=HpHyjcfRc8=QEmytWKx=9E} zz;O1#eS{U@jXO4fj%@A(<(j@w7GdYCZ1$`yTiYmVITQ;lCO~-QW+z=}DQ^4*$HTaX zWpsC9x3)jrO!~L)CvhQCn`dE2lO1Z?kfepQ}7h~a>C4K+YgBJDFGpewK76}4$n&C zRrnpCJmATudE|>0A*BJw`9_icKd$i^J3jaYPG*>XivV|$__%XOTNNR`&KQl%V%I*I4vUfd{@zVgFVi_WI85OKav@Ion#_zWf*IDwii0C@*`yw=TSu^I`X;zoxvx zFDm?e760l<*5f`(ORx6vWm1*y33Q^EQuSR-eYJtM!ZKtPTY*`Xd95P$V}M_j4ZGzW z70^h<#@}mh6%Qrpk*8i+?Mq%+SIM>Nn=uh-^cAxE#m$mfHUsXJ1%Gi_k>d@VBGR2~ z4f&Y0(zgSpSl|GG)D52enak6Xm=w4E7q0Wo6#pualX!$f{S%)hwc#B|$nm8z_iV$( z74S=7PHno>clPW=0#eo&N_%)c-aKVrEd^EIaL6l-9TZ+=#&a^`hdrlLor&(x_A$UI zjVo!11H)&tPzx%$IBUtCLJBzn8;jt4hN`V}1@RRRnA=-vr^Ogre`b<}+F1O{>#D;F z>&;#qr){~0&f+;K)am`yVoDPC_PCow?y$VxBB`h6KyQq`Pcb?s*3H0B=rto4z#Mt% z7uCHy3hD`!kQ1g=am}Dc`%bmLRmyw1U5;wh@7|;{QS@+TUq#a93)5${DJ=U}8m$tC zlby@B(nwu6_Tn6c0QFKWdlu54T1#n9ykY#*FCFyRkFtR7$uL1HKl`Rcgj8(5&!bO& zE9MiY^ZDw-Ih9cu*FChIh<^*SlT6V=5b=jw4+gQhTTyom!V@f*{tR9+-U^_!o+J7b z5Y>6d&$jc{WLykhe|bU}ls3A`USkOeqffJUZm#_)Ypd^>d7~{fv8br$nA$^NIyJx7 zUdF@OA~w42`zZxff}zJmsI~)4{TmBZ*(VtGk6$MSYyQcyI}6vD5*g$u{}KqYDYwZV zO+)EX`nRU96_gX)w5#Zhv*n2TR)Ozii?Yu#*-3V9yd8t>QLbscmi4l&cc;8!4RQ*H z-zle&2isjN33?LCG8R-2cL4ZUrREL(8GD9t`0_+l9_@~o_oGth@Q(Xw^uQsc^=rkG zcA6_@Ab%A1Fcsd`Wr@tG*bb}@Dk3#sYo;o3z(1p)wBQY%m33S(uSG~ zx33XKVUJx`Ejc~4M6FCs*v>YkQ4}@89rDSP^o3&rK*JtG&8T9Bl9n8!xLcF$?!X&@ z&1a3jUY0~SiBvQnO11l-rdIt=0(bZKITW(al?;hs&5{L%ierA$ug9P6&IudZL{bIW zP;04=1<`lJS^=xVNh!_)&KBTI>6ZZJnn1PmAFfKEoh~1cqdpnYDsd-jWYy3l`Hdx@ z#f;6oZwUat7JO{_V* zmy>x^-C34iO=xSUSC21wKQ+ILWK@7fXaA)|V@TmiIF3-d$-bG$aCGuo6GVDy zPgQ-N0bHzDz(y+eAnwrClgi(siz317MI(N#hq1AQT+{eww&B;34z5^l1)f*|il}nT z)%_WYPMR<2&M#iHIUA_#mInq(oqJ1a1s$QND|0Ow)9c=nvKRD_#)7yp$OQ_aKfLfq z`w8MSaDf0h!K7+UeLGqC5g|Ls7Ez%H)egE?zT8L#{*Rs%2Kc0bu>wT!>AJnxYXK~I z!vbheJMxl9P)N)E5SDI&_IL{n-E#`t`?f@N@WR2uA{^kMT?8J^2Q*1n!9S?Zc{Ig1 zz8fDzdhP#k1Hgyc9=N^A#Ljx?qK;t6uYp3M^%Qf-mt%u<)-C_VYUYbbgiLQMudgg$ z4qU<-#$BFyKP@>-{Am7^U&cIcD~Bxrtnsn@d0^+InqKu|PSN9U`I}wUc8T~lLr$Rs z9L5>p;_H3(6M@)qm!6i#XT*;?eVGq@ahyJ}rJf+^S+blf(%{Xc77GdVq(@^k=y*dX zn4{>xFNjZ3)CRrU&}s)hRSyaJgFYm@?%ONAYP5$EvQwysKKuZ7qd-(bb|Ir{9QBjJG6e7e{dV<* z+1ms9Napt*mU{XqXE%@dlfwpZu`*5#K_}~-X$K!IBMPwQ-;(=*y+36$6ABTud^GWU zzvRZrYsmMv8(W4~i-&u>Khb87g4Wgv}N)Y5p-44j-q-_TIs3@RTr>@ZTit5c#-Y5!B-3^I$dP50FF>Sh{;XiN$M-WqBc zA15zYXXYpjD&W>ZO$g2|OvcS8n0}jJntks{2wGWRn5uqGWP;yo4`FnS=)ee{H*pai zmG}}=la;4Qe{V;>n$ z+z%3?XLceo#sRgP;zjFmYu#Iig`I*SY4Sj931t#D?D|QRPYAq+kuI(c)x1x!2&ccU zVF28T)c8GtWTe=+8`}ovuBMqP1OW>oBoTu_ z5=g{AB7_j&CJ7-N{_6%iGygsNnVHU>7ti*cpU-05cTV?jt@XRE?`5A`uGG7wzhpbK z3jd-t&WyDq?-}7-#mYm+<%dkEZ#vX5`t;_i?fL@!;v?+J7#1@w<-Il@^-b3Jk*x7i zo4HL3c$z)f)p=})75);2)=J@%dkn>NJ9c>{|33U>MO)$H;@nbb;~gP=^e(z0L~0_g znu!H8`l>8sSyl*SokrR)1XLtqN7@QJO}}%qBUyA=s_q@aqjhx2Xs~`w8$^#E{Stgw z!rAndr-B1l+;uJ*8?2@1wnEjv%?-*92G5=VW~F z&%I5xbXg)MeNS)MpAau>$v4HnAu?K6gVg1;D)Dg2;>*6nKcc|^&Sd&|dzX_~qiXfa z<7jcfpBy>AX>M54DyFG{QfZxkb}E<@>KMRD@UW=-6X*RxfEpGM#DJ4mO;FhavY{)M zWYO=EV=S`SEsX`%k^;)YSz}JfLXPJm^wFEER7T8iG9{k*TO~aCIVtvcyYD;CgHCm< zc?`z*dZJNAbBfVC27fACMcNDyjqG%col#^jLe)V!+Ojp7a%K()q;5LWo`-bP&O+@P zB&85ZdC-f7x!2Y{HEY|-8v0x-sb5kVmt#yeBlM+j*TP1Tv^|tD5Q<({Q`N58t$2cd zU>}$4Cb8#0?UOQYTPF~caB1^`X3QnUX? zg>{I>D12RbCfKs@mykD@O4FYM`i0#>a@cmXSSm8G0O4(npKCc}3~csNY-o;eUgkLT zHAZxTIL=?`bl2p8bMpH(m-|W}N9;>HQ3|+sf3e+7-Vt!XZa3bZF=$)?f=IH0OBN9@^;fFSGz$@9IG(&c05Zy+0fE z&59ZP)_QOZISxIbGmbiEjXHCvpXf?=67vMBTPRtRoD+myC4t5-!v6oSTs{+%6Vzj+ z-0}xY;km3$bLIwx8)-X;w*#wgK&ER(APwJ#1Ag6k%l6^@66xORw6W?d7Q|&<#bYf9 z`dF-XS|ih)(|N=OrncYPcql}iSxFo`5*;KrytUo=AO!ZF4e^-m=&=!Ob0I;3)**M5 zEGBw^h4-Zk`0f_rKF!%g)ynNMd0*&_T!C#f`Fk;6x6}nsbdDEaaAH^N*B=z{?No6i zoTot|O_0Z&)Y7KAsMMgqIqy`kr*^#6E>sh=b?MSxpwGw!ZQ1-z6jV;cni}Wkm7+RX>H7^NoaqQv%bR#rJ@F6OqZT>*p{C#d8_&?u!$@=EfRA8&b zW5t~TzmFQyDqQRl+2kcs9ftd9U`SDbG*dFfP`$kXD}KF!a(8w1;HHpNRZQR)-6){O zOP99V+kv=~KCbibe(A-F5V`Ntrd5~BsW@@zR7KOtqxj`>K||VMQkLdyB{#k!s=m9V zIipH>z;9QH*3hO%6x02v_glHC-byx2LX3XTBs-W1P86L6^H0+*bZwLgmX8Egmlk~D zNU?xZYZggr&9e^=SqVJsd#bIyg*1X~BwZhGO39JI$aAlNw=?;FtXiL4gkeJpU&}7MyO=;DMMIT${Z}QgZZ+0$?1^Ck{;)Y`PYnw1(;GDgA5 zDM%e$4LorYKDGNyww*5O_FS9Ty6l+S8?+Ur+t>+O?@^cm9Z&!V_nEeKFg{#G4UNnR zE6{c{aE{3m08r=}{pVl$h8zm{JkfQl7bXD`d5;92z%Y6fX6S)<)8{0J?=Z# z(nl|o-9!^C(Zn?!7jhFFO3UhaG`cRBM%eWL@t~Qzrkq+x+(-E&YY*4?-*H$Q?HPwI zIiVn7{*7Tir?daJ_&K9_>6Ke+4RCFaYM#r@oQI_d1=+P$AF09JPBO=ieUD7nb*lDc zefJ2ZE6bm*jz6vy6Qq5&ffVRMDOU!G1^s?n8+!OV9^-XUz-P{1Pg79UH zwk{`$i1cinkOCnA3NZ31=IIt&tDup-=YYuCL1aiMp*uW(DRHI;Ti*=Woh^HR^|U~k z8psTBV=O>>M!%GjRXYL^I?|@mkZBEOI<4FKB_i8(&KmGtg3f+%3s1bGiGVB!-V8uZB2(}a zJxDHkjnNbdwo|j`>W%D73QM^Zh9CY0oEijnuI&0-Z?m`b(Ao18c`M zM&M$?Qu0R_H%3;6V|HSMRs{h=)}9dGcf+$q%i5(sG<246Q^rE{)?-^K86wSPa=`cA zbnTqvqnNv2NK5&x6=hGC=341zreia@ZA)+R#yH%`Qx7q220nCMyA$(lr$?~P8#7C? zUpjmf2><)aR(&M)-!2hJF~6DNFY8_nt#-G~srSiVHopJYuL6~{M=3jX<79v@1FX_v z8e+}3?KN$rq4X{vLl&~U>8-}}fwKUw_(^Ya%p%wn0lH#JbgyMcU!wwXhQwg)Tex`#eB1$}C<-G9{SgYSe5{HwKbLKlHQpVYj0}F4KueFOW>TLU!({C6)Dq zC^ynGay~;_!M~blF6H_Gctmt3n5>5LRx@?m5y<84_g*O+}DR!V~$tY;zr zT8cQQi+CI5>-Nl)d!rMoa4++XtT)o%urA#n=woLyLISzOl8)yAQ6vSgiwok= z2#Pl-k0`G9Q#%e{_r(|3tOI6iapsCuT0ce(%EShDSxJn$cJ#X%u4!q$9QpxZVbf#o z1C5Q&2njT4^AIH$s@`k2WUhrIV%TSgw!+W(T%q;K_quv}O4{Anm5jTwKP%=o{F|GU zvLiFAdmqQ}c`fxZBAZjGqH>mwl@_Q`;Du^|+X}bNVRR^b!HXhW*Em&|4ilHloZ3iJ zh!F9{uzKk_`*FM13+R<`4)OE(-GeUGAS=&1OZNoupX{0OTXG~U6CN>`Vpo^AIv1s@UuSE*%= zPu6t!8wN3&)Kt;au$t`e9?j~=KVaspJ#f8@>lx)Tl2+#W$*#vcZnKtG~uZ;yVXGX*rGd7 z`hlv3xm@OZU5W}3K3~$K+8QUG(i0ZwSERmIr7MsoOqzQcA$0^#tWI#qFxic^{yu=V z@N8^Y*V&JAZ2K^T7Gr&I1T(c+>iqAo)Tgh)=L&_st8>2D_fJgorHC>|B-p1eNJn|u z7fZiHuFeIp05w1j}8Wo4plK350H6M_pb`YcI1VRH1+3C{%$l@Sso?VR*%@{+$pC<4>DVT? ziAG)d&o!DBmp)NVDN0}>V)h-#zU=!e2W>-8Nk(}SH(|G~ikEB*RyUDEhKZZ2wEkz# zTR7<0f9E>e>Grm-6#bf;z*lm#LH9X>!s*jkLytJ(dg6qX_wQxS+t#dBm%$!*c@P`3 zx;Pk8s7IMWw&V1d{OYx-Z|Q!JXipS11_@5n7|7MJ14MB>h{BwG6X2`+63Oow4iZB8 zPe~#GGkbrnu67O7wbE0pV&Z@_TJv$kJUrQPMX3Uiy4dxq?}ddi(%9KGh*+Vo&|iXC z0iv-D6UVUZf)gVci&JQ3w(K(*URB>e%)3-cWc!EMo614VlYrdFd`~@j0eZhmsmpry zs*xC(1Z)WbOn?|S<<)C(*HjHaA!DgI?4*rCr(w>YwhgoJfc4pySUKTu97+I4@ByTR z$^D8d+YT>9Bg0CFrh8W4;U|TpowFc){DoSytC-Q7%5~mah_pcL!MVr>HsAR;%5bS>G69@~dou?$~ zYKnX}!kkx;0%9`C0Lzk?pxS&$0>VUWl379jl8n)buBJN}3OK_enkFwt-IlzJ99 zy*02`H2Wv|79e7tcu=~q0jEZBLNb>QzvYS^ei>}}+xZx&-ak|{!r2j8ea(=_yo27b zj^2XlkvbRSynuoufW;j|6<;9&AM~gA1R66wS^D8RR13f;`zSCkOBLbD)*~pO+Y0!! zY5;-0AqPuE@e|IObx0SWW|lyg$W)b}J^(;7DMIai9zb5-@^3)Pmqwo_2oCYKBpKE@ zoZ2;NrLoFEVxq**^Xi=a=z6zfd6OCI+xcwXnUphKJK?61iWF<$Eb`@<+pe}-6dPJg2DUZZ}k z@wbrIC?KIo^|3h9BSdIMb#}wYw3l#YKucyHo^90}1!jYM##E_h4ZdZgSo|_20?0Mw z1XK7C6mFZ^*=OTd07-sDoF`4&+=P49q03UC9XJT%QWv%)zV@L&hOxa0Wlg^gybTji zx;dyC!wwygd0e8|qFw*yu+nB=<|AWsbbUc>vvw=SKSr{Y4+W}FfcsKO(c@8w5O>=X zM0X?F$r)pTrW0q_6df|F!;kirw#+;fT4Et#t@1u>H^$tTMv&h7O1dndZ4bt&ta;q` z#4*<>6%6w>?eg#;QR57D`Q^ZlJ(%_6JjSgso<7Jet-@Q^SM(!JiyYX&G<_)`aYNyp zzBSOLsB;^cnl4G^b2jS~CBjvZvF1D}OsC8a(;!n=&A z=lu0AQFNFBICpQ2atZ!&dVY*O;TXz;>0LkY535IC#mhI$Nf;h$iGQ9+GQ;B5(BX+5 zRQ)*Bi;dR>N=@$hb@AvojKjKqSXE`Y7BEs{OubjeVI!h`407d}>D0T#kQr1m0t)~`VvAZo7)v^kZn z?X3(fryQHG8&SXt@JU#MR<+$G4q;qt1jE@s5CqUjp)M?osTOcRsXPxNm|(2c-yc!I0pJAD zNn3LB-1!>;X|6`PWsFkD)go(gw~*Cqh)lrvGEPP{NzcFIYr(q32Ko_u$yg53nwSMq z&x3Ni#;Yvht=6nhlfL>3-mK&&j>tFn6~qx{&H`Ly4iI8~qaZ6Eo5O-4%SV?s87&CE z>c@F;pFE_}CQ9cg{4!I{l0XhkS=E3@S<^&R^nc)>LuR(YzOEEE#96uWfb?5zMLWrf z4FYD2U);o$WcA%#o~m${q+tH$K=VjOBSV&7lo;67Zj+7%#mNC9tC*P}6wV)9JheR- z*^UCyHsObp)Py5Sq#>d7Jme}^hgs&kfQIx<+@lKFQ~qYjHQp@w`_F7CtqM_MXs;jp zPYBJvS0jnO=t4}{CaHU{$^L0Vl28b8>I@HA>l6%fl1(YO%Q&v8SDiS>M5M>gs=7t9 zeVfbC)jT+I&z*XR$HFXD&W01Rl`hSWeKdK{rTwH&rr%N7o*+4>_J#@*oV(YwmzbnJ zqW_HjeEqtV*5`E#+-)>f6tJJ z78ZEsXJkfYjoV{GP8dhtot%mqX)e-d%{7&3?FZL@ltXdiC+2Mi7eEOYlW|9a3gz)b zyp}Yd!R0XmLhKCOGXQQ`-_5*Z*r`A?l*yS!Nlk{AxZcp&56)52N3f~@;4@V)a`D0b zG?++v9lckXSZ$r2O2R!-ylXoQjv!jN0z$d9!~LLVzLrokJ1})8KH2uQc?+SUQQKO7 zyKFzc{(pXBlR;aniF+`kD7;a7t+-Ar(C%n*_mM8YqMzO{XNaiUmQ^HI2EkGw-WKi* zyO9h%JaCj01HGoqFVbb5J#Bf(YK9Ti2lRkP8P+-(bspf06s?r!Z#}}c{Vd-DdV#+h zu^Us?AKkh1ZZ%BqGJHpL`R}ed0ZB?IasZ`Na>S4;5K}RFXEsYCXh=ulIe(qM;~hf+ zW!n2F+4C}SjcDBfq}u9++oF>Tz~(Jv<;HX{A!hEAjD}!~Q`L*3n1u+C3v>@iu8fx| zvR-26jb#=honf@qoSZqWaZ{Zkk&xt92>52cdwD?V&$IrXJC-Qasntmar`7iBd=HDB_LpR#K-kYUpEAJJ+Xj2h6a* zVw$pbmeW`Zvn?@$PE7)z!67+`M;hM=P}T*eyFtR+vthh~<*}W{2%CbC@9Xybj?{E+ zf>M)SMnIE;j$qQw+ssED1u_8fI%Ru;1zqibbT(nyl!1gk(W`thl?W^ib7+pADJQ$O zWnI>C=f!bt#;cTDVOIgO86x=h1A&UJOg8+A-lsru7@7YR3L7dE!N4s)$HvVAY#4(1 zR-}4KD^QdhYZv|kty3z8(JoHZ4N5^lSO z&REN*_N#-0ZT~ zgL<1E^|gAJWHpy;#KHv1Y2=5KyYPZ zDjLgMy{d>FW{i9S5vuMAYBRi9HDr8O^IL?GQ^G-^3;a6;^)Oj;8-VV*Xq$QCO=TkQ zG6AIA)xlhr0QEIAePN1Li#!TEa3IuYfG4QxhXy~O;tSr zu3n7?GQ?iCC1&)2KA0x9tq-cgu9lT+!vG%39Lzplr0?1`(1)x(SqiD%iKdj{YANb{ zy`zR>_l}|ciPx=O4S+=d69#b)fzu)?B!`A9#Mku*Fv8Vs3H7Wp*XB)Vp#Q*|QbDF0BAvd}9zedLnQg8Rr?*P2P1GIA^?+ z?>%KX2%Xaa+9*n+$ii!H%!)d}sgK$E5cgy-iS{y{oN>p@rgdaV{jhrIk}UX1!{b*lC0|f2coG4U7cIHu`hK6uuFCrZK4X zcaQ3%gGi#s@FYm(;vjjJ}2IMz#hqqM4+ zIOh9U2^HGJNgNL7ZB6s(rkGzlv{ipWd7LrXLktS`1iE>tTAUD!(u-X)%hQza8oL_Z zbVGX&qN>(`m~Dm`db43DI2(CmcB~3n3rHV*MRA!mKVdGmvja%ionI@PShSUptLr34 zwboXbJa3$kvO(&X!WsulAWbms>$h`XFgAJa97i6Sr8CC+ao8rUL$IuwP(;>5=0j6E z%H8Bny!OR`LUl$aX4sqzA7N7%Jq5Cc^A*DP?nv z4p%ulZKLtkZWbLn^k)#yIg)q7)PAgbzI@|=A<>E%1?0=-v*dFc_lE2qhd!7GjSsV| z@iEU8p(iF-Dj1ODqxKe@5W5_i$xjvZAVV7E>lOVdHv6t~^Go;}C$ZGUawH%CVS52z z@+hh67lNH`#5pnc&nP7)N!i2iz0nLAAOP_y#%<^R!Ixnt6=+hQrsg)hvBrF+d8xm3 z<={CvO@mp`0H9w?XvLiIt56uc9r-dO45P~)s1FDpoWSjbT*jA-L zFL&Z7=ah(}JKIbcvw!t?a%zgwg)Le>uU4(1<)>~2BW+8ls9o#F!A?P?R+pC zm=0I?3qdwVrg1vUbMd*q(M1Y=CWd$89+e9VxojM$wn0w|;u;5s`Vw03zgH0b)FaEl z$%hBj7w~=k7u=syut{?i*>FNieH&Ci$s5_@V2?!&@URpXKM6LV(%}d?p04zi6dV1J z?o?y8W~@cA)Gl#M;nFTm*4)oo73fenR?cD$E}XVruxAoG9%IxsuAzZB^!&CrZm0r{ zZ}kykeh)S1?i*av4Jr=q5jrGo4;0p{&?!%AUd3H*n**Clv}=K_=Rh)NChLqc{(d8k z#@TOKy0;Mk9h11AmA#X_XXSx0!Z`nDYz>^YN0cHaINau|R_LW+Li5|F-UG_ez%&NC1Bc zJ;p3%@+(0P5|AOaoXqrj|LVt}NfB4lO5F;YxNFXkhkI)%m8C4u>xM ze7duWlAkrmDgd_Kgtf^a(FI;^30;!bbuScA6F=f)wt<43bk1MeZ=^z{%De2Y2;`vI zU*!Q@EZyx05N$>G(H+SKihxlc7)~J zGDhr_zx{-mG~cSCYXjb^NRx{j8F_zw$>w43csf%jr*6t^eFXC3sJ6VsE~Kyum|8?R z+7swB4nDUe5PUJaOE=-GEl_4C(C_?%qZR@3->W2^F6&j~zGnk_kD~Nv8nxuDrk-v<(&`yfH-ju7wm)@y)%(N;!b#y^#M*utFa(d{UoZsM zCySiKfn$wa^zjjeR`~*|5RjRRg|qDZjDVK#+Pv*|j#U)tu}n#+yqX|-0jt2}kQZJc z??$~H_p~{h?lLXIU+LJ1FD5FlU_2k-IH*Qn;M~q}gI`(33~a!wNX{HmIe>c+^=02E zI2q4}p1QAr@i;nTT51*xqBm$0QSfA_w9WVD*4Vkf`&E{Mf6sMd$xEJCr4RB_s&go16pv*IJ?HAsaSJ@Q< z2L1Ov>j*FJOfNF+uvzBDZv}xP>v#fgPm8Uq!faqhKjjaIf7-Y_y0IAtawt!Rh_AG= zK$Z>wFX%`MrB$sU@(5(^Y~&mm5v=jmvd;eHxTTi|Jvw)DJEdO$d>CNNhwYk)p48rq zuW665I0YBBqTjJ4<`0js#c{yB`Ol8)1{s_nQu|}T5G#`jDtr3%f;Ei76;KU_54j(O zF?8SjUQ?EE7^ zT&G5fSxk0z-^~K+-A1AOJ}BtZm0PQ5v6{+yA|RagCD0qrzpZ4RXd^L^{wQi0d5!Xr z?pji4bIa;FLxrfZn&n`h9HGCB31Yv?lkw-5CF_&})f(mCr%XxvZi8Mmm^nGck~TUb zwtH5e8Nv!v6ZU`%?mH=Nxa|RktLTfCPsxV8qR|?-KNq}Bg%E-43lRvE9NQk;4VTX+ z4LRm_RiPt|j@I*kK}-Nyb&+OyA%4b9-<4K=Lbi1&ZkMB=z@>(9A36MZjeO8S>jGps z@!C^jeNLy(ijV1}==4XS5BSa%(+ z7b1Z03e{Z!-ZED0KZ2WM*_XSRF=1fa4Fe10nMsx2PwL05O+1Ph@wf`aDT706gjKvz zwuQzmFRbTe_lTY2`4NY}P`K-dCU=K|31cJ;m%{LrsY=^HaCA6y3P)W0oP)QOA;zNs zA`TrDld{6ouW~As|NNEL@CW$d#sR0Pp*gxz8C-~b&!|Add+^dFzTV!B8F&!DmvR!b z0LNZ5qGhTYGOllnGAe+7&q+Cbyw_`r6<_snMQx(o)$<@=R{5`|+BG`mJpo8n;B0%6 zVHXu}LhTo~27*;B9A~OOs49bCI#{W%WNBlptwR#~Gy;!wk z*&rZXwZY?GNe^q9em z$(`AXn=SY7TMpmK|56A5^tvbu`mt*XS{zYP;GuF-e;~L$vJY|sa0S&sPPw{ghjSed z>{97^Q3Z8FeFaKC-``1l7ImK7$V$ipWbq$Z)Q9sysIiyL@ZOU+V)+l^akC$;3`5Pt z(tBrWGMvQN%-s6SLyk<*_yE~0_}+&wWI#l5AWd(;jc3$YcyVxBuJeyr!UOs?7+BD} zoAcHbK1;s_B~3k?qN8R9!QH@sgVuraKId5|+;Hi(W{p+w|I9Q)Pb<}KVb{*sGiXJ_ z^FJf(N{9@^{jT*{bRi_s2Bl7yMF3B`o=y`VYkxsKLhEttOprrhuY;in_;l3}-A*Dz z=rJYnWorzA8t`~(mho@^djJv_5=Ys44F5?AlLWDhclqS_5j$BGWbh|%l=>$tULr4= ztGH>;K!Ae}8-^9s^Z*-;d!R*9WjoG14M<2ePhUWeI3~a}(48&+inOWm9f?26m<%HC zRi&pr57tX~gTudg0d7*(XLVh$L_cgum0ZpEf$ya&6NAQ9P(5()%hy2);5qzdklvV< zaNvnDZVkXWY$pzy9ggF$Y0v%>^w|0Z7s->i*~4OlIHsD5FRUezY5Xa&FLXxVGdKXBa{4$-wl%R?)?I^0B=y7jRQwed=`#L&OcFx!kB@q z=PV5MWB?796s_&yd)I*0qpP?zvqJQsXCzoAAZ^k2&EEMDy)d1lEz6t+r>*F;A4qal zg6fAL)92yS+EQ&<+)*50DO7T;hi|_6iuLsbaqe#1%a$`Z#`7I7EcU`j!+&zv7eLBz zXkbW*OvDVJoB;b)zt`~u7Ymhpg&M!nuDPE!kmy(%Tb;qIkf=oaAwaVPriKVl>Z>pC z2ep3d>Eb0CnnJVGBnv)mHit1CPfK2z-XL9kxc3DBfOeDrXaB#BSEjA3=Mf*#Z;`x! z{a2pr(ShM=!@}}a?}*pURN4iOIk3&(hbjuC+AXQXadj1_Qw->1cbRrMwMyKqj0Mf}aQtBCN0H7N0 z{QC8HaF{0`9cMuURkQ-#Ss4Oedg`-4z2G>g3KG9)>r%7`g%!70 zGIx~#+1$~%5xSIRP@ZOV@9?sa{Vem;BT84Uv8Qw0yiUArO#K9MB?s>jsKosJaq9SC zb^mazfpnNI@^%DXpF66Df@)ExiO3Mx?tNo8@OFbS=A!mmgzrKj>(4b-Xrry@Oae8N}{i>T<`vR!b(OJbJ=&f$!*u zjs1)N@eN5AN59(ZbTp`h3N847rWzYWAJw(FvDP z|2L0fS%5!vi>rg3q8F5{Q=Yc6Fo08(5T$#3jg_2?rl}^{Tp$nd{s^_FRr`9q*#uvr zk^8@YoJZT|Ju@GLZ0Uzv`W&V$kL5SX0+1-%RdJ@}TkAvR!aF`xm>Kk!mhrBz?Iy7@ zbBz~7dqf9PrEgvrwp(Q*S|Jfaf*eX_g*Hp$mA9xR1dn185Gr&c2Iz`@dr<|WfFKpm z3Id{W;zOjiw8BpqPyi>@6QF=Nex6j?=YcL6kLD8@?Qg09zo~SlCJtrXYfSlYbpIj%t411yT zL0NsmRRN@sEI*-|X9))+S?pDw*iXXfEeTY$2kce-D{o9nnfxjz?H5y7-<8FQchmGf z${-flX*LZ_q-1&i{z1c_OW2xD=Mk|^5^Wqx(jBdrsDOb%QtmXJL&#`hsdGy{Fr0?z zH$bN|?^=Zzq%|o}@~V0sko(t9oUnN6Uy8FCn$uE666LynPIdv6c zKz;^8c**^aQQ^W?{ztcCWx%wzpIySC;O}FK-wN$ zYDs^F{Cv{;9#e8m^2SsmBn~;lWXPLu!vLL(HU40n1OhwrD?~o~{mAqEr>4_4oxrk) zE1L(V>bs8R0pII-rFHugS?Ebao8#fVpF3OaidopU{Fkv+vpGsfx#Gwr}f@GsV_h=qtVK}KBhnFwCXY?f_PCncaQ6kb zCznKUY#caLQ*b)IuBmt}m2V~)0BVkNExZ;};p@#WAU3HrhHFLC2c|ocEpjsq@9Ejb zAv9|?PeCh(dBD#Lgu4LVbI4QDhz7fNK1N>Ju7F84@1i>D9l)Alz5?Thc8Jh)Qsb#V zvz2wXP>==K98(YZshi{=34nw0NF3gvXg{!Qej;p-gz+s)O{~*UiU)U-zR-9V#V*zU z3ExsEZ+<6_n$uYHaURl2K@yvQq@!60-2;d=_8ox=0KNWly87}x-us0d)^TTBa$pdv18lYix9ZMW= zt!jkPImFfoq)|JYSckE0Wp|b)N&ws^O#+z}NZ5UQ3wboigaE#ZP23qE>*r@U0a^c{ zLd!_H*GP=5Z#HA(PoNWlB8~`^;^Kj>zh-2gQZ_#@#^AB{^9I+PmeJ!p>)UFVO^90a z6D3_+Is`Wek^N>rv_D1dgL`CXlmdII!x1YX>jC+Ed*5xeIe<%vZvvhJMHsN2V#8GA z-qKSv1EO1by7Nbt4U|+M6yrFh|I%BF0BB`I6tSe}Gvx;?^=Yb2+{zWe?2%ULZSh9) zx1eA02#yBd2SpO!Fj9sp?KegY$S@$u1t!-3&sDf092eSqz z3`k?v9PC|~o~#aVep0AF!#2j1u5Dtx@Qgqwl$9*i$dp9Y@)&GN7=9c@*j48Z&VmYo zcOh>lbC9}$BBfQlZ#`DQQwwZ8`mEg?{%xKj`ZzYpGUg4ZeTVwhnl{aGEa7o;t>MHr ziluOH8PH>n5F7b-IRn!@YrfQc;Inr0b=#62^)zl;65XQ z3R}yaJeNV2XlEdi1H|Td|M$fK)dofU2rXkTl7&s@km(%0>L;VPv zv(;SA&2d!s3twX$PNtwY$d%Gdw_Pk||AW@P>jeplu8mP4=mWi^{LxFphNF1KacOy@ z71UL(%Mrx10bep#`mtw!L!Nz87PY8I=hnGy`n$^s!$bX%#1POU9aav$H;%Z7xt{3i zoV+~$u8$OT9+A$At2UiMv*g+vjjAlrmTl(Mo_K(Jov}|Au%G7^vqWffp&SU1`_u z#{gF2swwK5pA+`HX_`mr)yHFYF??h06N9!ZRQf1ZjaJlNq}29(U9_#IlI2`Us~F#2 z{GM;I$k}&{=k7o-nySN&kkrR}38eUayMCKTfO$$jaQ+K(qyiTtj&`g(5~f3UX_LiZ|Z47}{|F!D*dlI_y}!JDYl(3_`? z4bhsPYsVj1o_)2{bmN*qC~BNYns!a*6C{@sT&}QNC|6p@ku__=J3{)grm zwc?b&#i4090QX75rQd{)2p06&pZ~UgxcuUnFLpXP0r~hdsYuK!qWzqTeBP=ZoicqJ zu-r6=JM34>&;%U&RW>CZx3{&jh-xON8iorWw(e2;JK}1+8z*=|O#O9=!Id}hrnR>) z$W;;6I8aZ7%Evk9R1S+Zlt3D~GOW#DD*c5RuWk-=?uq3L4?Rrz?k0!9Li$*KHJ|d3 zEbbiA?#)+|wBU)mE9 zbF4v0RZinm=zF1*RO#UIP&TC$p*G(-x%^Ds;QT>hy`Uw5XKET599}rD9eK*BwbifA zeSi}gKJ8unp)Jw(3ojhybbpGT&|1p}IEmJFbgTupI(||@SKcTxUCfrn+&?Ipo4h_E zS7yYLV6$EhO*AcC>Fs4(n?il`&a36Z%cbn`#~-&=^9&+w7t+tT$wD9DE*BT0AVNW; z+;=66wgZ9cy?rqwil}6WVRNCww6!I~=Icw+Dm@yjvX|X+|BV@udEfFwkV5;ZG|5-T zIY%CTyEsxlVQM1I_{VC}dE|30L}oFO*SBR@Z+qrWfNtR$i%Hmypy@vz@X^mxmBQ1`;fv6h(hAlcrr;2-sL;8u{ zDu(9g)4$33uGTvBaj#eQi1K@YPr6Kpv^8V%Z;i?D;aR;g*w}c3YjJAm+#!Y-=NlPd z)vJo!lZVcav5@4qDtvcvV@PvRX6oVx;OBWR=nvdUJcic)eNU{VY3g`yz^~K-{yQkf zX6rn&Q2qU&tT@8f_15r}rT>V9hAtL`t+en?+C??(uxiD}dL|CsuNY)66$v?_J_T~u zX#QBmB2yaEkUTdi^Z^YLhaMzx`nTb+A7O;rG2WDE-eG02`nRHiCaU!hmYYqV)A-s0 zffvm_1g=N_{P1IQ40JUur-=9)jIrd(5#{9g0adZ$uwk6(JNmAI*TA6elaY=nX#j5F z5GmSW6~uiKL%3b+ONmX2YAnW!&ebQVADJh%g-MSQjQ)<>kqOrG01)g#X{?SBjWH?- zk7H`AKbKr*98gw-`k~2nk3Ig^-ITax+~NM_{En91A1{F&e0{%s}wAz`S)iugAIQbOP(Dr53gN=?;&B-rjj1p4fb$Xg>yJnipmeMFB|l-PZJp)=Xkx?=dFnc6t~w zva?7TZ`;@VD=wS57}JW3WwD4)kMZY^36SjZU1I*{{Bq#x>m}4PuAlq^HXO~U9bwgq z#mJeO=*3+sS4n!YcKEw!8A}f8>JX;?gC6e+jtE4g4r;ZonMZapoQ)1R{sB_T=QR^y zcu-f9xe29Xg@>?eyhV#AK@Ng>*`8KHfk~V^*<7+9S6;X zBG+3N`%b!-9P!Ll6B8o!uDB4=N5sP@8{zfEfadPua__z`)bac?E%&44XDFsKmSqq# zCQ36SyyxS;saMBeBj(Wh2>(w`wXBL=O?~fbfJu)iZZsr`vS%xhjm~{dL7f%5m^{-o zs8?8SLX2tZaiO}QGM3A)A{>F1FNO#!g~yvlTrj8m1=VDWKQe1^AM_or7J%c!mKXa@ z5?YzY*rkau!^V1pWTx_>mLP=7r_7sq1WhxIAqkU%xLtVVWBTedEHTNqgg`!zJ?2N$ zkAUjO#qZsM9v0u7#GCiYsv9EHoN&nWs zqn(5Ehuc!~u;QqsXD!7ut_r-UOPI1;cT$iTvvT^tGIev~{C$7nW8Y}aA*hJiJpIwHTOn#!o}~&l}lX?1AWmRjq}fm z!yD18HcFMbr;%7c*gA_qVo*5Y5$@1{Nzi*+QnWgRW z)>T<15v&%DGxB!`5@q1gjkYxB`Q#Pt9fkLxNE~DAk`6D7u}v|I_(OxxvrS3yy9y$y z^T_G59I-n1ux(7i8`6&~l)7>OUE{Vid6F74{GqsS{L=#kD_zGP)>~QKN!(~#08Qu` zLednHWoFmr{HIG(M2S+N8B`h|z&3;rpzHd&fmyC^XN=^N)1Q= literal 0 HcmV?d00001 diff --git a/spring-cloud-alibaba-examples/governance-example/authentication-example/istio-authentication-provider-mvc-example/pom.xml b/spring-cloud-alibaba-examples/governance-example/authentication-example/istio-authentication-provider-mvc-example/pom.xml new file mode 100644 index 000000000..d821c8bc0 --- /dev/null +++ b/spring-cloud-alibaba-examples/governance-example/authentication-example/istio-authentication-provider-mvc-example/pom.xml @@ -0,0 +1,38 @@ + + + + + com.alibaba.cloud + spring-cloud-alibaba-examples + ${revision} + ../../../pom.xml + + 4.0.0 + + istio-authentication-provider-mvc-example + Spring Cloud Starter Alibaba Authentication Provider MVC Example + Example demonstrating how to use authentication on spring mvc application + jar + + + + org.springframework.boot + spring-boot-starter-web + + + com.alibaba.cloud + spring-cloud-starter-alibaba-governance-auth + + + com.alibaba.cloud + spring-cloud-starter-alibaba-controlplane-istio + + + org.projectlombok + lombok + + + + + diff --git a/spring-cloud-alibaba-examples/governance-example/authentication-example/istio-authentication-provider-mvc-example/src/main/java/com/alibaba/cloud/examples/AuthApplication.java b/spring-cloud-alibaba-examples/governance-example/authentication-example/istio-authentication-provider-mvc-example/src/main/java/com/alibaba/cloud/examples/AuthApplication.java new file mode 100644 index 000000000..21b0347cc --- /dev/null +++ b/spring-cloud-alibaba-examples/governance-example/authentication-example/istio-authentication-provider-mvc-example/src/main/java/com/alibaba/cloud/examples/AuthApplication.java @@ -0,0 +1,29 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.examples; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class AuthApplication { + + public static void main(String[] args) { + SpringApplication.run(AuthApplication.class, args); + } + +} diff --git a/spring-cloud-alibaba-examples/governance-example/authentication-example/istio-authentication-provider-mvc-example/src/main/java/com/alibaba/cloud/examples/AuthMvcController.java b/spring-cloud-alibaba-examples/governance-example/authentication-example/istio-authentication-provider-mvc-example/src/main/java/com/alibaba/cloud/examples/AuthMvcController.java new file mode 100644 index 000000000..84944252d --- /dev/null +++ b/spring-cloud-alibaba-examples/governance-example/authentication-example/istio-authentication-provider-mvc-example/src/main/java/com/alibaba/cloud/examples/AuthMvcController.java @@ -0,0 +1,38 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.examples; + +import javax.servlet.http.HttpServletRequest; + +import lombok.extern.slf4j.Slf4j; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@Slf4j +public class AuthMvcController { + + @RequestMapping("/auth") + public String auth(HttpServletRequest request) { + String resp = "received request from " + request.getRemoteHost() + + ", local addr is " + request.getLocalAddr() + ", local host is " + + request.getLocalName() + ", request path is" + request.getRequestURI(); + return resp; + } + +} diff --git a/spring-cloud-alibaba-examples/governance-example/authentication-example/istio-authentication-provider-mvc-example/src/main/resources/application.yml b/spring-cloud-alibaba-examples/governance-example/authentication-example/istio-authentication-provider-mvc-example/src/main/resources/application.yml new file mode 100644 index 000000000..1a509245b --- /dev/null +++ b/spring-cloud-alibaba-examples/governance-example/authentication-example/istio-authentication-provider-mvc-example/src/main/resources/application.yml @@ -0,0 +1,15 @@ +server: + port: ${SERVER_PORT:80} +spring: + cloud: + governance: + auth: + enabled: ${ISTIO_AUTH_ENABLE:true} + istio: + config: + enabled: ${ISTIO_CONFIG_ENABLE:true} + host: ${ISTIOD_ADDR:127.0.0.1} + port: ${ISTIOD_PORT:15010} + polling-pool-size: ${POLLING_POOL_SIZE:10} + polling-time: ${POLLING_TIME:10} + istiod-token: ${ISTIOD_TOKEN:} \ No newline at end of file diff --git a/spring-cloud-alibaba-examples/governance-example/authentication-example/istio-authentication-provider-webflux-example/pom.xml b/spring-cloud-alibaba-examples/governance-example/authentication-example/istio-authentication-provider-webflux-example/pom.xml new file mode 100644 index 000000000..06722ed58 --- /dev/null +++ b/spring-cloud-alibaba-examples/governance-example/authentication-example/istio-authentication-provider-webflux-example/pom.xml @@ -0,0 +1,46 @@ + + + + + com.alibaba.cloud + spring-cloud-alibaba-examples + ${revision} + ../../../pom.xml + + 4.0.0 + + istio-authentication-provider-webflux-example + Spring Cloud Starter Alibaba Authentication Provider WebFlux Example + Example demonstrating how to use authentication on spring webflux application + jar + + + + org.springframework.boot + spring-boot-starter-webflux + + + com.alibaba.cloud + spring-cloud-starter-alibaba-governance-auth + + + spring-web + 5.2.15.RELEASE + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-controlplane-istio + + + spring-web + 5.2.15.RELEASE + + + + + + + diff --git a/spring-cloud-alibaba-examples/governance-example/authentication-example/istio-authentication-provider-webflux-example/src/main/java/com/alibaba/cloud/examples/AuthWebFluxController.java b/spring-cloud-alibaba-examples/governance-example/authentication-example/istio-authentication-provider-webflux-example/src/main/java/com/alibaba/cloud/examples/AuthWebFluxController.java new file mode 100644 index 000000000..de12bf9f7 --- /dev/null +++ b/spring-cloud-alibaba-examples/governance-example/authentication-example/istio-authentication-provider-webflux-example/src/main/java/com/alibaba/cloud/examples/AuthWebFluxController.java @@ -0,0 +1,40 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.examples; + +import reactor.core.publisher.Mono; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.server.ServerWebExchange; + +@RestController +public class AuthWebFluxController { + + @RequestMapping("/auth") + public Mono auth(ServerWebExchange request) { + String resp = "received request from " + + request.getRequest().getRemoteAddress().getAddress().getHostAddress() + + ", local addr is " + + request.getRequest().getLocalAddress().getAddress().getHostAddress() + + ", local host is " + + request.getRequest().getLocalAddress().getAddress().getHostName() + + ", request path is" + request.getRequest().getURI().getPath(); + return Mono.just(resp); + } + +} diff --git a/spring-cloud-alibaba-examples/governance-example/authentication-example/istio-authentication-provider-webflux-example/src/main/java/com/alibaba/cloud/examples/ScaAuthReactiveApplication.java b/spring-cloud-alibaba-examples/governance-example/authentication-example/istio-authentication-provider-webflux-example/src/main/java/com/alibaba/cloud/examples/ScaAuthReactiveApplication.java new file mode 100644 index 000000000..7a207a7b5 --- /dev/null +++ b/spring-cloud-alibaba-examples/governance-example/authentication-example/istio-authentication-provider-webflux-example/src/main/java/com/alibaba/cloud/examples/ScaAuthReactiveApplication.java @@ -0,0 +1,29 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.examples; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class ScaAuthReactiveApplication { + + public static void main(String[] args) { + SpringApplication.run(ScaAuthReactiveApplication.class, args); + } + +} diff --git a/spring-cloud-alibaba-examples/governance-example/authentication-example/istio-authentication-provider-webflux-example/src/main/resources/application.yml b/spring-cloud-alibaba-examples/governance-example/authentication-example/istio-authentication-provider-webflux-example/src/main/resources/application.yml new file mode 100644 index 000000000..1a509245b --- /dev/null +++ b/spring-cloud-alibaba-examples/governance-example/authentication-example/istio-authentication-provider-webflux-example/src/main/resources/application.yml @@ -0,0 +1,15 @@ +server: + port: ${SERVER_PORT:80} +spring: + cloud: + governance: + auth: + enabled: ${ISTIO_AUTH_ENABLE:true} + istio: + config: + enabled: ${ISTIO_CONFIG_ENABLE:true} + host: ${ISTIOD_ADDR:127.0.0.1} + port: ${ISTIOD_PORT:15010} + polling-pool-size: ${POLLING_POOL_SIZE:10} + polling-time: ${POLLING_TIME:10} + istiod-token: ${ISTIOD_TOKEN:} \ No newline at end of file diff --git a/spring-cloud-alibaba-examples/governance-example/authentication-example/readme-zh.md b/spring-cloud-alibaba-examples/governance-example/authentication-example/readme-zh.md new file mode 100644 index 000000000..1fa4a1e2b --- /dev/null +++ b/spring-cloud-alibaba-examples/governance-example/authentication-example/readme-zh.md @@ -0,0 +1,207 @@ +# Istio Authentication Example + +## 项目说明 + +本项目演示如何使用 Istio 下发鉴权配置到Spring Cloud Alibaba并对应用做鉴权。Spring Cloud Alibaba鉴权模块同时支持对Spring MVC以及Spring WebFlux应用做鉴权。 + +## 准备 +### 安装K8s环境 +请参考K8s的[安装工具](https://kubernetes.io/zh-cn/docs/tasks/tools/)小节。 +### 在K8s上安装并启用Istio +请参考Istio官方文档的[安装](https://istio.io/latest/zh/docs/setup/install/)小节。 + +## Istio鉴权规则介绍 +- [授权概述](https://istio.io/latest/zh/docs/concepts/security/#authorization) +- [具体配置方法](https://istio.io/latest/zh/docs/reference/config/security/) + +## 示例 +### 如何接入 +在启动示例进行演示之前,我们先了解一下 Spring Cloud 应用如何接入Istio并提供鉴权功能。 注意 本章节只是为了便于您理解接入方式,本示例代码中已经完成接入工作,您无需再进行修改。 +1. 修改`pom.xml`文件,引入Istio资源转换以及Spring Cloud Alibaba鉴权模块: + +```xml + + com.alibaba.cloud + spring-cloud-starter-alibaba-governance-auth + + + com.alibaba.cloud + spring-cloud-starter-alibaba-controlplane-istio + +``` +2. 在应用的 `src/main/resources/application.yml` 配置文件中配置Istio相关元数据: + +```yml +server: + port: ${SERVER_PORT:80} +spring: + cloud: + governance: + auth: + enabled: ${ISTIO_AUTH_ENABLE:true} + istio: + config: + enabled: ${ISTIO_CONFIG_ENABLE:true} + host: ${ISTIOD_ADDR:127.0.0.1} + port: ${ISTIOD_PORT:15010} + polling-pool-size: ${POLLING_POOL_SIZE:10} + polling-time: ${POLLING_TIMEOUT:10} + istiod-token: ${ISTIOD_TOKEN:} + log-xds: ${LOG_XDS:true} +``` +下面解释一下各字段的含义: +|配置项|key|默认值|说明 +|--|--|--|--| +|是否开启鉴权| spring.cloud.governance.auth.enabled|true| +|是否连接Istio获取鉴权配置| spring.cloud.istio.config.enabled|true| +|Istiod的地址| spring.cloud.istio.config.host|127.0.0.1| +|Istiod的端口| spring.cloud.istio.config.port|15012|注:连接15010端口无需TLS,连接15012端口需TLS认证| +|SCA去Istio拉取配置的线程池大小| spring.cloud.istio.config.polling-pool-size|10| +|SCA去Istio拉取配置的间隔时间| spring.cloud.istio.config.polling-time|30|单位为秒 +|连接Istio
15012端口时使用的JWT token| spring.cloud.istio.config.istiod-token|应用所在pod的`/var/run/secrets/tokens/istio-token`文件的内容| +|是否打印xDS相关日志| spring.cloud.istio.config.log-xds|true| +### 运行应用 +需要将应用运行在K8s环境中,并给运行的应用将K8s的一些元信息注入以下环境变量中: +|环境变量名|K8s pod metadata name| +|--|--| +|POD_NAME|metadata.name| +|NAMESPACE_NAME|metadata.namespace| + +**注:您部署的应用所在的pod不需要被Istio执行自动注入,因为Spring Cloud Alibaba的各个治理模块将会被用来替代Envoy Proxy的各种功能。** +### 效果演示 +下面给出几个简单的鉴权规则配置的示例: +#### IP黑白名单 +我们在使用如下命令通过Istio下发一条鉴权规则至demo应用,这条规则的限制了访问该应用的来源IP: +``` +kubectl apply -f - << EOF +apiVersion: security.istio.io/v1beta1 +kind: AuthorizationPolicy +metadata: + name: from-ip-allow + namespace: ${namespace_name} +spec: + selector: + matchLabels: + app: ${app_name} + action: DENY + rules: + - from: + - source: + ipBlocks: ["127.0.0.1"] +EOF +``` +可以通过请求本demo的auth接口来验证规则是否生效: +``` +curl --location --request GET '${demo_ip}/auth' +``` +在本例中,若请求的来源IP为`127.0.0.1`,则本应用返回: +``` +Auth failed, please check the request and auth rule +``` +说明此请求被拒绝。
+若请求的来源IP不为`127.0.0.1`,则本应用返回: +``` +received request from ${from_ip}, local addr is ${local_ip}, local host is ${local_host}, request path is/auth +``` +说明通过了SCA的鉴权,将会返回此请求的一些元信息。 + +在此之后,我们删除这条IP黑白名单的鉴权规则: +```shell +kubectl delete AuthorizationPolicy from-ip-allow -n ${namespace_name} +``` +之后再次请求本demo的auth接口,可以发现,因为鉴权规则已被删除,所以本应用将会返回: +``` +received request from ${from_ip}, local addr is ${local_ip}, local host is ${local_host}, request path is/auth +``` + +#### 请求头认证 +我们在使用如下命令通过Istio下发一条鉴权规则至demo应用,这条规则的限制了访问该应用的请求header: +``` +kubectl apply -f - << EOF +apiVersion: security.istio.io/v1beta1 +kind: AuthorizationPolicy +metadata: + name: http-headers-allow + namespace: ${namespace_name} +spec: + selector: + matchLabels: + app: ${app_name} + action: ALLOW + rules: + - when: + - key: request.headers[User-Agent] + values: ["PostmanRuntime/*"] +EOF +``` +之后发送一个带User-Agent头部的HTTP请求来验证规则是否生效: +``` +curl --location --request GET '${demo_ip}/auth' \ +--header 'User-Agent: PostmanRuntime/7.29.2' +``` +由于此请求由于携带了正确的HTTP Header信息,将会返回: +``` +received request from ${from_ip}, local addr is ${local_ip}, local host is ${local_host}, request path is/auth +``` +之后发送一个不带User-Agent头部的HTTP请求来验证规则是否生效: +``` +curl --location --request GET '${demo_ip}/auth' +``` +由于此请求没有携带正确的HTTP Header信息,将会返回: +``` +Auth failed, please check the request and auth rule +``` +在此之后,我们删除这条请求头认证的规则: +```shell +kubectl delete AuthorizationPolicy http-headers-allow -n ${namespace_name} +``` +之后再次请求本demo的auth接口,可以发现,因为鉴权规则已被删除,所以本应用将会返回: +``` +received request from ${from_ip}, local addr is ${local_ip}, local host is ${local_host}, request path is/auth +``` + +#### JWT认证 +我们使用如下命令通过Istio下发一条鉴权规则至demo应用,这条规则限制了访问该应用需要携带的JWT token value: +``` +kubectl apply -f - < + com.alibaba.cloud + spring-cloud-starter-alibaba-governance-auth + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-controlplane-istio + +``` +2. Configure Istio related metadata in the `src/main/resources/application` yml configuration file: + +```yml +server: + port: ${SERVER_PORT:80} +spring: + cloud: + governance: + auth: + enabled: ${ISTIO_AUTH_ENABLE:true} + istio: + config: + enabled: ${ISTIO_CONFIG_ENABLE:true} + host: ${ISTIOD_ADDR:127.0.0.1} + port: ${ISTIOD_PORT:15010} + polling-pool-size: ${POLLING_POOL_SIZE:10} + polling-time: ${POLLING_TIMEOUT:10} + istiod-token: ${ISTIOD_TOKEN:} + log-xds: ${LOG_XDS:true} +``` +Here's an explanation of each field: +|Configuration Item|key|Default Value|Description +|--|--|--|--| +|Whether to enable authentication| spring.cloud.governance.auth.enabled|true| +|Whether to connect to Istio to obtain authentication configuration| spring.cloud.istio.config.enabled|true| +|Host of Istiod| spring.cloud.istio.config.host|127.0.0.1| +|Port of Istiod| spring.cloud.istio.config.port|15012|15010 port does not need TLS,but 15012 does +|Thread pool size for SCA to pull the config| spring.cloud.istio.config.polling-pool-size|10| +|Time interval for SCA to pull the config| spring.cloud.istio.config.polling-time|30|The unit is second| +|JWT token for SCA to connect to 15012 port| spring.cloud.istio.config.istiod-token|Content of file `/var/run/secrets/tokens/istio-token` in the pod of application| +|Whether to print logs about xDS| spring.cloud.istio.config.log-xds|true| + +### Run the application +You need to run the application in the K8s environment and inject some meta information about K8s into the following environment variables for the running application: +|Environment variable name|K8s pod metadata name| +|--|--| +|POD_NAME|metadata.name| +|NAMESPACE_NAME|metadata.namespace| + +**HINT:The POD in which your deployed application does not need to be automatically injected by Istio because the various governance modules of Spring Cloud Alibaba will be used to replace the functions of the Envoy Proxy.** +### Demostration +The following are some simple examples of authentication rule configurations: +#### IP Blocks +The following command is used to deliver an authentication rule to the demo application through Istio. This rule restricts the source IP addresses that can access the application: +``` +kubectl apply -f - << EOF +apiVersion: security.istio.io/v1beta1 +kind: AuthorizationPolicy +metadata: + name: from-ip-allow + namespace: ${namespace_name} +spec: + selector: + matchLabels: + app: ${app_name} + action: DENY + rules: + - from: + - source: + ipBlocks: ["127.0.0.1"] +EOF +``` +You can validate the rules by sending request to the auth interface of this demo: +``` +curl --location --request GET '${demo_ip}/auth' +``` +In this example, if the source IP of the request is 127.0.0.1, then the application returns: +``` +Auth failed, please check the request and auth rule +``` +This indicates that the request is denied.
+If the source IP of the request is not '127.0.0.1', then the application returns: +``` +received request from ${from_ip}, local addr is ${local_ip}, local host is ${local_host}, request path is/auth +``` +It indicates that the request has been authenticated by SCA and some meta data of the request will be returned. + +After that, we delete the authentication rule for the IP Blocks: +```shell +kubectl delete AuthorizationPolicy from-ip-allow -n ${namespace_name} +``` +Then request the auth interface of this demo again, we can find that the application will return the following message because the authentication rule has been deleted: +``` +received request from ${from_ip}, local addr is ${local_ip}, local host is ${local_host}, request path is/auth +``` + +#### Request Header Authentication +We use the following command to deliver an authentication rule to the demo application through Istio. This rule restricts the request header for accessing the application: +``` +kubectl apply -f - << EOF +apiVersion: security.istio.io/v1beta1 +kind: AuthorizationPolicy +metadata: + name: http-headers-allow + namespace: ${namespace_name} +spec: + selector: + matchLabels: + app: ${app_name} + action: ALLOW + rules: + - when: + - key: request.headers[User-Agent] + values: ["PostmanRuntime/*"] +EOF +``` +Then send a HTTP request with a user-agent header to verify whether the rule is valid: +``` +curl --location --request GET '${demo_ip}/auth' \ +--header 'User-Agent: PostmanRuntime/7.29.2' +``` +Since this request carries a correct HTTP Header, it will return: +``` +received request from ${from_ip}, local addr is ${local_ip}, local host is ${local_host}, request path is/auth +``` +Then send a HTTP request without a user-agent header to verify whether the rule is valid: +``` +curl --location --request GET '${demo_ip}/auth' +``` +Since this request don't carry a correct HTTP Header, it will return: +``` +Auth failed, please check the request and auth rule +``` + +After that, we remove the rule for requests header authentication: +```shell +kubectl delete AuthorizationPolicy http-headers-allow -n ${namespace_name} +``` +Then request the auth interface of this demo again, we can find that the application will return the following message because the authentication rule has been deleted: +``` +received request from ${from_ip}, local addr is ${local_ip}, local host is ${local_host}, request path is/auth +``` + +#### JWT Authentication +We use the following command to deliver an authentication rule to the demo application through Istio. This rule restricts the JWT token value that must be carried to access the application: +``` +kubectl apply -f - < + + + + com.alibaba.cloud + spring-cloud-alibaba-examples + ${revision} + ../../../pom.xml + + 4.0.0 + + consumer-example + Spring Cloud Starter Alibaba Label Routing Consumer Example + Example demonstrating how to use label routing consumer + jar + + + + org.springframework.boot + spring-boot-starter-web + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-governance-routing + + + + diff --git a/spring-cloud-alibaba-examples/governance-example/label-routing-example/consumer-example/src/main/java/com/alibaba/cloud/examples/ConsumerApplication.java b/spring-cloud-alibaba-examples/governance-example/label-routing-example/consumer-example/src/main/java/com/alibaba/cloud/examples/ConsumerApplication.java new file mode 100644 index 000000000..b34e3a12d --- /dev/null +++ b/spring-cloud-alibaba-examples/governance-example/label-routing-example/consumer-example/src/main/java/com/alibaba/cloud/examples/ConsumerApplication.java @@ -0,0 +1,178 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.examples; + +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.cloud.commons.governance.event.LabelRoutingDataChangedEvent; +import com.alibaba.cloud.commons.governance.labelrouting.LabelRouteRule; +import com.alibaba.cloud.commons.governance.labelrouting.MatchService; +import com.alibaba.cloud.commons.governance.labelrouting.UnifiedRouteDataStructure; +import com.alibaba.cloud.commons.governance.labelrouting.rule.HeaderRule; +import com.alibaba.cloud.commons.governance.labelrouting.rule.RouteRule; +import com.alibaba.cloud.commons.governance.labelrouting.rule.UrlRule; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author HH + */ +@SpringBootApplication +@EnableDiscoveryClient(autoRegister = true) +@EnableFeignClients +public class ConsumerApplication { + + public static void main(String[] args) { + SpringApplication.run(ConsumerApplication.class, args); + } + + @FeignClient(name = "service-provider") + public interface FeignService { + + /** + * Feign test. + * @return String + */ + @GetMapping("/test") + String test(); + + } + + @RestController + public class Controller implements ApplicationContextAware { + + @Autowired + private ApplicationContext applicationContext; + + @Autowired + private ConsumerApplication.FeignService feignService; + + @Override + public void setApplicationContext(ApplicationContext applicationContext) + throws BeansException { + this.applicationContext = applicationContext; + } + + @GetMapping("/router-test") + public String notFound() { + return feignService.test(); + } + + @GetMapping("/add") + public void getDataFromControlPlaneTest() { + List routeRules = new ArrayList<>(); + List matchServices = new ArrayList<>(); + + UnifiedRouteDataStructure unifiedRouteDataStructure = new UnifiedRouteDataStructure(); + unifiedRouteDataStructure.setTargetService("service-provider"); + + LabelRouteRule labelRouteData = new LabelRouteRule(); + labelRouteData.setDefaultRouteVersion("v1"); + + RouteRule routeRule = new HeaderRule(); + routeRule.setType("header"); + routeRule.setCondition("="); + routeRule.setKey("tag"); + routeRule.setValue("gray"); + RouteRule routeRule1 = new UrlRule.Parameter(); + routeRule1.setType("parameter"); + routeRule1.setCondition(">"); + routeRule1.setKey("id"); + routeRule1.setValue("10"); + RouteRule routeRule2 = new UrlRule.Path(); + routeRule2.setType("path"); + routeRule2.setCondition("="); + routeRule2.setValue("/router-test"); + routeRules.add(routeRule); + routeRules.add(routeRule1); + routeRules.add(routeRule2); + + MatchService matchService = new MatchService(); + matchService.setVersion("v2"); + matchService.setWeight(100); + matchService.setRuleList(routeRules); + matchServices.add(matchService); + + labelRouteData.setMatchRouteList(matchServices); + + unifiedRouteDataStructure.setLabelRouteRule(labelRouteData); + + List unifiedRouteDataStructureList = new ArrayList<>(); + unifiedRouteDataStructureList.add(unifiedRouteDataStructure); + applicationContext.publishEvent(new LabelRoutingDataChangedEvent(this, + unifiedRouteDataStructureList)); + } + + @GetMapping("/update") + public void updateDataFromControlPlaneTest() { + List routeRules = new ArrayList<>(); + List matchServices = new ArrayList<>(); + + UnifiedRouteDataStructure unifiedRouteDataStructure = new UnifiedRouteDataStructure(); + unifiedRouteDataStructure.setTargetService("service-provider"); + + LabelRouteRule labelRouteData = new LabelRouteRule(); + labelRouteData.setDefaultRouteVersion("v1"); + + RouteRule routeRule = new HeaderRule(); + routeRule.setType("header"); + routeRule.setCondition("="); + routeRule.setKey("tag"); + routeRule.setValue("gray"); + RouteRule routeRule1 = new UrlRule.Parameter(); + routeRule1.setType("parameter"); + routeRule1.setCondition(">"); + routeRule1.setKey("id"); + routeRule1.setValue("10"); + RouteRule routeRule2 = new UrlRule.Path(); + routeRule2.setType("path"); + routeRule2.setCondition("="); + routeRule2.setValue("/router-test"); + routeRules.add(routeRule); + routeRules.add(routeRule1); + routeRules.add(routeRule2); + + MatchService matchService = new MatchService(); + matchService.setVersion("v2"); + matchService.setWeight(50); + matchService.setRuleList(routeRules); + matchServices.add(matchService); + + labelRouteData.setMatchRouteList(matchServices); + + unifiedRouteDataStructure.setLabelRouteRule(labelRouteData); + + List unifiedRouteDataStructureList = new ArrayList<>(); + unifiedRouteDataStructureList.add(unifiedRouteDataStructure); + applicationContext.publishEvent(new LabelRoutingDataChangedEvent(this, + unifiedRouteDataStructureList)); + } + + } + +} diff --git a/spring-cloud-alibaba-examples/governance-example/label-routing-example/consumer-example/src/main/resources/application.properties b/spring-cloud-alibaba-examples/governance-example/label-routing-example/consumer-example/src/main/resources/application.properties new file mode 100644 index 000000000..17eab0834 --- /dev/null +++ b/spring-cloud-alibaba-examples/governance-example/label-routing-example/consumer-example/src/main/resources/application.properties @@ -0,0 +1,8 @@ +spring.application.name=service-consumer +server.port=18083 +management.endpoints.web.exposure.include=* +spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848 +spring.cloud.nacos.discovery.fail-fast=true +spring.cloud.nacos.username=nacos +spring.cloud.nacos.password=nacos +#spring.cloud.governance.router.rule=RandomRule diff --git a/spring-cloud-alibaba-examples/governance-example/label-routing-example/default-provider-version-example/pom.xml b/spring-cloud-alibaba-examples/governance-example/label-routing-example/default-provider-version-example/pom.xml new file mode 100644 index 000000000..ac0535037 --- /dev/null +++ b/spring-cloud-alibaba-examples/governance-example/label-routing-example/default-provider-version-example/pom.xml @@ -0,0 +1,36 @@ + + + + + com.alibaba.cloud + spring-cloud-alibaba-examples + ${revision} + ../../../pom.xml + + 4.0.0 + + default-provider-version-example + Spring Cloud Starter Alibaba Label Routing Provider Example + Example demonstrating how to use label routing provider + jar + + + + org.springframework.boot + spring-boot-starter-web + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + + + diff --git a/spring-cloud-alibaba-examples/governance-example/label-routing-example/default-provider-version-example/src/main/java/com/alibaba/cloud/examples/ProviderApplication.java b/spring-cloud-alibaba-examples/governance-example/label-routing-example/default-provider-version-example/src/main/java/com/alibaba/cloud/examples/ProviderApplication.java new file mode 100644 index 000000000..1b6d0eb9e --- /dev/null +++ b/spring-cloud-alibaba-examples/governance-example/label-routing-example/default-provider-version-example/src/main/java/com/alibaba/cloud/examples/ProviderApplication.java @@ -0,0 +1,55 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.examples; + +import com.alibaba.cloud.nacos.registry.NacosRegistration; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author HH + */ +@EnableDiscoveryClient +@SpringBootApplication +public class ProviderApplication { + + public static void main(String[] args) { + SpringApplication.run(ProviderApplication.class, args); + } + + @Autowired + NacosRegistration nacosRegistration; + + @RestController + class Controller { + + @GetMapping("/test") + public String test() { + String host = nacosRegistration.getHost(); + int port = nacosRegistration.getPort(); + String version = nacosRegistration.getMetadata().get("version"); + return "Route in " + host + ": " + port + ",version is " + version + "."; + } + + } + +} diff --git a/spring-cloud-alibaba-examples/governance-example/label-routing-example/default-provider-version-example/src/main/resources/application.properties b/spring-cloud-alibaba-examples/governance-example/label-routing-example/default-provider-version-example/src/main/resources/application.properties new file mode 100644 index 000000000..b58fa91fa --- /dev/null +++ b/spring-cloud-alibaba-examples/governance-example/label-routing-example/default-provider-version-example/src/main/resources/application.properties @@ -0,0 +1,12 @@ +server.port=18081 +spring.application.name=service-provider +spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848 +spring.cloud.nacos.discovery.enabled=true +spring.cloud.nacos.discovery.metadata.version=v1 + + +spring.cloud.nacos.username=nacos +spring.cloud.nacos.password=nacos + +management.endpoints.web.exposure.include=* +management.endpoint.health.show-details=always diff --git a/spring-cloud-alibaba-examples/governance-example/label-routing-example/istio-consumer-example/pom.xml b/spring-cloud-alibaba-examples/governance-example/label-routing-example/istio-consumer-example/pom.xml new file mode 100644 index 000000000..504e59191 --- /dev/null +++ b/spring-cloud-alibaba-examples/governance-example/label-routing-example/istio-consumer-example/pom.xml @@ -0,0 +1,45 @@ + + + + + com.alibaba.cloud + spring-cloud-alibaba-examples + ${revision} + ../../../pom.xml + + 4.0.0 + + label-routing-istio-consumer-example + Spring Cloud Starter Alibaba Istio Label Routing Consumer Example + Example demonstrating how to use Istio label routing consumer + jar + + + + org.springframework.boot + spring-boot-starter-web + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-governance-routing + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-controlplane-istio + + + + diff --git a/spring-cloud-alibaba-examples/governance-example/label-routing-example/istio-consumer-example/src/main/java/com/alibaba/cloud/examples/IstioConsumerApplication.java b/spring-cloud-alibaba-examples/governance-example/label-routing-example/istio-consumer-example/src/main/java/com/alibaba/cloud/examples/IstioConsumerApplication.java new file mode 100644 index 000000000..12723a84a --- /dev/null +++ b/spring-cloud-alibaba-examples/governance-example/label-routing-example/istio-consumer-example/src/main/java/com/alibaba/cloud/examples/IstioConsumerApplication.java @@ -0,0 +1,65 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.examples; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author HH + */ +@SpringBootApplication +@EnableDiscoveryClient(autoRegister = true) +@EnableFeignClients +public class IstioConsumerApplication { + + public static void main(String[] args) { + SpringApplication.run(IstioConsumerApplication.class, args); + } + + @FeignClient(name = "service-provider") + public interface FeignService { + + /** + * Feign test. + * @return String + */ + @GetMapping("/test") + String test(); + + } + + @RestController + public class Controller { + + @Autowired + private IstioConsumerApplication.FeignService feignService; + + @GetMapping("/istio-label-routing") + public String labelRouting() { + return feignService.test(); + } + + } + +} diff --git a/spring-cloud-alibaba-examples/governance-example/label-routing-example/istio-consumer-example/src/main/resources/application.yml b/spring-cloud-alibaba-examples/governance-example/label-routing-example/istio-consumer-example/src/main/resources/application.yml new file mode 100644 index 000000000..fab651433 --- /dev/null +++ b/spring-cloud-alibaba-examples/governance-example/label-routing-example/istio-consumer-example/src/main/resources/application.yml @@ -0,0 +1,25 @@ +server: + port: 18084 +spring: + main: + allow-bean-definition-overriding: true + application: + name: service-consumer + cloud: + nacos: + discovery: + server-addr: 127.0.0.1:8848 + fail-fast: true + username: nacos + password: nacos + governance: + auth: + enabled: ${ISTIO_AUTH_ENABLE:false} + istio: + config: + enabled: ${ISTIO_CONFIG_ENABLE:true} + host: ${ISTIOD_ADDR:127.0.0.1} + port: ${ISTIOD_PORT:15010} + polling-pool-size: ${POLLING_POOL_SIZE:10} + polling-time: ${POLLING_TIME:10} + istiod-token: ${ISTIOD_TOKEN:} \ No newline at end of file diff --git a/spring-cloud-alibaba-examples/governance-example/label-routing-example/opensergo-consumer-example/pom.xml b/spring-cloud-alibaba-examples/governance-example/label-routing-example/opensergo-consumer-example/pom.xml new file mode 100644 index 000000000..f4b3bafa2 --- /dev/null +++ b/spring-cloud-alibaba-examples/governance-example/label-routing-example/opensergo-consumer-example/pom.xml @@ -0,0 +1,45 @@ + + + + + com.alibaba.cloud + spring-cloud-alibaba-examples + ${revision} + ../../../pom.xml + + 4.0.0 + + opensergo-consumer-example + Spring Cloud Starter Alibaba OpenSergo Label Routing Consumer Example + Example demonstrating how to use OpenSergo label routing consumer + jar + + + + org.springframework.boot + spring-boot-starter-web + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-governance-routing + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-controlplane-opensergo + + + + diff --git a/spring-cloud-alibaba-examples/governance-example/label-routing-example/opensergo-consumer-example/src/main/java/com/alibaba/cloud/examples/OpenSergoConsumerApplication.java b/spring-cloud-alibaba-examples/governance-example/label-routing-example/opensergo-consumer-example/src/main/java/com/alibaba/cloud/examples/OpenSergoConsumerApplication.java new file mode 100644 index 000000000..f2cec8283 --- /dev/null +++ b/spring-cloud-alibaba-examples/governance-example/label-routing-example/opensergo-consumer-example/src/main/java/com/alibaba/cloud/examples/OpenSergoConsumerApplication.java @@ -0,0 +1,65 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.examples; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author HH + */ +@SpringBootApplication +@EnableDiscoveryClient(autoRegister = true) +@EnableFeignClients +public class OpenSergoConsumerApplication { + + public static void main(String[] args) { + SpringApplication.run(OpenSergoConsumerApplication.class, args); + } + + @FeignClient(name = "service-provider") + public interface FeignService { + + /** + * Feign test. + * @return String + */ + @GetMapping("/test") + String test(); + + } + + @RestController + public class Controller { + + @Autowired + private OpenSergoConsumerApplication.FeignService feignService; + + @GetMapping("/router-test") + public String labelRouting() { + return feignService.test(); + } + + } + +} diff --git a/spring-cloud-alibaba-examples/governance-example/label-routing-example/opensergo-consumer-example/src/main/resources/application.properties b/spring-cloud-alibaba-examples/governance-example/label-routing-example/opensergo-consumer-example/src/main/resources/application.properties new file mode 100644 index 000000000..841777bbf --- /dev/null +++ b/spring-cloud-alibaba-examples/governance-example/label-routing-example/opensergo-consumer-example/src/main/resources/application.properties @@ -0,0 +1,8 @@ +spring.application.name=service-consumer +server.port=18083 +management.endpoints.web.exposure.include=* +spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848 +spring.cloud.nacos.discovery.fail-fast=true +spring.cloud.nacos.username=nacos +spring.cloud.nacos.password=nacos +spring.cloud.opensergo.endpoint=127.0.0.1:10246 diff --git a/spring-cloud-alibaba-examples/governance-example/label-routing-example/provider-version-example/pom.xml b/spring-cloud-alibaba-examples/governance-example/label-routing-example/provider-version-example/pom.xml new file mode 100644 index 000000000..c9a985f0e --- /dev/null +++ b/spring-cloud-alibaba-examples/governance-example/label-routing-example/provider-version-example/pom.xml @@ -0,0 +1,36 @@ + + + + + com.alibaba.cloud + spring-cloud-alibaba-examples + ${revision} + ../../../pom.xml + + 4.0.0 + + provider-version-example + Spring Cloud Starter Alibaba Label Routing Provider Example + Example demonstrating how to use label routing provider + jar + + + + org.springframework.boot + spring-boot-starter-web + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + + + diff --git a/spring-cloud-alibaba-examples/governance-example/label-routing-example/provider-version-example/src/main/java/com/alibaba/cloud/examples/ProviderApplication.java b/spring-cloud-alibaba-examples/governance-example/label-routing-example/provider-version-example/src/main/java/com/alibaba/cloud/examples/ProviderApplication.java new file mode 100644 index 000000000..1b6d0eb9e --- /dev/null +++ b/spring-cloud-alibaba-examples/governance-example/label-routing-example/provider-version-example/src/main/java/com/alibaba/cloud/examples/ProviderApplication.java @@ -0,0 +1,55 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.examples; + +import com.alibaba.cloud.nacos.registry.NacosRegistration; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author HH + */ +@EnableDiscoveryClient +@SpringBootApplication +public class ProviderApplication { + + public static void main(String[] args) { + SpringApplication.run(ProviderApplication.class, args); + } + + @Autowired + NacosRegistration nacosRegistration; + + @RestController + class Controller { + + @GetMapping("/test") + public String test() { + String host = nacosRegistration.getHost(); + int port = nacosRegistration.getPort(); + String version = nacosRegistration.getMetadata().get("version"); + return "Route in " + host + ": " + port + ",version is " + version + "."; + } + + } + +} diff --git a/spring-cloud-alibaba-examples/governance-example/label-routing-example/provider-version-example/src/main/resources/application.properties b/spring-cloud-alibaba-examples/governance-example/label-routing-example/provider-version-example/src/main/resources/application.properties new file mode 100644 index 000000000..145bdffb2 --- /dev/null +++ b/spring-cloud-alibaba-examples/governance-example/label-routing-example/provider-version-example/src/main/resources/application.properties @@ -0,0 +1,11 @@ +server.port=18082 +spring.application.name=service-provider +spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848 +spring.cloud.nacos.discovery.enabled=true +spring.cloud.nacos.discovery.metadata.version=v2 + +spring.cloud.nacos.username=nacos +spring.cloud.nacos.password=nacos + +management.endpoints.web.exposure.include=* +management.endpoint.health.show-details=always diff --git a/spring-cloud-alibaba-examples/governance-example/label-routing-example/readme-zh.md b/spring-cloud-alibaba-examples/governance-example/label-routing-example/readme-zh.md new file mode 100644 index 000000000..8a52749d9 --- /dev/null +++ b/spring-cloud-alibaba-examples/governance-example/label-routing-example/readme-zh.md @@ -0,0 +1,440 @@ +# label route example + +## 项目说明 + +本项目演示如何使用 spring cloud ailbaba governance labelrouting 模块完成标签路由功能。 + +## 模块结构 + +本模块包括一个消费者实例和一个提供者集群,该集群包含着两个实例。 + +## 示例 + +### 如何接入 + +**注意 本章节只是为了便于您理解接入方式,本示例代码中已经完成接入工作,您无需再进行修改。** +1. 首先,修改需要进行路由服务的`pom.xml` 文件,引入 spring cloud ailbaba governance labelrouting依赖。 +```xml + + com.alibaba.cloud + spring-cloud-starter-alibaba-governance-routing + +``` + +### 应用启动 + +启动一个三个模块的启动类,分别为ConsumerApplication,两个ProviderApplication,将其注入到Nacos注册中心中。 + +### 效果演示 + +#### 规则说明 +实例中设置的规则如下: +```java +@GetMapping("/add") +public void getDataFromControlPlaneTest() { + List routeRules = new ArrayList<>(); + List matchServices = new ArrayList<>(); + UnifiedRouteDataStructure unifiedRouteDataStructure = new UntiedRouteDataStructure(); + unifiedRouteDataStructure.setTargetService("service-provider"); + LabelRouteRule labelRouteData = new LabelRouteRule(); + labelRouteData.setDefaultRouteVersion("v1"); + RouteRule routeRule = new HeaderRule(); + routeRule.setType("header"); + routeRule.setCondition("="); + routeRule.setKey("tag"); + routeRule.setValue("gray"); + RouteRule routeRule1 = new UrlRule.Parameter(); + routeRule1.setType("parameter"); + routeRule1.setCondition(">"); + routeRule1.setKey("id"); + routeRule1.setValue("10"); + RouteRule routeRule2 = new UrlRule.Path(); + routeRule2.setType("path"); + routeRule2.setCondition("="); + routeRule2.setValue("/router-test"); + routeRules.add(routeRule); + routeRules.add(routeRule1); + routeRules.add(routeRule2); + MatchService matchService = new MatchService(); + matchService.setVersion("v2"); + matchService.setWeight(100); + matchService.setRuleList(routeRules); + matchServices.add(matchService); + labelRouteData.setMatchRouteList(matchServices); + unifiedRouteDataStructure.setLabelRouteRule(labelRouteData); + List unifiedRouteDataStructureList = new ArrayList<>(); + unifiedRouteDataStructureList.add(unifiedRouteDataStructure); + controlPlaneConnection.pushRouteData(unifiedRouteDataStructureList); +} +``` +代码对应的规则如下: +若同时满足请求参数中含有tag=gray,请求头中含有id且值小于10,uri为/router-test则流量全部路由到v2版本中,若有一条不满足,则流量路由到v1版本中。 + +规则也支持动态修改,测试动态修改的规则如下: +```java +@GetMapping("/add") +public void getDataFromControlPlaneTest() { + List routeRules = new ArrayList<>(); + List matchServices = new ArrayList<>(); + UntiedRouteDataStructure unifiedRouteDataStructure = new UntiedRouteDataStructure(); + unifiedRouteDataStructure.setTargetService("service-provider"); + LabelRouteRule labelRouteData = new LabelRouteRule(); + labelRouteData.setDefaultRouteVersion("v1"); + + RouteRule routeRule = new HeaderRule(); + routeRule.setType("header"); + routeRule.setCondition("="); + routeRule.setKey("tag"); + routeRule.setValue("gray"); + RouteRule routeRule1 = new UrlRule.Parameter(); + routeRule1.setType("parameter"); + routeRule1.setCondition(">"); + routeRule1.setKey("id"); + routeRule1.setValue("10"); + + RouteRule routeRule2 = new UrlRule.Path(); + routeRule2.setType("path"); + routeRule2.setCondition("="); + routeRule2.setValue("/router-test"); + routeRules.add(routeRule); + routeRules.add(routeRule1); + routeRules.add(routeRule2); + + MatchService matchService = new MatchService(); + matchService.setVersion("v2"); + matchService.setWeight(50); + matchService.setRuleList(routeRules); + matchServices.add(matchService); + labelRouteData.setMatchRouteList(matchServices); + unifiedRouteDataStructure.setLabelRouteRule(labelRouteData); + List unifiedRouteDataStructureList = new ArrayList<>(); + unifiedRouteDataStructureList.add(unifiedRouteDataStructure); + controlPlaneConnection.pushRouteData(unifiedRouteDataStructureList); +} +``` +代码对应的规则如下: +若同时满足请求参数中含有tag=gray,请求头中含有id且值小于10,uri为/router-test,则50%流量路由到v2版本中,剩下的流量路由到v1版本中,若有一条不满足,则流量路由到v1版本中。 + +##### 演示步骤 +1. 访问 http://localhost:18083/add 将路由规则由控制面接口推入路由规则仓库中。 + 访问 http://localhost:18083/router-test 不满足路由规则,路由到v1版本中,v1版本实例打印返回如下结果: + ``` + Route in 30.221.132.228: 18081,version is v1. + ``` + 访问 http://localhost:18083/router-test?id=11 且请求头设置test值为gray 满足路由规则,路由到v2版本中,v2版本实例打印返回如下结果: + ``` + Route in 30.221.132.228: 18082,version is v2. + ``` + +2. 访问 http://localhost:18083/update 模拟动态修改路由规则。 + 访问 http://localhost:18083/router-test 不满足路由规则,路由到v1版本中,v1版本实例打印返回如下结果: + ``` + Route in 30.221.132.228: 18081,version is v1. + ``` + 访问 http://localhost:18083/router-test?id=11 且请求头设置test值为gray 满足路由规则,50%路由到v2版本中,v2版本实例打印返回如下结果: + ``` + Route in 30.221.132.228: 18082,version is v2. + ``` + 50%路由到v1版本中,v1版本实例打印返回如下结果: + ``` + Route in 30.221.132.228: 18081,version is v1. + ``` + +3. 如果不推送规则,走正常路由 + +## 集成Istio +**注意 本章节只是为了便于您理解接入方式,本示例代码中已经完成接入工作,您无需再进行修改。** +### 安装K8s环境 +请参考K8s的[安装工具](https://kubernetes.io/zh-cn/docs/tasks/tools/)小节。 +### 在K8s上安装并启用Istio +请参考Istio官方文档的[安装](https://istio.io/latest/zh/docs/setup/install/)小节。 +### Istio流量治理规则介绍 +- [VirtualService](https://istio.io/latest/zh/docs/reference/config/networking/virtual-service/) +- [DestinationRule](https://istio.io/latest/zh/docs/reference/config/networking/destination-rule/) +### 配置 +1. 首先,修改pom.xml 文件,引入`spring-cloud-starter-alibaba-governance-routing`依赖。同时引入Spring Cloud Alibaba的`spring-cloud-starter-alibaba-controlplane-istio`模块 +```xml + + com.alibaba.cloud + spring-cloud-starter-alibaba-governance-routing + + + com.alibaba.cloud + spring-cloud-starter-alibaba-controlplane-istio + +``` +2. 在`src/main/resources/application.yml`配置文件中配置Istio控制面的相关信息: +``` +server: + port: 18084 +spring: + main: + allow-bean-definition-overriding: true + application: + name: service-consumer + cloud: + nacos: + discovery: + server-addr: 127.0.0.1:8848 + fail-fast: true + username: nacos + password: nacos + governance: + auth: + # 是否开启鉴权 + enabled: ${ISTIO_AUTH_ENABLE:false} + istio: + config: + # 是否开启Istio配置转换 + enabled: ${ISTIO_CONFIG_ENABLE:true} + # Istiod ip + host: ${ISTIOD_ADDR:127.0.0.1} + # Istiod 端口 + port: ${ISTIOD_PORT:15010} + # 轮询Istio线程池大小 + polling-pool-size: ${POLLING_POOL_SIZE:10} + # 轮询Istio时间间隔 + polling-time: ${POLLING_TIME:10} + # Istiod鉴权token(访问Istiod 15012端口时可用) + istiod-token: ${ISTIOD_TOKEN:} + # 是否打印xds相关日志 + log-xds: ${LOG_XDS:true} +``` +### 应用启动 +启动三个模块的启动类,分别为IstioConsumerApplication,两个ProviderApplication,将其注入到Nacos注册中心中。 + +### 下发配置 +我们通过Istio控制面下发标签路由规则,首先下发DestinationRule规则: +``` +kubectl apply -f - << EOF +apiVersion: networking.istio.io/v1alpha3 +kind: DestinationRule +metadata: + name: my-destination-rule +spec: + host: sca-virtual-service + subsets: + - name: v1 + labels: + version: v1 + - name: v2 + labels: + version: v2 +EOF +``` +此规则将后端服务拆分为两个版本,label为v1的pod被分到v1版本,label为v2的pod被分到v2版本 +之后,我们下发VirtualService规则: +``` +kubectl apply -f - << EOF +apiVersion: networking.istio.io/v1alpha3 +kind: VirtualService +metadata: + name: sca-virtual-service +spec: + hosts: + - service-provider + http: + - match: + - headers: + tag: + exact: gray + uri: + exact: /istio-label-routing + route: + - destination: + host: service-provider + subset: v2 + - route: + - destination: + host: service-provider + subset: v1 +EOF +``` +这条VirtualService指定了一条最简单的标签路由规则,将请求头tag为gray,请求路径为/istio-label-routing的HTTP请求路由到v2版本,其余的流量都路由到v1版本: +### 效果演示 +我们发送一条不带请求头的HTTP请求至IstioConsumerApplication: +``` +curl --location --request GET '127.0.0.1:18084/istio-label-routing' +``` +因为请求头不为gray,所以请求将会被路由到v1版本,返回如下: +``` +Route in 30.221.132.228: 18081,version is v1. +``` +之后我们发送一条请求头tag为gray,且请求路径为/istio-label-routing的HTTP请求: +``` +curl --location --request GET '127.0.0.1:18084/istio-label-routing' --header 'tag: gray' +``` +因为满足路由规则,所以请求会被路由至v2版本: +``` +Route in 30.221.132.228: 18082,version is v2. +``` +最后我们删除这条标签路由规则: +```shell +kubectl delete VirtualService sca-virtual-service +kubectl delete DestinationRule my-destination-rule +``` +删除规则后,可以看到路由的策略将不由请求头的携带与否来决定,而是完全遵从于负载均衡器的实现。 + +## 集成OpenSergo +**注意 本章节只是为了便于您理解接入方式,本示例代码中已经完成接入工作,您无需再进行修改。** +1. 首先,修改`pom.xml` 文件,引入`spring-cloud-starter-alibaba-governance-routing`依赖。同时引入Spring Cloud Alibaba的`spring-cloud-starter-alibaba-controlplane-opensergo`模块 +``` + + com.alibaba.cloud + spring-cloud-starter-alibaba-governance-routing + + + com.alibaba.cloud + spring-cloud-starter-alibaba-controlplane-opensergo + +``` +2. 在`application.properties`配置文件中配置OpenSergo控制面的相关信息 +``` +# OpenSergo 控制面 endpoint +spring.cloud.opensergo.endpoint=127.0.0.1:10246 +``` +### 应用启动 +启动三个模块的启动类,分别为OpenSergoConsumerApplication,两个ProviderApplication,将其注入到Nacos注册中心中。 + +### 下发配置 + +[启动 OpenSergo 控制面](https://opensergo.io/zh-cn/docs/quick-start/opensergo-control-plane/) ,并通过 OpenSergo 控制面下发流量路由规则 + +``` +kubectl apply -f - << EOF +apiVersion: traffic.opensergo.io/v1alpha1 +kind: TrafficRouter +metadata: + name: service-provider + namespace: default + labels: + app: service-provider +spec: + hosts: + - service-provider + http: + - match: + - headers: + tag: + exact: v2 + route: + - destination: + host: service-provider + subset: v2 + fallback: + host: service-provider + subset: v1 + - route: + - destination: + host: service-provider + subset: v1 +EOF +``` +这条TrafficRouter指定了一条最简单的流量路由规则,将请求头tag为v2的HTTP请求路由到v2版本,其余的流量都路由到v1版本。 +如果v2版本没有对应的节点,则将流量fallback至v1版本。 +### 效果演示 +我们发送一条不带请求头的HTTP请求至IstioConsumerApplication: +``` +curl --location --request GET '127.0.0.1:18083/router-test' +``` +因为请求头不为gray,所以请求将会被路由到v1版本,返回如下: +``` +Route in 30.221.132.228: 18081,version is v1. +``` +之后我们发送一条请求头tag为gray的HTTP请求 +``` +curl --location --request GET '127.0.0.1:18083/router-test' --header 'tag: v2' +``` +因为满足路由规则,所以请求会被路由至v2版本: +``` +Route in 30.221.132.228: 18082,version is v2. +``` +最后我们删除这条标签路由规则: +```shell +kubectl delete VirtualService sca-virtual-service +kubectl delete DestinationRule my-destination-rule +``` +删除规则后,可以看到路由的策略将不由请求头的携带与否来决定,而是完全遵从于负载均衡器的实现。 + +## 集成OpenSergo +**注意 本章节只是为了便于您理解接入方式,本示例代码中已经完成接入工作,您无需再进行修改。** +1. 首先,修改pom.xml 文件,引入`spring-cloud-starter-alibaba-governance-routing`依赖。同时引入Spring Cloud Alibaba的`spring-cloud-starter-alibaba-controlplane-opensergo`模块 +``` + + com.alibaba.cloud + spring-cloud-starter-alibaba-governance-routing + + + com.alibaba.cloud + spring-cloud-starter-alibaba-controlplane-opensergo + +``` +2. 在application.properties配置文件中配置OpenSergo控制面的相关信息 +``` +# OpenSergo 控制面 endpoint +spring.cloud.opensergo.endpoint=127.0.0.1:10246 +``` +### 应用启动 +启动三个模块的启动类,分别为OpenSergoConsumerApplication,两个ProviderApplication,将其注入到Nacos注册中心中。 + +### 下发配置 + +[启动 OpenSergo 控制面](https://opensergo.io/zh-cn/docs/quick-start/opensergo-control-plane/) ,并通过 OpenSergo 控制面下发流量路由规则 + +``` +kubectl apply -f - << EOF +apiVersion: traffic.opensergo.io/v1alpha1 +kind: TrafficRouter +metadata: + name: service-provider + namespace: default + labels: + app: service-provider +spec: + hosts: + - service-provider + http: + - match: + - headers: + tag: + exact: v2 + route: + - destination: + host: service-provider + subset: v2 + fallback: + host: service-provider + subset: v1 + - route: + - destination: + host: service-provider + subset: v1 +EOF +``` +这条TrafficRouter指定了一条最简单的流量路由规则,将请求头tag为v2的HTTP请求路由到v2版本,其余的流量都路由到v1版本。 +如果v2版本没有对应的节点,则将流量fallback至v1版本。 +### 效果演示 +我们发送一条不带请求头的HTTP请求至IstioConsumerApplication +``` +curl --location --request GET '127.0.0.1:18083/router-test' +``` +因为请求头不为gray,所以请求将会被路由到v1版本,返回如下 +``` +Route in 30.221.132.228: 18081,version is v1. +``` +之后我们发送一条请求头tag为gray的HTTP请求 +``` +curl --location --request GET '127.0.0.1:18083/router-test' --header 'tag: v2' +``` +因为满足路由规则,所以请求会被路由至v2版本 +``` +Route in 30.221.132.228: 18082,version is v2. +``` +我们停止v2版本的ProviderApplication后,继续发送一条请求头tag为gray的HTTP请求 +``` +curl --location --request GET '127.0.0.1:18083/router-test' --header 'tag: v2' +``` +因为v2版本没有服务提供者,因此流量被fallback至v1版本。 +``` +Route in 30.221.132.228: 18081,version is v1. +``` diff --git a/spring-cloud-alibaba-examples/governance-example/label-routing-example/readme.md b/spring-cloud-alibaba-examples/governance-example/label-routing-example/readme.md new file mode 100644 index 000000000..18c47b284 --- /dev/null +++ b/spring-cloud-alibaba-examples/governance-example/label-routing-example/readme.md @@ -0,0 +1,355 @@ +# label route example + +## Project Description + +This project demonstrates how to use the spring cloud ailbaba governance labelrouting module to complete the label routing function. + +## module structure + +This module includes a consumer instance and a provider cluster, which contains two instances. + +## Example + +### How to access + +**Note that this section is only for your convenience in understanding the access method. The access work has been completed in this sample code, and you do not need to modify it.** +1. First, modify the pom XML file, which introduces the spring cloud ailbaba governance labelrouting dependency. +```xml + + com.alibaba.cloud + spring-cloud-starter-alibaba-governance-routing + +``` + +### Application Start + +Start a startup class of three modules, ConsumerApplication and two ProviderApplications, and inject them into the Nacos registry. + +### Effect demonstration + +#### Rule Description +The rules set in the instance are as follows: +```java +@GetMapping("/add") +public void getDataFromControlPlaneTest() { + List routeRules = new ArrayList<>(); + List matchServices = new ArrayList<>(); + UnifiedRouteDataStructure unifiedRouteDataStructure = new UntiedRouteDataStructure(); + unifiedRouteDataStructure.setTargetService("service-provider"); + LabelRouteRule labelRouteData = new LabelRouteRule(); + labelRouteData.setDefaultRouteVersion("v1"); + RouteRule routeRule = new HeaderRule(); + routeRule.setType("header"); + routeRule.setCondition("="); + routeRule.setKey("tag"); + routeRule.setValue("gray"); + RouteRule routeRule1 = new UrlRule.Parameter(); + routeRule1.setType("parameter"); + routeRule1.setCondition(">"); + routeRule1.setKey("id"); + routeRule1.setValue("10"); + RouteRule routeRule2 = new UrlRule.Path(); + routeRule2.setType("path"); + routeRule2.setCondition("="); + routeRule2.setValue("/router-test"); + routeRules.add(routeRule); + routeRules.add(routeRule1); + routeRules.add(routeRule2); + MatchService matchService = new MatchService(); + matchService.setVersion("v2"); + matchService.setWeight(100); + matchService.setRuleList(routeRules); + matchServices.add(matchService); + labelRouteData.setMatchRouteList(matchServices); + unifiedRouteDataStructure.setLabelRouteRule(labelRouteData); + List unifiedRouteDataStructureList = new ArrayList<>(); + unifiedRouteDataStructureList.add(unifiedRouteDataStructure); + controlPlaneConnection.pushRouteData(unifiedRouteDataStructureList); +} +``` +The rules corresponding to the code are as follows: +If the request parameter contains tag=gray and the request header contains id and the value is greater than 10, uri is /router-test at the same time, the traffic is routed to the v2 version. If one of the request parameters does not meet the requirement, the traffic is routed to the v1 version. + +Rules also support dynamic modification. The rules for testing dynamic modification are as follows: +```java +@GetMapping("/add") +public void getDataFromControlPlaneTest() { + List routeRules = new ArrayList<>(); + List matchServices = new ArrayList<>(); + UntiedRouteDataStructure unifiedRouteDataStructure = new UntiedRouteDataStructure(); + unifiedRouteDataStructure.setTargetService("service-provider"); + LabelRouteRule labelRouteData = new LabelRouteRule(); + labelRouteData.setDefaultRouteVersion("v1"); + + RouteRule routeRule = new HeaderRule(); + routeRule.setType("header"); + routeRule.setCondition("="); + routeRule.setKey("tag"); + routeRule.setValue("gray"); + RouteRule routeRule1 = new UrlRule.Parameter(); + routeRule1.setType("parameter"); + routeRule1.setCondition(">"); + routeRule1.setKey("id"); + routeRule1.setValue("10"); + + RouteRule routeRule2 = new UrlRule.Path(); + routeRule2.setType("path"); + routeRule2.setCondition("="); + routeRule2.setValue("/router-test"); + routeRules.add(routeRule); + routeRules.add(routeRule1); + routeRules.add(routeRule2); + + MatchService matchService = new MatchService(); + matchService.setVersion("v2"); + matchService.setWeight(50); + matchService.setRuleList(routeRules); + matchServices.add(matchService); + labelRouteData.setMatchRouteList(matchServices); + unifiedRouteDataStructure.setLabelRouteRule(labelRouteData); + List unifiedRouteDataStructureList = new ArrayList<>(); + unifiedRouteDataStructureList.add(unifiedRouteDataStructure); + controlPlaneConnection.pushRouteData(unifiedRouteDataStructureList); +} +``` +The rules corresponding to the code are as follows: +If the request parameter contains tag=gray, and the request header contains id and the value is greater than 10,uri is /router-test, 50% of the traffic is routed to the v2 version, and the rest is routed to the v1 version. If one of the traffic does not meet the requirements, the traffic is routed to the v1 version. + +##### demonstrate Steps +1. visit http://localhost:18083/add Push the routing rules from the control surface interface to the routing rule warehouse + visit http://localhost:18083/router -The test does not meet the routing rules. When the test is routed to the v1 version, the v1 version instance prints and returns the following results: + ``` + Route in 30.221.132.228: 18081,version is v1. + ``` + visit http://localhost:18083/router-test?id=11 and the test value set in the request header is gray, which meets the routing rules. The route is to the v2 version. The v2 version instance prints and returns the following results: + ``` + Route in 30.221.132.228: 18082,version is v2. + ``` + +2. visit http://localhost:18083/update Simulate dynamic modification of routing rules. + visit http://localhost:18083/router The test does not meet the routing rules. When the test is routed to the v1 version, the v1 version instance prints and returns the following results: + ``` + Route in 30.221.132.228: 18081,version is v1. + ``` + visit http://localhost:18083/router-test?id=11 and the test value set in the request header is gray, which meets the routing rules. 50% of the routes are routed to the v2 version. The v2 version instance prints the following results: + ``` + Route in 30.221.132.228: 18082,version is v2. + ``` + 50% of them are routed to the v1 version, and the following results are returned when the v1 version instance is printed: + ``` + Route in 30.221.132.228: 18081,version is v1. + ``` + +3. If you don't push rule,it will load balance by common rule you set. +## Integrating Istio +**Note that this section is only for your convenience in understanding the access method. The access work has been completed in this sample code, and you do not need to modify it.** +## Preparation +### Install K8s +Please refer to [tools](https://kubernetes.io/zh-cn/docs/tasks/tools/) chapter of K8s document. +### Enable Istio on K8s +Please refer to [install](https://istio.io/latest/zh/docs/setup/install/) chapter of Istio document. +### Introduction to Istio traffic control rules +- [overview](https://istio.io/latest/zh/docs/concepts/security/#authorization) +- [detail](https://istio.io/latest/zh/docs/reference/config/security/) +1. First, modify the pom.xml file to introduce the `spring-cloud-starter-alibaba-governance-routing` and `spring-cloud-starter-alibaba-controlplane-istio` dependency +```xml + + com.alibaba.cloud + spring-cloud-starter-alibaba-governance-routing + + + com.alibaba.cloud + spring-cloud-starter-alibaba-controlplane-istio + +``` +2. Configure application.yml for Istio control plane: +``` +server: + port: 18084 +spring: + main: + allow-bean-definition-overriding: true + application: + name: service-consumer + cloud: + nacos: + discovery: + server-addr: 127.0.0.1:8848 + fail-fast: true + username: nacos + password: nacos + governance: + auth: + # Is authentication enabled + enabled: ${ISTIO_AUTH_ENABLE:false} + istio: + config: + # Is Istio resource transform enabled + enabled: ${ISTIO_CONFIG_ENABLE:true} + # Istiod ip + host: ${ISTIOD_ADDR:127.0.0.1} + # Istiod port + port: ${ISTIOD_PORT:15010} + # Istiod thread-pool size + polling-pool-size: ${POLLING_POOL_SIZE:10} + # Istiod polling gap + polling-time: ${POLLING_TIME:10} + # Istiod token(For Istio 15012 port) + istiod-token: ${ISTIOD_TOKEN:} + # Whether to print xds log + log-xds: ${LOG_XDS:true} +``` +### Startup Application +Start IstioConsumerApplication and two ProviderApplications, and inject it into the Nacos registry center. + +### Publish Configuration +We publish the label routing rules through the Istio control plane. We publish a DestinationRule rule first: +``` +kubectl apply -f - << EOF +apiVersion: networking.istio.io/v1alpha3 +kind: DestinationRule +metadata: + name: my-destination-rule +spec: + host: sca-virtual-service + subsets: + - name: v1 + labels: + version: v1 + - name: v2 + labels: + version: v2 +EOF +``` +This rule splits the back-end service into two versions. Pod with label v1 is assigned to v1, and pod with label v2 is assigned to v2 +After that, we publish the VirtualService rule: +``` +kubectl apply -f - << EOF +apiVersion: networking.istio.io/v1alpha3 +kind: VirtualService +metadata: + name: sca-virtual-service +spec: + hosts: + - service-provider + http: + - match: + - headers: + tag: + exact: gray + uri: + exact: /istio-label-routing + route: + - destination: + host: service-provider + subset: v2 + - route: + - destination: + host: service-provider + subset: v1 +EOF +``` +This VirtualService specifies the simplest label routing rule. HTTP requests with a gray header and /istio-label-routing path are routed to v2, and the rest of the traffic is routed to v1: +### Demonstrate effect +We send an HTTP request without a request header to IstioConsumerApplication: +``` +curl --location --request GET '127.0.0.1:18084/istio-label-routing' +``` +Since the request header is not gray, the request will be routed to version v1 with the following result: +``` +Route in 30.221.132.228: 18081,version is v1. +``` +We then send an HTTP request with a gray tag in its header and the request path is /istio-label-routing: +``` +curl --location --request GET '127.0.0.1:18084/istio-label-routing' --header 'tag: gray' +``` +The request is routed to version v2 because the routing rule is matched by the request: +``` +Route in 30.221.132.228: 18082,version is v2. +``` +Finally, we delete this label routing rule:: +```shell +kubectl delete VirtualService sca-virtual-service +kubectl delete DestinationRule my-destination-rule +``` +After the rule is deleted, the routing policy is not determined by whether the request header is carried or not, but completely depends on the implementation of the loadbalancer。 +## Integrating OpenSergo +**Note that this section is only for your convenience in understanding the access method. The access work has been completed in this sample code, and you do not need to modify it.** +### Configure +1. First, modify the pom.xml file to introduce the `spring-cloud-starter-alibaba-governance-routing` and `spring-cloud-starter-alibaba-controlplane-opensergo` dependency +``` + + com.alibaba.cloud + spring-cloud-starter-alibaba-governance-routing + + + com.alibaba.cloud + spring-cloud-starter-alibaba-controlplane-opensergo + +``` +2. Configure application.yml for OpenSergo control plane +``` +# The endpoint of OpenSergo ControlPlane +spring.cloud.opensergo.endpoint=127.0.0.1:10246 +``` +### Startup Application +Start OpenSergoConsumerApplication and two ProviderApplications, and inject it into the Nacos registry center. +### Publish Configuration +[First start OpenSergo control plan](https://opensergo.io/docs/quick-start/opensergo-control-plane/) , Then we publish the label routing rules through the OpenSergo control plane. We publish a TrafficRouter rule. +``` +kubectl apply -f - << EOF +apiVersion: traffic.opensergo.io/v1alpha1 +kind: TrafficRouter +metadata: + name: service-provider + namespace: default + labels: + app: service-provider +spec: + hosts: + - service-provider + http: + - match: + - headers: + tag: + exact: v2 + route: + - destination: + host: service-provider + subset: v2 + fallback: + host: service-provider + subset: v1 + - route: + - destination: + host: service-provider + subset: v1 +EOF +``` +This TrafficRouter specifies the simplest label routing rule. HTTP requests with a gray header are routed to v2, and the rest of the traffic is routed to v1. +If the version v2 does not have a corresponding instance, the HTTP request will fall back to the version v1. +### Demonstrate effect +We send an HTTP request without a request header to IstioConsumerApplication +``` +curl --location --request GET '127.0.0.1:18083/router-test' +``` +Since the request header is not gray, the request will be routed to version v1 with the following result +``` +Route in 30.221.132.228: 18081,version is v1. +``` +We then send an HTTP request with a gray tag in its header and the request path is /istio-label-routing +``` +curl --location --request GET '127.0.0.1:18083/router-test' --header 'tag: gray' +``` +The request is routed to version v2 because the routing rule is matched by the request. +``` +Route in 30.221.132.228: 18082,version is v2. +``` +After we stop the ProviderApplication of the version v2, we send an HTTP request with the request header tag gray. +``` +curl --location --request GET '127.0.0.1:18083/router-test' --header 'tag: v2' +``` +because the version v2 does not have a corresponding instance, so the Http requesr is fallback to the version v1. +``` +Route in 30.221.132.228: 18081,version is v1. +``` \ No newline at end of file diff --git a/spring-cloud-alibaba-examples/pom.xml b/spring-cloud-alibaba-examples/pom.xml index 6caa7ba77..b1d0b679e 100644 --- a/spring-cloud-alibaba-examples/pom.xml +++ b/spring-cloud-alibaba-examples/pom.xml @@ -58,7 +58,13 @@ integrated-example/integrated-praise-consumer integrated-example/integrated-common integrated-example/integrated-frontend - + governance-example/label-routing-example/istio-consumer-example + governance-example/label-routing-example/opensergo-consumer-example + governance-example/label-routing-example/consumer-example + governance-example/label-routing-example/default-provider-version-example + governance-example/label-routing-example/provider-version-example + + diff --git a/spring-cloud-alibaba-starters/pom.xml b/spring-cloud-alibaba-starters/pom.xml index 6773cb080..cfbc52938 100644 --- a/spring-cloud-alibaba-starters/pom.xml +++ b/spring-cloud-alibaba-starters/pom.xml @@ -26,6 +26,10 @@ spring-cloud-alibaba-sentinel-datasource spring-cloud-alibaba-sentinel-gateway spring-cloud-starter-alibaba-appactive + spring-cloud-starter-alibaba-controlplane-istio + spring-cloud-starter-alibaba-controlplane-opensergo + spring-cloud-starter-alibaba-governance-auth + spring-cloud-starter-alibaba-governance-routing spring-cloud-alibaba-commons diff --git a/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/pom.xml b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/pom.xml index 73248d29b..0ad44d296 100644 --- a/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/pom.xml +++ b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/pom.xml @@ -13,6 +13,18 @@ spring-cloud-alibaba-commons Spring Cloud Alibaba Commons + + + org.slf4j + slf4j-api + provided + + + org.springframework + spring-context + provided + + \ No newline at end of file diff --git a/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/auth/condition/AuthCondition.java b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/auth/condition/AuthCondition.java new file mode 100644 index 000000000..3ab7e9028 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/auth/condition/AuthCondition.java @@ -0,0 +1,80 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.commons.governance.auth.condition; + +import com.alibaba.cloud.commons.matcher.Matcher; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author musi + * @author
+ */ +public class AuthCondition { + + public enum ValidationType { + + /** + * All types of auth validation. + */ + HEADER, SOURCE_IP, REMOTE_IP, DEST_IP, REQUEST_PRINCIPALS, AUTH_AUDIENCES, AUTH_CLAIMS, AUTH_PRESENTERS, HOSTS, PATHS, PORTS, METHODS, IDENTITY + + } + + private static final Logger log = LoggerFactory.getLogger(AuthCondition.class); + + private ValidationType type; + + private String key; + + private Matcher matcher; + + public AuthCondition(ValidationType type, Matcher matcher) { + this.type = type; + this.matcher = matcher; + } + + public AuthCondition(ValidationType type, String key, Matcher matcher) { + this(type, matcher); + this.key = key; + } + + public ValidationType getType() { + return type; + } + + public void setType(ValidationType type) { + this.type = type; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public Matcher getMatcher() { + return matcher; + } + + public void setMatcher(Matcher matcher) { + this.matcher = matcher; + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/auth/rule/AuthRule.java b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/auth/rule/AuthRule.java new file mode 100644 index 000000000..34815b633 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/auth/rule/AuthRule.java @@ -0,0 +1,112 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.commons.governance.auth.rule; + +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.cloud.commons.governance.auth.condition.AuthCondition; + +/** + * @author musi + * @author + */ +public class AuthRule { + + public enum RuleOperation { + + /** + * In what way are subrules connected. + */ + UNKNOWN, AND, OR + + } + + private RuleOperation op = RuleOperation.UNKNOWN; + + private List children = new ArrayList<>(); + + private AuthCondition condition; + + private boolean isNot; + + public AuthRule(RuleOperation op) { + this.op = op; + } + + public AuthRule(RuleOperation op, boolean isNot) { + this(op); + this.isNot = isNot; + } + + public AuthRule(AuthCondition condition) { + this.condition = condition; + } + + public AuthRule(AuthCondition condition, boolean isNot) { + this(condition); + this.isNot = isNot; + } + + public void addChildren(AuthRule rule) { + children.add(rule); + } + + public boolean isEmpty() { + if (children.isEmpty()) { + return condition == null; + } + return false; + } + + public boolean isLeaf() { + return condition != null; + } + + public RuleOperation getOp() { + return op; + } + + public void setOp(RuleOperation op) { + this.op = op; + } + + public List getChildren() { + return children; + } + + public void setChildren(List children) { + this.children = children; + } + + public AuthCondition getCondition() { + return condition; + } + + public void setCondition(AuthCondition condition) { + this.condition = condition; + } + + public boolean isNot() { + return isNot; + } + + public void setNot(boolean not) { + isNot = not; + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/auth/rule/AuthRules.java b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/auth/rule/AuthRules.java new file mode 100644 index 000000000..7f0e83be5 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/auth/rule/AuthRules.java @@ -0,0 +1,52 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.commons.governance.auth.rule; + +import java.util.Map; + +/** + * @author musi + * @author + */ +public class AuthRules { + + private final Map allowAuthRules; + + private final Map denyAuthRules; + + private final Map jwtRules; + + public AuthRules(Map allowAuthRules, + Map denyAuthRules, Map jwtRules) { + this.allowAuthRules = allowAuthRules; + this.denyAuthRules = denyAuthRules; + this.jwtRules = jwtRules; + } + + public Map getAllowAuthRules() { + return allowAuthRules; + } + + public Map getDenyAuthRules() { + return denyAuthRules; + } + + public Map getJwtRules() { + return jwtRules; + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/auth/rule/JwtRule.java b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/auth/rule/JwtRule.java new file mode 100644 index 000000000..b4a0cb77a --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/auth/rule/JwtRule.java @@ -0,0 +1,89 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.commons.governance.auth.rule; + +import java.util.List; +import java.util.Map; + +/** + * @author musi + * @author + */ +public class JwtRule { + + private String name; + + private Map fromHeaders; + + private String issuer; + + private List audiences; + + private String jwks; + + private List fromParams; + + private String outputPayloadToHeader; + + private boolean forwardOriginalToken; + + public JwtRule(String name, Map fromHeaders, String issuer, + List audiences, String jwks, List fromParams, + String outputPayloadToHeader, boolean forwardOriginalToken) { + this.name = name; + this.fromHeaders = fromHeaders; + this.issuer = issuer; + this.audiences = audiences; + this.jwks = jwks; + this.fromParams = fromParams; + this.outputPayloadToHeader = outputPayloadToHeader; + this.forwardOriginalToken = forwardOriginalToken; + } + + public String getName() { + return name; + } + + public Map getFromHeaders() { + return fromHeaders; + } + + public String getIssuer() { + return issuer; + } + + public List getAudiences() { + return audiences; + } + + public String getJwks() { + return jwks; + } + + public List getFromParams() { + return fromParams; + } + + public String getOutputPayloadToHeader() { + return outputPayloadToHeader; + } + + public boolean isForwardOriginalToken() { + return forwardOriginalToken; + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/event/AuthDataChangedEvent.java b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/event/AuthDataChangedEvent.java new file mode 100644 index 000000000..2b7bf2c91 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/event/AuthDataChangedEvent.java @@ -0,0 +1,41 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.commons.governance.event; + +import com.alibaba.cloud.commons.governance.auth.rule.AuthRules; + +/** + * @author musi + * @author + */ +public class AuthDataChangedEvent extends GovernanceEvent { + + /** + * Configuration for authentication. + */ + private final AuthRules authRules; + + public AuthDataChangedEvent(Object source, AuthRules authRules) { + super(source); + this.authRules = authRules; + } + + public AuthRules getAuthRules() { + return authRules; + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/event/GovernanceEvent.java b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/event/GovernanceEvent.java new file mode 100644 index 000000000..7eb295fa4 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/event/GovernanceEvent.java @@ -0,0 +1,36 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.commons.governance.event; + +import org.springframework.context.ApplicationEvent; + +/** + * @author musi + * @author + */ +public class GovernanceEvent extends ApplicationEvent { + + /** + * Create a new {@code ApplicationEvent}. + * @param source the object on which the event initially occurred or with which the + * event is associated (never {@code null}) + */ + public GovernanceEvent(Object source) { + super(source); + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/event/LabelRoutingDataChangedEvent.java b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/event/LabelRoutingDataChangedEvent.java new file mode 100644 index 000000000..5085c0e81 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/event/LabelRoutingDataChangedEvent.java @@ -0,0 +1,44 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.commons.governance.event; + +import java.util.Collection; + +import com.alibaba.cloud.commons.governance.labelrouting.UnifiedRouteDataStructure; + +/** + * @author musi + * @author + */ +public class LabelRoutingDataChangedEvent extends GovernanceEvent { + + /** + * Configuration for Label Routing. + */ + private final Collection untiedRouterDataStructureList; + + public LabelRoutingDataChangedEvent(Object source, + Collection untiedRouterDataStructureList) { + super(source); + this.untiedRouterDataStructureList = untiedRouterDataStructureList; + } + + public Collection getUntiedRouterDataStructureList() { + return untiedRouterDataStructureList; + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/event/TargetServiceChangedEvent.java b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/event/TargetServiceChangedEvent.java new file mode 100644 index 000000000..40bf5d970 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/event/TargetServiceChangedEvent.java @@ -0,0 +1,27 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.commons.governance.event; + +import org.springframework.context.ApplicationEvent; + +public class TargetServiceChangedEvent extends ApplicationEvent { + + public TargetServiceChangedEvent(Object source) { + super(source); + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/labelrouting/LabelRouteRule.java b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/labelrouting/LabelRouteRule.java new file mode 100644 index 000000000..47e578044 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/labelrouting/LabelRouteRule.java @@ -0,0 +1,71 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.commons.governance.labelrouting; + +import java.util.List; +import java.util.Objects; + +/** + * @author HH + */ +public class LabelRouteRule { + + private String defaultRouteVersion; + + private List matchRouteList; + + public String getDefaultRouteVersion() { + return defaultRouteVersion; + } + + public void setDefaultRouteVersion(String defaultRouteVersion) { + this.defaultRouteVersion = defaultRouteVersion; + } + + public List getMatchRouteList() { + return matchRouteList; + } + + public void setMatchRouteList(List matchRouteList) { + this.matchRouteList = matchRouteList; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + LabelRouteRule that = (LabelRouteRule) o; + return Objects.equals(defaultRouteVersion, that.defaultRouteVersion) + && Objects.equals(getMatchRouteList(), that.getMatchRouteList()); + } + + @Override + public int hashCode() { + return Objects.hash(defaultRouteVersion, getMatchRouteList()); + } + + @Override + public String toString() { + return "LabelRouteData{" + "defaultRouteVersion='" + defaultRouteVersion + '\'' + + ", matchRouteList=" + matchRouteList + '}'; + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/labelrouting/MatchService.java b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/labelrouting/MatchService.java new file mode 100644 index 000000000..6fe6353d8 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/labelrouting/MatchService.java @@ -0,0 +1,95 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.commons.governance.labelrouting; + +import java.util.List; +import java.util.Objects; + +import com.alibaba.cloud.commons.governance.labelrouting.rule.RouteRule; + +/** + * @author HH + */ +public class MatchService { + + private List ruleList; + + private String version; + + private Integer weight; + + private String fallbackVersion; + + public String getFallback() { + return fallbackVersion; + } + + public void setFallback(String fallbackVersion) { + this.fallbackVersion = fallbackVersion; + } + + public List getRuleList() { + return ruleList; + } + + public void setRuleList(List ruleList) { + this.ruleList = ruleList; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public Integer getWeight() { + return weight; + } + + public void setWeight(Integer weight) { + this.weight = weight; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + MatchService that = (MatchService) o; + return getWeight().equals(that.getWeight()) + && Objects.equals(getRuleList(), that.getRuleList()) + && Objects.equals(getFallback(), that.getFallback()) + && Objects.equals(getVersion(), that.getVersion()); + } + + @Override + public int hashCode() { + return Objects.hash(getRuleList(), getVersion(), getWeight(), getFallback()); + } + + @Override + public String toString() { + return "MatchService{" + "ruleList=" + ruleList + ", version='" + version + '\'' + + ", weight=" + weight + ", getFallback=" + fallbackVersion + '}'; + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/labelrouting/UnifiedRouteDataStructure.java b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/labelrouting/UnifiedRouteDataStructure.java new file mode 100644 index 000000000..90f6ee855 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/labelrouting/UnifiedRouteDataStructure.java @@ -0,0 +1,50 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.commons.governance.labelrouting; + +/** + * @author HH + */ +public class UnifiedRouteDataStructure { + + private LabelRouteRule labelRouteRule; + + private String targetService; + + public LabelRouteRule getLabelRouteRule() { + return labelRouteRule; + } + + public void setLabelRouteRule(LabelRouteRule labelRouteRule) { + this.labelRouteRule = labelRouteRule; + } + + public String getTargetService() { + return targetService; + } + + public void setTargetService(String targetService) { + this.targetService = targetService; + } + + @Override + public String toString() { + return "UntiedRouteDataStructure{" + "labelRouteData=" + labelRouteRule + + ", targetService='" + targetService + '\'' + '}'; + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/labelrouting/rule/HeaderRule.java b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/labelrouting/rule/HeaderRule.java new file mode 100644 index 000000000..793d8ff49 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/labelrouting/rule/HeaderRule.java @@ -0,0 +1,100 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.commons.governance.labelrouting.rule; + +import java.util.Objects; + +/** + * @author HH + */ +public class HeaderRule implements RouteRule { + + private String type; + + private String condition; + + private String key; + + private String value; + + @Override + public String getCondition() { + return condition; + } + + @Override + public void setCondition(String condition) { + this.condition = condition; + } + + @Override + public String getKey() { + return key; + } + + @Override + public void setKey(String key) { + this.key = key; + } + + @Override + public String getValue() { + return value; + } + + @Override + public void setValue(String value) { + this.value = value; + } + + @Override + public String getType() { + return this.type; + } + + @Override + public void setType(String type) { + this.type = type; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + HeaderRule that = (HeaderRule) o; + return Objects.equals(getType(), that.getType()) + && Objects.equals(getCondition(), that.getCondition()) + && Objects.equals(getKey(), that.getKey()) + && Objects.equals(getValue(), that.getValue()); + } + + @Override + public int hashCode() { + return Objects.hash(getType(), getCondition(), getKey(), getValue()); + } + + @Override + public String toString() { + return "HeaderRule{" + "type='" + type + '\'' + ", condition='" + condition + '\'' + + ", key='" + key + '\'' + ", value='" + value + '\'' + '}'; + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/labelrouting/rule/RouteRule.java b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/labelrouting/rule/RouteRule.java new file mode 100644 index 000000000..c60166d5a --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/labelrouting/rule/RouteRule.java @@ -0,0 +1,44 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.commons.governance.labelrouting.rule; + +/** + * @author HH + */ +public interface RouteRule { + + /** + * get type of rule. + * @return String + */ + String getType(); + + void setType(String type); + + String getCondition(); + + void setCondition(String condition); + + String getKey(); + + void setKey(String key); + + String getValue(); + + void setValue(String value); + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/labelrouting/rule/UrlRule.java b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/labelrouting/rule/UrlRule.java new file mode 100644 index 000000000..c3a798491 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/governance/labelrouting/rule/UrlRule.java @@ -0,0 +1,179 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.commons.governance.labelrouting.rule; + +import java.util.Objects; + +/** + * @author HH + */ +public class UrlRule { + + public static class Path implements RouteRule { + + private String type; + + private String condition; + + private String value; + + @Override + public String getCondition() { + return condition; + } + + @Override + public void setCondition(String condition) { + this.condition = condition; + } + + @Override + public String getKey() { + return null; + } + + @Override + public void setKey(String key) { + // + } + + @Override + public String getValue() { + return value; + } + + @Override + public void setValue(String value) { + this.value = value; + } + + @Override + public String getType() { + return this.type; + } + + @Override + public void setType(String type) { + this.type = type; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Path path = (Path) o; + return Objects.equals(getType(), path.getType()) + && Objects.equals(getCondition(), path.getCondition()) + && Objects.equals(getValue(), path.getValue()); + } + + @Override + public int hashCode() { + return Objects.hash(getType(), getCondition(), getValue()); + } + + @Override + public String toString() { + return "Path{" + "type='" + type + '\'' + ", condition='" + condition + '\'' + + ", value='" + value + '\'' + '}'; + } + + } + + public static class Parameter implements RouteRule { + + private String type; + + private String condition; + + private String key; + + private String value; + + @Override + public String getCondition() { + return condition; + } + + @Override + public void setCondition(String condition) { + this.condition = condition; + } + + @Override + public String getKey() { + return key; + } + + @Override + public void setKey(String key) { + this.key = key; + } + + @Override + public String getValue() { + return value; + } + + @Override + public void setValue(String value) { + this.value = value; + } + + @Override + public String getType() { + return this.type; + } + + @Override + public void setType(String type) { + this.type = type; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Parameter parameter = (Parameter) o; + return Objects.equals(getType(), parameter.getType()) + && Objects.equals(getCondition(), parameter.getCondition()) + && Objects.equals(getKey(), parameter.getKey()) + && Objects.equals(getValue(), parameter.getValue()); + } + + @Override + public int hashCode() { + return Objects.hash(getType(), getCondition(), getKey(), getValue()); + } + + @Override + public String toString() { + return "Parameter{" + "type='" + type + '\'' + ", condition='" + condition + + '\'' + ", key='" + key + '\'' + ", value='" + value + '\'' + '}'; + } + + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/matcher/IpMatcher.java b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/matcher/IpMatcher.java new file mode 100644 index 000000000..04b043afb --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/matcher/IpMatcher.java @@ -0,0 +1,108 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.commons.matcher; + +import com.alibaba.cloud.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author musi + * @author + */ +public class IpMatcher implements Matcher { + + private static final Logger log = LoggerFactory.getLogger(IpMatcher.class); + + private int prefixLen; + + private String ip; + + public IpMatcher() { + + } + + public IpMatcher(int prefixLen, String ip) { + this.prefixLen = prefixLen; + this.ip = ip; + } + + public boolean match(Object object) { + if (!(object instanceof String)) { + return false; + } + String ip = (String) object; + String ruleIp = ip2BinaryString(this.ip); + if (StringUtils.isEmpty(ruleIp)) { + return false; + } + String ipBinary = ip2BinaryString(ip); + if (StringUtils.isEmpty(ipBinary)) { + return false; + } + if (prefixLen <= 0) { + return ruleIp.equals(ipBinary); + } + if (ruleIp.length() >= prefixLen && ipBinary.length() >= prefixLen) { + return ruleIp.substring(0, prefixLen) + .equals(ipBinary.substring(0, prefixLen)); + } + return false; + } + + /** + * @param ip dotted ip string, for example: 127.0.0.1 + * @return + */ + private String ip2BinaryString(String ip) { + try { + String[] ips = ip.split("\\."); + long[] ipLong = new long[4]; + for (int i = 0; i < 4; ++i) { + ipLong[i] = Long.parseLong(ips[i]); + if (ipLong[i] < 0 || ipLong[i] > 255) { + return ""; + } + } + return String + .format("%32s", Long.toBinaryString((ipLong[0] << 24) + + (ipLong[1] << 16) + (ipLong[2] << 8) + ipLong[3])) + .replace(" ", "0"); + } + catch (Exception e) { + log.error("failed to parse ip {} to binary string", ip); + } + return ""; + } + + public int getPrefixLen() { + return prefixLen; + } + + public String getIp() { + return ip; + } + + public void setPrefixLen(int prefixLen) { + this.prefixLen = prefixLen; + } + + public void setIp(String ip) { + this.ip = ip; + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/matcher/Matcher.java b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/matcher/Matcher.java new file mode 100644 index 000000000..6753e16c7 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/matcher/Matcher.java @@ -0,0 +1,27 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.commons.matcher; + +/** + * @author musi + * @author + */ +public interface Matcher { + + boolean match(Object obj); + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/matcher/PortMatcher.java b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/matcher/PortMatcher.java new file mode 100644 index 000000000..4b0303cf7 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/matcher/PortMatcher.java @@ -0,0 +1,47 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.commons.matcher; + +/** + * @author musi + * @author + */ +public class PortMatcher implements Matcher { + + private Integer matcher; + + public PortMatcher() { + + } + + public PortMatcher(Integer matcher) { + this.matcher = matcher; + } + + @Override + public boolean match(Object object) { + if (!(object instanceof Integer)) { + return false; + } + return matcher != null && matcher.equals(object); + } + + public void setMatcher(Integer matcher) { + this.matcher = matcher; + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/matcher/StringMatcher.java b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/matcher/StringMatcher.java new file mode 100644 index 000000000..b115ed0a1 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/matcher/StringMatcher.java @@ -0,0 +1,119 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.commons.matcher; + +import java.util.Locale; +import java.util.regex.Pattern; + +import com.alibaba.cloud.commons.lang.StringUtils; + +/** + * @author musi + * @author + */ +public class StringMatcher implements Matcher { + + private String matcher; + + private StringMatcherType type; + + private boolean isIgnoreCase; + + private String regex; + + public StringMatcher() { + + } + + public StringMatcher(String regex) { + this.regex = regex; + this.type = StringMatcherType.REGEX; + } + + public StringMatcher(String matcher, StringMatcherType type, boolean isIgnoreCase) { + this.matcher = matcher; + this.type = type; + this.isIgnoreCase = isIgnoreCase; + } + + public boolean match(Object obj) { + if (!(obj instanceof String)) { + return false; + } + String str = (String) obj; + if (StringUtils.isEmpty(str)) { + return false; + } + if (isIgnoreCase) { + str = str.toLowerCase(Locale.ROOT); + matcher = matcher.toLowerCase(Locale.ROOT); + } + switch (type) { + case EXACT: + return str.equals(matcher); + case PREFIX: + return str.startsWith(matcher); + case SUFFIX: + return str.endsWith(matcher); + case CONTAIN: + return str.contains(matcher); + case REGEX: + try { + return Pattern.matches(regex, str); + } + catch (Exception e) { + return false; + } + default: + throw new UnsupportedOperationException( + "unsupported string compare operation"); + } + } + + public String getMatcher() { + return matcher; + } + + public void setMatcher(String matcher) { + this.matcher = matcher; + } + + public StringMatcherType getType() { + return type; + } + + public void setType(StringMatcherType type) { + this.type = type; + } + + public boolean isIgnoreCase() { + return isIgnoreCase; + } + + public void setIgnoreCase(boolean ignoreCase) { + isIgnoreCase = ignoreCase; + } + + public String getRegex() { + return regex; + } + + public void setRegex(String regex) { + this.regex = regex; + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/matcher/StringMatcherType.java b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/matcher/StringMatcherType.java new file mode 100644 index 000000000..6cf2a9b9d --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-alibaba-commons/src/main/java/com/alibaba/cloud/commons/matcher/StringMatcherType.java @@ -0,0 +1,64 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.commons.matcher; + +/** + * @author musi + * @author + */ +public enum StringMatcherType { + + /** + * exact match. + */ + EXACT("exact"), + /** + * prefix match. + */ + PREFIX("prefix"), + /** + * suffix match. + */ + SUFFIX("suffix"), + /** + * present match. + */ + PRESENT("present"), + /** + * regex match. + */ + REGEX("regex"), + /** + * contain match. + */ + CONTAIN("contain"); + + /** + * type of matcher. + */ + public final String type; + + StringMatcherType(String type) { + this.type = type; + } + + @Override + public String toString() { + return this.type; + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/pom.xml b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/pom.xml new file mode 100644 index 000000000..5a7d26db4 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/pom.xml @@ -0,0 +1,94 @@ + + + 4.0.0 + + com.alibaba.cloud + spring-cloud-alibaba-starters + ${revision} + ../pom.xml + + + spring-cloud-starter-alibaba-controlplane-istio + Spring Cloud Alibaba Istio Control Plane + + + + io.grpc + grpc-protobuf + + + + io.grpc + grpc-stub + + + + io.grpc + grpc-netty-shaded + + + io.envoyproxy.controlplane + api + 1.0.36 + + + + org.springframework.boot + spring-boot + true + + + + org.springframework.boot + spring-boot-autoconfigure + true + + + + com.alibaba.cloud + spring-cloud-alibaba-commons + + + + org.springframework.boot + spring-boot-starter-test + test + true + + + + org.slf4j + slf4j-api + + + ch.qos.logback + logback-classic + + + + com.alibaba + fastjson + 2.0.16 + test + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-governance-auth + test + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-governance-routing + test + + + + + 1.8 + + + diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/NodeBuilder.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/NodeBuilder.java new file mode 100644 index 000000000..d9888cc7f --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/NodeBuilder.java @@ -0,0 +1,81 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.governance.istio; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +import com.alibaba.cloud.governance.istio.constant.IstioConstants; +import com.google.protobuf.Struct; +import com.google.protobuf.Value; +import io.envoyproxy.envoy.config.core.v3.Node; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author musi + * @author + */ +public final class NodeBuilder { + + private static final Logger log = LoggerFactory.getLogger(NodeBuilder.class); + + private static Node NODE; + + private NodeBuilder() { + + } + + public static Node getNode() { + try { + if (NODE != null) { + return NODE; + } + String podName = System.getenv(IstioConstants.POD_NAME); + if (podName == null) { + podName = IstioConstants.DEFAULT_POD_NAME; + } + String podNamespace = System.getenv(IstioConstants.NAMESPACE_NAME); + if (podNamespace == null) { + podNamespace = IstioConstants.DEFAULT_NAMESPACE; + } + String ip = "127.0.0.1"; + try { + InetAddress local = InetAddress.getLocalHost(); + ip = local.getHostAddress(); + } + catch (UnknownHostException e) { + log.error("can not get local ip", e); + } + Struct.Builder metaBuilder = Struct.newBuilder(); + // metadata is necessary for RequestAuthentication CRD + metaBuilder.putFields("NAMESPACE", + Value.newBuilder().setStringValue(podNamespace).build()); + NODE = Node.newBuilder() + .setId(String.format( + "sidecar~%s~%s.%s~%s" + IstioConstants.SVC_CLUSTER_LOCAL, ip, + podName, podNamespace, podNamespace)) + .setMetadata(metaBuilder.build()).build(); + return NODE; + } + catch (Exception e) { + log.error("unable to create node for xds request", e); + } + return null; + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/PilotExchanger.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/PilotExchanger.java new file mode 100644 index 000000000..6a1a0f75b --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/PilotExchanger.java @@ -0,0 +1,100 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.governance.istio; + +import java.util.List; +import java.util.Set; + +import com.alibaba.cloud.governance.istio.protocol.impl.CdsProtocol; +import com.alibaba.cloud.governance.istio.protocol.impl.EdsProtocol; +import com.alibaba.cloud.governance.istio.protocol.impl.LdsProtocol; +import com.alibaba.cloud.governance.istio.protocol.impl.RdsProtocol; +import io.envoyproxy.envoy.config.cluster.v3.Cluster; +import io.envoyproxy.envoy.config.endpoint.v3.ClusterLoadAssignment; +import io.envoyproxy.envoy.config.listener.v3.Listener; +import io.envoyproxy.envoy.config.route.v3.RouteConfiguration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author musi + * @author PilotExchanger is the class which + * communicate with istio pilot. + */ +public class PilotExchanger { + + private static final Logger log = LoggerFactory.getLogger(PilotExchanger.class); + + private final LdsProtocol ldsProtocol; + + private final CdsProtocol cdsProtocol; + + private final EdsProtocol edsProtocol; + + private final RdsProtocol rdsProtocol; + + private void observeListeners(List listeners) { + if (listeners == null) { + return; + } + Set resourceName = ldsProtocol.getResourceNames(); + if (resourceName != null && !resourceName.isEmpty()) { + rdsProtocol.observeResource(resourceName, this::observeRoutes); + } + + } + + private void observeClusters(List clusters) { + Set resourceName = cdsProtocol.getResourceNames(); + if (resourceName != null && !resourceName.isEmpty()) { + // eds + edsProtocol.observeResource(resourceName, this::observeEndpoints); + } + else { + // lds + ldsProtocol.observeResource(null, this::observeListeners); + } + + } + + private void observeEndpoints(List endpoints) { + ldsProtocol.observeResource(null, this::observeListeners); + + } + + private void observeRoutes(List routes) { + if (log.isDebugEnabled()) { + log.debug("A Xds configuration update is finished"); + } + } + + public PilotExchanger(LdsProtocol ldsProtocol, CdsProtocol cdsProtocol, + EdsProtocol edsProtocol, RdsProtocol rdsProtocol) { + this.ldsProtocol = ldsProtocol; + this.cdsProtocol = cdsProtocol; + this.edsProtocol = edsProtocol; + this.rdsProtocol = rdsProtocol; + // observe cluster first, and update the other xds sequentially + this.ldsProtocol.setNeedPolling(false); + this.edsProtocol.setNeedPolling(false); + this.rdsProtocol.setNeedPolling(false); + // only polling cds, other protocol will be obtained sequentially + this.cdsProtocol.setNeedPolling(true); + cdsProtocol.observeResource(null, this::observeClusters); + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/XdsAutoConfiguration.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/XdsAutoConfiguration.java new file mode 100644 index 000000000..751c425fa --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/XdsAutoConfiguration.java @@ -0,0 +1,141 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.governance.istio; + +import java.util.List; + +import com.alibaba.cloud.commons.governance.event.GovernanceEvent; +import com.alibaba.cloud.governance.istio.filter.XdsResolveFilter; +import com.alibaba.cloud.governance.istio.filter.impl.AuthXdsResolveFilter; +import com.alibaba.cloud.governance.istio.filter.impl.LabelRoutingXdsResolveFilter; +import com.alibaba.cloud.governance.istio.protocol.impl.CdsProtocol; +import com.alibaba.cloud.governance.istio.protocol.impl.EdsProtocol; +import com.alibaba.cloud.governance.istio.protocol.impl.LdsProtocol; +import com.alibaba.cloud.governance.istio.protocol.impl.RdsProtocol; +import io.envoyproxy.envoy.config.listener.v3.Listener; +import io.envoyproxy.envoy.config.route.v3.RouteConfiguration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.AutoConfigureOrder; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.ApplicationListener; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author musi + * @author + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnProperty(name = "spring.cloud.istio.config.enabled", matchIfMissing = true) +@EnableConfigurationProperties(XdsConfigProperties.class) +// We need to auto config the class after all the governance data listener, to prevent +// event publisher hang permanently. +@AutoConfigureOrder(XdsAutoConfiguration.RESOURCE_TRANSFORM_AUTO_CONFIG_ORDER) +public class XdsAutoConfiguration { + + /** + * xds auto configuration log. + */ + private static final Logger log = LoggerFactory.getLogger(XdsAutoConfiguration.class); + + /** + * Order of xds auto config. + */ + public static final int RESOURCE_TRANSFORM_AUTO_CONFIG_ORDER = 100; + + @Autowired + private XdsConfigProperties xdsConfigProperties; + + @Bean + public DummyGovernanceDataListener dummyGovernanceDataListener() { + return new DummyGovernanceDataListener(); + } + + @Bean + public XdsChannel xdsChannel() { + return new XdsChannel(xdsConfigProperties); + } + + @Bean + public XdsScheduledThreadPool xdsScheduledThreadPool() { + return new XdsScheduledThreadPool(xdsConfigProperties); + } + + @Bean + public XdsResolveFilter> authXdsResolveFilter() { + return new AuthXdsResolveFilter(); + } + + @Bean + public XdsResolveFilter> labelRoutingXdsResolveFilter() { + return new LabelRoutingXdsResolveFilter(); + } + + @Bean + public PilotExchanger pilotExchanger(LdsProtocol ldsProtocol, CdsProtocol cdsProtocol, + EdsProtocol edsProtocol, RdsProtocol rdsProtocol) { + return new PilotExchanger(ldsProtocol, cdsProtocol, edsProtocol, rdsProtocol); + } + + @Bean + public LdsProtocol ldsProtocol(XdsChannel xdsChannel, + XdsScheduledThreadPool xdsScheduledThreadPool, + List>> filters) { + return new LdsProtocol(xdsChannel, xdsScheduledThreadPool, xdsConfigProperties, + filters); + } + + @Bean + public CdsProtocol cdsProtocol(XdsChannel xdsChannel, + XdsScheduledThreadPool xdsScheduledThreadPool) { + return new CdsProtocol(xdsChannel, xdsScheduledThreadPool, xdsConfigProperties); + } + + @Bean + public EdsProtocol edsProtocol(XdsChannel xdsChannel, + XdsScheduledThreadPool xdsScheduledThreadPool) { + return new EdsProtocol(xdsChannel, xdsScheduledThreadPool, xdsConfigProperties); + } + + @Bean + public RdsProtocol rdsProtocol(XdsChannel xdsChannel, + XdsScheduledThreadPool xdsScheduledThreadPool, + List>> filters) { + return new RdsProtocol(xdsChannel, xdsScheduledThreadPool, xdsConfigProperties, + filters); + } + + /** + * To prevent the event publish hang permanently. + */ + private final class DummyGovernanceDataListener + implements ApplicationListener { + + @Override + public void onApplicationEvent(GovernanceEvent event) { + if (log.isDebugEnabled()) { + log.debug("Received governance event " + event.toString()); + } + } + + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/XdsChannel.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/XdsChannel.java new file mode 100644 index 000000000..7fef37580 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/XdsChannel.java @@ -0,0 +1,135 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.governance.istio; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +import com.alibaba.cloud.commons.io.FileUtils; +import com.alibaba.cloud.commons.lang.StringUtils; +import com.alibaba.cloud.governance.istio.constant.IstioConstants; +import io.envoyproxy.envoy.service.discovery.v3.AggregatedDiscoveryServiceGrpc; +import io.envoyproxy.envoy.service.discovery.v3.DiscoveryRequest; +import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse; +import io.grpc.ManagedChannel; +import io.grpc.Metadata; +import io.grpc.netty.shaded.io.grpc.netty.GrpcSslContexts; +import io.grpc.netty.shaded.io.grpc.netty.NegotiationType; +import io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder; +import io.grpc.netty.shaded.io.netty.handler.ssl.SslContext; +import io.grpc.netty.shaded.io.netty.handler.ssl.util.InsecureTrustManagerFactory; +import io.grpc.stub.MetadataUtils; +import io.grpc.stub.StreamObserver; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static com.alibaba.cloud.governance.istio.constant.IstioConstants.ISTIOD_SECURE_PORT; + +/** + * @author musi + * @author + */ +public class XdsChannel implements AutoCloseable { + + private static final Logger log = LoggerFactory.getLogger(XdsChannel.class); + + private ManagedChannel channel; + + private String istiodToken; + + private final XdsConfigProperties xdsConfigProperties; + + public XdsChannel(XdsConfigProperties xdsConfigProperties) { + this.xdsConfigProperties = xdsConfigProperties; + try { + if (xdsConfigProperties.getPort() == ISTIOD_SECURE_PORT) { + // fetch token first + if (StringUtils.isNotEmpty(xdsConfigProperties.getIstiodToken())) { + istiodToken = xdsConfigProperties.getIstiodToken(); + } + else { + this.refreshIstiodToken(); + } + SslContext sslcontext = GrpcSslContexts.forClient() + // if server's cert doesn't chain to a standard root + .trustManager(InsecureTrustManagerFactory.INSTANCE) + // TODO: fill the publicKey and privateKey + .build(); + this.channel = NettyChannelBuilder + .forTarget(xdsConfigProperties.getHost() + ":" + + xdsConfigProperties.getPort()) + .negotiationType(NegotiationType.TLS).sslContext(sslcontext) + .build(); + } + else { + this.channel = NettyChannelBuilder + .forTarget(xdsConfigProperties.getHost() + ":" + + xdsConfigProperties.getPort()) + .negotiationType(NegotiationType.PLAINTEXT).build(); + } + } + catch (Exception e) { + log.error("create XdsChannel failed", e); + } + } + + public void refreshIstiodToken() { + if (xdsConfigProperties.getPort() != ISTIOD_SECURE_PORT) { + return; + } + File saFile = new File(IstioConstants.THIRD_PART_JWT_PATH); + if (saFile.canRead()) { + try { + this.istiodToken = FileUtils.readFileToString(saFile, + StandardCharsets.UTF_8); + return; + } + catch (IOException e) { + log.error("Unable to read token file", e); + } + } + if (this.istiodToken == null) { + throw new UnsupportedOperationException( + "Unable to found kubernetes service account token file. " + + "Please check if work in Kubernetes and mount service account token file correctly."); + } + } + + @Override + public void close() { + if (channel != null) { + channel.shutdown(); + } + } + + public StreamObserver createDiscoveryRequest( + StreamObserver observer) { + if (channel == null) { + return null; + } + AggregatedDiscoveryServiceGrpc.AggregatedDiscoveryServiceStub stub = AggregatedDiscoveryServiceGrpc + .newStub(channel); + Metadata header = new Metadata(); + Metadata.Key key = Metadata.Key.of("authorization", + Metadata.ASCII_STRING_MARSHALLER); + header.put(key, "Bearer " + this.istiodToken); + stub = MetadataUtils.attachHeaders(stub, header); + return stub.streamAggregatedResources(observer); + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/XdsConfigProperties.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/XdsConfigProperties.java new file mode 100644 index 000000000..bfb98be27 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/XdsConfigProperties.java @@ -0,0 +1,120 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.governance.istio; + +import javax.annotation.PostConstruct; + +import com.alibaba.cloud.commons.lang.StringUtils; +import com.alibaba.cloud.governance.istio.constant.IstioConstants; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * @author musi + * @author + */ +@ConfigurationProperties(XdsConfigProperties.PREFIX) +public class XdsConfigProperties { + + /** + * Prefix in yaml. + */ + public static final String PREFIX = "spring.cloud.istio.config"; + + private String host; + + private int port; + + private int pollingPoolSize; + + private int pollingTime; + + /** + * jwt token for istiod 15012 port. + */ + private String istiodToken; + + private Boolean logXds; + + @PostConstruct + public void init() { + if (this.port <= 0 || this.port > 65535) { + this.port = IstioConstants.ISTIOD_SECURE_PORT; + } + if (StringUtils.isEmpty(host)) { + this.host = IstioConstants.DEFAULT_ISTIOD_ADDR; + } + if (pollingPoolSize <= 0) { + pollingPoolSize = IstioConstants.DEFAULT_POLLING_SIZE; + } + if (pollingTime <= 0) { + pollingTime = IstioConstants.DEFAULT_POLLING_TIME; + } + if (logXds == null) { + logXds = true; + } + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public int getPollingPoolSize() { + return pollingPoolSize; + } + + public void setPollingPoolSize(int pollingPoolSize) { + this.pollingPoolSize = pollingPoolSize; + } + + public int getPollingTime() { + return pollingTime; + } + + public void setPollingTime(int pollingTime) { + this.pollingTime = pollingTime; + } + + public String getIstiodToken() { + return istiodToken; + } + + public void setIstiodToken(String istiodToken) { + this.istiodToken = istiodToken; + } + + public boolean isLogXds() { + return Boolean.TRUE.equals(logXds); + } + + public void setLogXds(boolean logXds) { + this.logXds = logXds; + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/XdsScheduledThreadPool.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/XdsScheduledThreadPool.java new file mode 100644 index 000000000..a278b6b30 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/XdsScheduledThreadPool.java @@ -0,0 +1,35 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.governance.istio; + +import java.util.concurrent.ScheduledThreadPoolExecutor; + +/** + * @author musi + * @author + */ +public class XdsScheduledThreadPool extends ScheduledThreadPoolExecutor { + + public XdsScheduledThreadPool(XdsConfigProperties xdsConfigProperties) { + this(xdsConfigProperties.getPollingPoolSize()); + } + + public XdsScheduledThreadPool(int corePoolSize) { + super(corePoolSize); + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/constant/IstioConstants.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/constant/IstioConstants.java new file mode 100644 index 000000000..3e162d744 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/constant/IstioConstants.java @@ -0,0 +1,99 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.governance.istio.constant; + +/** + * @author musi + * @author + */ +public final class IstioConstants { + + /** + * Suffix of node. + */ + public static final String SVC_CLUSTER_LOCAL = ".svc.cluster.local"; + + /** + * Default pod name. + */ + public static final String DEFAULT_POD_NAME = "sidecar"; + + /** + * Default namespace name. + */ + public static final String DEFAULT_NAMESPACE = "default"; + + /** + * Key of pod name. + */ + public static final String POD_NAME = "POD_NAME"; + + /** + * Key of namespace name. + */ + public static final String NAMESPACE_NAME = "NAMESPACE_NAME"; + + /** + * third-part jwt token location. + */ + public static final String THIRD_PART_JWT_PATH = "/var/run/secrets/tokens/istio-token"; + + /** + * url of cds request. + */ + public static final String CDS_URL = "type.googleapis.com/envoy.config.cluster.v3.Cluster"; + + /** + * url of eds request. + */ + public static final String EDS_URL = "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment"; + + /** + * url of lds request. + */ + public static final String LDS_URL = "type.googleapis.com/envoy.config.listener.v3.Listener"; + + /** + * url of rds request. + */ + public static final String RDS_URL = "type.googleapis.com/envoy.config.route.v3.RouteConfiguration"; + + /** + * secure port of istiod. + */ + public static final int ISTIOD_SECURE_PORT = 15012; + + /** + * default polling size of xds request. + */ + public static final int DEFAULT_POLLING_SIZE = 10; + + /** + * default polling time of xds request. + */ + public static final int DEFAULT_POLLING_TIME = 30; + + /** + * default ip address of istiod. + */ + public static final String DEFAULT_ISTIOD_ADDR = "127.0.0.1"; + + private IstioConstants() { + + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/filter/AbstractXdsResolveFilter.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/filter/AbstractXdsResolveFilter.java new file mode 100644 index 000000000..fcdbc262e --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/filter/AbstractXdsResolveFilter.java @@ -0,0 +1,72 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.governance.istio.filter; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; + +/** + * @author musi + * @author + */ +public abstract class AbstractXdsResolveFilter + implements XdsResolveFilter, ApplicationContextAware { + + protected static final Logger log = LoggerFactory + .getLogger(AbstractXdsResolveFilter.class); + + protected ApplicationContext applicationContext; + + protected static final String ALLOW_ANY = "allow_any"; + + protected static final String PATH = "path"; + + protected static final String VIRTUAL_INBOUND = "virtualInbound"; + + protected static final String CONNECTION_MANAGER = "envoy.filters.network.http_connection_manager"; + + protected static final String RBAC_FILTER = "envoy.filters.http.rbac"; + + protected static final String JWT_FILTER = "envoy.filters.http.jwt_authn"; + + protected static final String ISTIO_AUTHN = "istio_authn"; + + protected static final String REQUEST_AUTH_PRINCIPAL = "request.auth.principal"; + + protected static final String REQUEST_AUTH_AUDIENCE = "request.auth.audiences"; + + protected static final String REQUEST_AUTH_PRESENTER = "request.auth.presenter"; + + protected static final String REQUEST_AUTH_CLAIMS = "request.auth.claims"; + + protected static final String HEADER_NAME_AUTHORITY = ":authority"; + + protected static final String HEADER_NAME_METHOD = ":method"; + + protected static final int MIN_PORT = 0; + + protected static final int MAX_PORT = 65535; + + @Override + public void setApplicationContext(ApplicationContext applicationContext) { + this.applicationContext = applicationContext; + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/filter/XdsResolveFilter.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/filter/XdsResolveFilter.java new file mode 100644 index 000000000..dcf63a9de --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/filter/XdsResolveFilter.java @@ -0,0 +1,29 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.governance.istio.filter; + +/** + * @author musi + * @author + */ +public interface XdsResolveFilter { + + boolean resolve(T t); + + String getTypeUrl(); + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/filter/impl/AuthXdsResolveFilter.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/filter/impl/AuthXdsResolveFilter.java new file mode 100644 index 000000000..cf0575ad9 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/filter/impl/AuthXdsResolveFilter.java @@ -0,0 +1,377 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.governance.istio.filter.impl; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +import com.alibaba.cloud.commons.governance.auth.condition.AuthCondition; +import com.alibaba.cloud.commons.governance.auth.rule.AuthRule; +import com.alibaba.cloud.commons.governance.auth.rule.AuthRules; +import com.alibaba.cloud.commons.governance.auth.rule.JwtRule; +import com.alibaba.cloud.commons.governance.event.AuthDataChangedEvent; +import com.alibaba.cloud.commons.lang.StringUtils; +import com.alibaba.cloud.commons.matcher.PortMatcher; +import com.alibaba.cloud.commons.matcher.StringMatcher; +import com.alibaba.cloud.governance.istio.constant.IstioConstants; +import com.alibaba.cloud.governance.istio.filter.AbstractXdsResolveFilter; +import com.alibaba.cloud.governance.istio.util.ConvUtil; +import com.google.protobuf.Any; +import com.google.protobuf.InvalidProtocolBufferException; +import io.envoyproxy.envoy.config.listener.v3.FilterChain; +import io.envoyproxy.envoy.config.listener.v3.Listener; +import io.envoyproxy.envoy.config.rbac.v3.Permission; +import io.envoyproxy.envoy.config.rbac.v3.Policy; +import io.envoyproxy.envoy.config.rbac.v3.Principal; +import io.envoyproxy.envoy.config.rbac.v3.RBAC; +import io.envoyproxy.envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication; +import io.envoyproxy.envoy.extensions.filters.http.jwt_authn.v3.JwtHeader; +import io.envoyproxy.envoy.extensions.filters.http.jwt_authn.v3.JwtProvider; +import io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager; +import io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.HttpFilter; +import io.envoyproxy.envoy.type.matcher.v3.MetadataMatcher; + +/** + * @author musi + * @author + */ +public class AuthXdsResolveFilter extends AbstractXdsResolveFilter> { + + @Override + public boolean resolve(List listeners) { + if (listeners == null || listeners.isEmpty()) { + return false; + } + Map allowAuthRules = new HashMap<>(); + Map denyAuthRules = new HashMap<>(); + Map jwtRules = new HashMap<>(); + List httpFilters = resolveHttpFilter(listeners); + List rbacList = resolveRbac(httpFilters); + for (RBAC rbac : rbacList) { + for (Map.Entry entry : rbac.getPoliciesMap().entrySet()) { + AuthRule authRule = new AuthRule(AuthRule.RuleOperation.AND); + AuthRule principalOr = new AuthRule(AuthRule.RuleOperation.OR); + // principals + List principals = entry.getValue().getPrincipalsList(); + for (Principal principal : principals) { + AuthRule principalAnd = resolvePrincipal(principal); + if (principalAnd != null && !principalAnd.isEmpty()) { + principalOr.addChildren(principalAnd); + } + } + // permission + AuthRule permissionOr = new AuthRule(AuthRule.RuleOperation.OR); + List permissions = entry.getValue().getPermissionsList(); + for (Permission permission : permissions) { + AuthRule permissionAnd = resolvePermission(permission); + if (permissionAnd != null && !permissionAnd.isEmpty()) { + permissionOr.addChildren(permissionAnd); + } + } + if (!principalOr.isEmpty()) { + authRule.addChildren(principalOr); + } + if (!permissionOr.isEmpty()) { + authRule.addChildren(permissionOr); + } + if (authRule.isEmpty()) { + continue; + } + switch (rbac.getAction()) { + case UNRECOGNIZED: + case ALLOW: + allowAuthRules.put(entry.getKey(), authRule); + break; + case DENY: + denyAuthRules.put(entry.getKey(), authRule); + break; + default: + log.warn("Unknown rbac action, {}", rbac.getAction()); + } + } + } + List jwtAuthentications = resolveJWT(httpFilters); + for (JwtAuthentication jwtRule : jwtAuthentications) { + Map jwtProviders = jwtRule.getProvidersMap(); + for (Map.Entry entry : jwtProviders.entrySet()) { + JwtProvider provider = entry.getValue(); + Map fromHeaders = new HashMap<>(); + for (JwtHeader header : provider.getFromHeadersList()) { + fromHeaders.put(header.getName(), header.getValuePrefix()); + } + jwtRules.put(entry.getKey(), + new JwtRule(entry.getKey(), fromHeaders, provider.getIssuer(), + new ArrayList<>(provider.getAudiencesList()), + provider.getLocalJwks().getInlineString(), + new ArrayList<>(provider.getFromParamsList()), + provider.getForwardPayloadHeader(), + provider.getForward())); + } + } + log.info("auth rules resolve finish, RBAC rules size: {}, Jwt rules size: {}", + allowAuthRules.size() + denyAuthRules.size(), jwtRules.size()); + applicationContext.publishEvent(new AuthDataChangedEvent(this, + new AuthRules(allowAuthRules, denyAuthRules, jwtRules))); + return true; + } + + private List resolveRbac(List httpFilters) { + return httpFilters.stream() + .filter(httpFilter -> httpFilter.getName().equals(RBAC_FILTER)) + .map(httpFilter -> { + try { + return httpFilter.getTypedConfig().unpack( + io.envoyproxy.envoy.extensions.filters.http.rbac.v3.RBAC.class); + } + catch (InvalidProtocolBufferException e) { + return null; + } + }).filter(Objects::nonNull) + .map(io.envoyproxy.envoy.extensions.filters.http.rbac.v3.RBAC::getRules) + .collect(Collectors.toList()); + } + + private List resolveHttpFilter(List listeners) { + return listeners.stream() + .filter(listener -> listener.getName().equals(VIRTUAL_INBOUND)) + .map(Listener::getFilterChainsList) + .flatMap(filterChains -> filterChains.stream() + .map(FilterChain::getFiltersList)) + .flatMap(filters -> filters.stream() + .filter(filter -> filter.getName().equals(CONNECTION_MANAGER))) + .map(filter -> unpackHttpConnectionManager(filter.getTypedConfig())) + .filter(Objects::nonNull) + .flatMap(httpConnectionManager -> httpConnectionManager + .getHttpFiltersList().stream()) + .filter(Objects::nonNull).collect(Collectors.toList()); + } + + private List resolveJWT(List httpFilters) { + return httpFilters.stream() + .filter(httpFilter -> httpFilter.getName().equals(JWT_FILTER)) + .map(httpFilter -> { + try { + return httpFilter.getTypedConfig() + .unpack(JwtAuthentication.class); + } + catch (InvalidProtocolBufferException e) { + return null; + } + }).filter(Objects::nonNull).collect(Collectors.toList()); + } + + private HttpConnectionManager unpackHttpConnectionManager(Any any) { + try { + if (!any.is(HttpConnectionManager.class)) { + return null; + } + return any.unpack(HttpConnectionManager.class); + } + catch (InvalidProtocolBufferException e) { + return null; + } + } + + private AuthRule resolvePrincipal(Principal principal) { + Principal.Set andIds = principal.getAndIds(); + AuthRule andChildren = new AuthRule(AuthRule.RuleOperation.AND); + for (Principal andId : andIds.getIdsList()) { + if (andId.getAny()) { + return null; + } + boolean isNot = false; + if (andId.hasNotId()) { + isNot = true; + andId = andId.getNotId(); + } + AuthRule orChildren = new AuthRule(AuthRule.RuleOperation.OR, isNot); + Principal.Set orIds = andId.getOrIds(); + for (Principal orId : orIds.getIdsList()) { + if (orId.hasAuthenticated() + && orId.getAuthenticated().hasPrincipalName()) { + StringMatcher identity = ConvUtil.convStringMatcher( + orId.getAuthenticated().getPrincipalName()); + if (identity != null) { + orChildren.addChildren(new AuthRule(new AuthCondition( + AuthCondition.ValidationType.IDENTITY, identity))); + } + } + if (orId.hasDirectRemoteIp()) { + orChildren.addChildren(new AuthRule(new AuthCondition( + AuthCondition.ValidationType.SOURCE_IP, + ConvUtil.convertIpMatcher(orId.getDirectRemoteIp())))); + } + if (orId.hasRemoteIp()) { + orChildren.addChildren(new AuthRule( + new AuthCondition(AuthCondition.ValidationType.REMOTE_IP, + ConvUtil.convertIpMatcher(orId.getRemoteIp())))); + } + if (orId.hasMetadata() + && ISTIO_AUTHN.equals(orId.getMetadata().getFilter())) { + List segments = orId.getMetadata() + .getPathList(); + switch (segments.get(0).getKey()) { + case REQUEST_AUTH_PRINCIPAL: + if (orId.hasMetadata() && orId.getMetadata().hasValue() + && orId.getMetadata().getValue().hasStringMatch()) { + StringMatcher requestPrinciple = ConvUtil.convStringMatcher( + orId.getMetadata().getValue().getStringMatch()); + if (requestPrinciple != null) { + orChildren.addChildren(new AuthRule(new AuthCondition( + AuthCondition.ValidationType.REQUEST_PRINCIPALS, + requestPrinciple))); + } + } + break; + case REQUEST_AUTH_AUDIENCE: + if (orId.hasMetadata() && orId.getMetadata().hasValue() + && orId.getMetadata().getValue().hasStringMatch()) { + StringMatcher authAudience = ConvUtil.convStringMatcher( + orId.getMetadata().getValue().getStringMatch()); + if (authAudience != null) { + orChildren.addChildren(new AuthRule(new AuthCondition( + AuthCondition.ValidationType.AUTH_AUDIENCES, + authAudience))); + } + } + break; + case REQUEST_AUTH_PRESENTER: + if (orId.hasMetadata() && orId.getMetadata().hasValue() + && orId.getMetadata().getValue().hasStringMatch()) { + StringMatcher authPresenter = ConvUtil.convStringMatcher( + orId.getMetadata().getValue().getStringMatch()); + if (authPresenter != null) { + orChildren.addChildren(new AuthRule(new AuthCondition( + AuthCondition.ValidationType.AUTH_PRESENTERS, + authPresenter))); + } + } + break; + case REQUEST_AUTH_CLAIMS: + if (orId.hasMetadata() && orId.getMetadata().hasValue() + && orId.getMetadata().getValue().hasListMatch()) { + if (segments.size() >= 2) { + String key = segments.get(1).getKey(); + StringMatcher stringMatcher = null; + try { + stringMatcher = ConvUtil.convStringMatcher( + orId.getMetadata().getValue().getListMatch() + .getOneOf().getStringMatch()); + } + catch (Exception e) { + log.error( + "unable to get/convert request auth claims"); + } + orChildren.addChildren(new AuthRule(new AuthCondition( + AuthCondition.ValidationType.AUTH_CLAIMS, key, + stringMatcher))); + } + } + break; + default: + } + } + if (orId.hasHeader()) { + String headerName = orId.getHeader().getName(); + if (StringUtils.isEmpty(headerName)) { + continue; + } + StringMatcher stringMatcher = ConvUtil + .convertHeaderMatcher(orId.getHeader()); + orChildren.addChildren(new AuthRule( + new AuthCondition(AuthCondition.ValidationType.HEADER, + headerName, stringMatcher))); + } + } + if (!orChildren.isEmpty()) { + andChildren.addChildren(orChildren); + } + } + return andChildren; + } + + private AuthRule resolvePermission(Permission permission) { + Permission.Set andRules = permission.getAndRules(); + AuthRule andChildren = new AuthRule(AuthRule.RuleOperation.AND); + for (Permission andRule : andRules.getRulesList()) { + if (andRule.getAny()) { + return null; + } + boolean isNot = false; + if (andRule.hasNotRule()) { + isNot = true; + andRule = andRule.getNotRule(); + } + Permission.Set orRules = andRule.getOrRules(); + AuthRule orChildren = new AuthRule(AuthRule.RuleOperation.OR, isNot); + for (Permission orRule : orRules.getRulesList()) { + int port = orRule.getDestinationPort(); + if (port > MIN_PORT && port <= MAX_PORT) { + orChildren.addChildren(new AuthRule(new AuthCondition( + AuthCondition.ValidationType.PORTS, new PortMatcher(port)))); + } + if (orRule.hasHeader()) { + switch (orRule.getHeader().getName()) { + case HEADER_NAME_AUTHORITY: + StringMatcher host = ConvUtil + .convStringMatcher(orRule.getHeader()); + if (host != null) { + orChildren.addChildren(new AuthRule(new AuthCondition( + AuthCondition.ValidationType.HOSTS, host))); + } + break; + case HEADER_NAME_METHOD: + StringMatcher method = ConvUtil + .convStringMatcher(orRule.getHeader()); + if (method != null) { + orChildren.addChildren(new AuthRule(new AuthCondition( + AuthCondition.ValidationType.METHODS, method))); + } + break; + } + } + if (orRule.hasUrlPath() && orRule.getUrlPath().hasPath()) { + StringMatcher path = ConvUtil + .convStringMatcher(orRule.getUrlPath().getPath()); + if (path != null) { + orChildren.addChildren(new AuthRule(new AuthCondition( + AuthCondition.ValidationType.PATHS, path))); + } + } + if (orRule.hasDestinationIp()) { + orChildren.addChildren(new AuthRule(new AuthCondition( + AuthCondition.ValidationType.DEST_IP, + ConvUtil.convertIpMatcher(orRule.getDestinationIp())))); + } + } + if (!orChildren.isEmpty()) { + andChildren.addChildren(orChildren); + } + } + return andChildren; + } + + @Override + public String getTypeUrl() { + return IstioConstants.LDS_URL; + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/filter/impl/LabelRoutingXdsResolveFilter.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/filter/impl/LabelRoutingXdsResolveFilter.java new file mode 100644 index 000000000..e6257efb8 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/filter/impl/LabelRoutingXdsResolveFilter.java @@ -0,0 +1,176 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.governance.istio.filter.impl; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.alibaba.cloud.commons.governance.event.LabelRoutingDataChangedEvent; +import com.alibaba.cloud.commons.governance.labelrouting.LabelRouteRule; +import com.alibaba.cloud.commons.governance.labelrouting.MatchService; +import com.alibaba.cloud.commons.governance.labelrouting.UnifiedRouteDataStructure; +import com.alibaba.cloud.commons.governance.labelrouting.rule.HeaderRule; +import com.alibaba.cloud.commons.governance.labelrouting.rule.RouteRule; +import com.alibaba.cloud.commons.governance.labelrouting.rule.UrlRule; +import com.alibaba.cloud.commons.lang.StringUtils; +import com.alibaba.cloud.commons.matcher.StringMatcherType; +import com.alibaba.cloud.governance.istio.constant.IstioConstants; +import com.alibaba.cloud.governance.istio.filter.AbstractXdsResolveFilter; +import com.alibaba.cloud.governance.istio.util.ConvUtil; +import io.envoyproxy.envoy.config.route.v3.HeaderMatcher; +import io.envoyproxy.envoy.config.route.v3.QueryParameterMatcher; +import io.envoyproxy.envoy.config.route.v3.Route; +import io.envoyproxy.envoy.config.route.v3.RouteConfiguration; +import io.envoyproxy.envoy.config.route.v3.RouteMatch; +import io.envoyproxy.envoy.config.route.v3.VirtualHost; +import io.envoyproxy.envoy.config.route.v3.WeightedCluster; + +/** + * @author musi + * @author + */ +public class LabelRoutingXdsResolveFilter + extends AbstractXdsResolveFilter> { + + @Override + public boolean resolve(List routeConfigurations) { + if (routeConfigurations == null) { + return false; + } + Map untiedRouteDataStructures = new HashMap<>(); + for (RouteConfiguration routeConfiguration : routeConfigurations) { + List virtualHosts = routeConfiguration.getVirtualHostsList(); + for (VirtualHost virtualHost : virtualHosts) { + UnifiedRouteDataStructure unifiedRouteDataStructure = new UnifiedRouteDataStructure(); + String targetService = ""; + String[] serviceAndPort = virtualHost.getName().split(":"); + if (serviceAndPort.length > 0) { + targetService = serviceAndPort[0].split("\\.")[0]; + } + if (ALLOW_ANY.equals(targetService)) { + continue; + } + unifiedRouteDataStructure.setTargetService(targetService); + List routes = virtualHost.getRoutesList(); + LabelRouteRule labelRouteRule = getLabelRouteData(routes); + unifiedRouteDataStructure.setLabelRouteRule(labelRouteRule); + untiedRouteDataStructures.put( + unifiedRouteDataStructure.getTargetService(), + unifiedRouteDataStructure); + } + } + applicationContext.publishEvent(new LabelRoutingDataChangedEvent(this, + untiedRouteDataStructures.values())); + return true; + } + + private LabelRouteRule getLabelRouteData(List routes) { + List matchServices = new ArrayList<>(); + LabelRouteRule labelRouteRule = new LabelRouteRule(); + for (Route route : routes) { + String cluster = route.getRoute().getCluster(); + if (StringUtils.isNotEmpty(cluster)) { + MatchService matchService = getMatchService(route, cluster, 100); + matchServices.add(matchService); + } + WeightedCluster weightedCluster = route.getRoute().getWeightedClusters(); + for (WeightedCluster.ClusterWeight clusterWeight : weightedCluster + .getClustersList()) { + MatchService matchService = getMatchService(route, + clusterWeight.getName(), clusterWeight.getWeight().getValue()); + matchServices.add(matchService); + } + } + labelRouteRule.setMatchRouteList(matchServices); + if (!matchServices.isEmpty()) { + labelRouteRule.setDefaultRouteVersion( + matchServices.get(matchServices.size() - 1).getVersion()); + } + return labelRouteRule; + } + + private MatchService getMatchService(Route route, String cluster, int weight) { + String version = ""; + try { + String[] info = cluster.split("\\|"); + version = info[2]; + } + catch (Exception e) { + log.error("invalid cluster info for route {}", route.getName()); + } + MatchService matchService = new MatchService(); + matchService.setVersion(version); + matchService.setRuleList(match2RouteRules(route.getMatch())); + matchService.setWeight(weight); + return matchService; + } + + private List match2RouteRules(RouteMatch routeMatch) { + List routeRules = new ArrayList<>(); + for (HeaderMatcher headerMatcher : routeMatch.getHeadersList()) { + HeaderRule headerRule = ConvUtil.headerMatcher2HeaderRule(headerMatcher); + if (headerRule != null) { + routeRules.add(headerRule); + } + } + + for (QueryParameterMatcher parameterMatcher : routeMatch + .getQueryParametersList()) { + UrlRule.Parameter parameter = ConvUtil + .parameterMatcher2ParameterRule(parameterMatcher); + if (parameter != null) { + routeRules.add(parameter); + } + } + + UrlRule.Path path = new UrlRule.Path(); + path.setType(PATH); + switch (routeMatch.getPathSpecifierCase()) { + case PREFIX: + path.setCondition(StringMatcherType.PREFIX.toString()); + path.setValue(routeMatch.getPrefix()); + break; + + case PATH: + path.setCondition(StringMatcherType.EXACT.toString()); + path.setValue(routeMatch.getPath()); + break; + + case SAFE_REGEX: + path.setCondition(StringMatcherType.REGEX.toString()); + path.setValue(routeMatch.getSafeRegex().getRegex()); + break; + + default: + // unknown type + path = null; + + } + if (path != null) { + routeRules.add(path); + } + return routeRules; + } + + @Override + public String getTypeUrl() { + return IstioConstants.RDS_URL; + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/protocol/AbstractXdsProtocol.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/protocol/AbstractXdsProtocol.java new file mode 100644 index 000000000..9bc23a152 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/protocol/AbstractXdsProtocol.java @@ -0,0 +1,301 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.governance.istio.protocol; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Consumer; + +import com.alibaba.cloud.governance.istio.NodeBuilder; +import com.alibaba.cloud.governance.istio.XdsChannel; +import com.alibaba.cloud.governance.istio.XdsConfigProperties; +import com.alibaba.cloud.governance.istio.XdsScheduledThreadPool; +import com.alibaba.cloud.governance.istio.constant.IstioConstants; +import com.alibaba.cloud.governance.istio.filter.XdsResolveFilter; +import io.envoyproxy.envoy.config.core.v3.Node; +import io.envoyproxy.envoy.service.discovery.v3.DiscoveryRequest; +import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse; +import io.grpc.stub.StreamObserver; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; + +/** + * @author musi + * @author + */ +public abstract class AbstractXdsProtocol + implements XdsProtocol, ApplicationContextAware { + + protected static final Logger log = LoggerFactory + .getLogger(AbstractXdsProtocol.class); + + protected XdsChannel xdsChannel; + + protected final Node node = NodeBuilder.getNode(); + + protected XdsConfigProperties xdsConfigProperties; + + protected List>> filters = new ArrayList<>(); + + private Set resourceNames = new HashSet<>(); + + private final XdsScheduledThreadPool xdsScheduledThreadPool; + + /** + * does the protocol need polling. + */ + private boolean needPolling; + + /** + * send event to submodules. + */ + protected ApplicationContext applicationContext; + + private final Map> requestObserverMap = new ConcurrentHashMap<>(); + + private final Map>> futureMap = new ConcurrentHashMap<>(); + + private final Map> requestResource = new ConcurrentHashMap<>(); + + protected final static AtomicLong requestId = new AtomicLong(0); + + public AbstractXdsProtocol(XdsChannel xdsChannel, + XdsScheduledThreadPool xdsScheduledThreadPool, + XdsConfigProperties xdsConfigProperties) { + this.xdsChannel = xdsChannel; + this.xdsScheduledThreadPool = xdsScheduledThreadPool; + this.xdsConfigProperties = xdsConfigProperties; + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) + throws BeansException { + this.applicationContext = applicationContext; + } + + public void setNeedPolling(boolean needPolling) { + this.needPolling = needPolling; + } + + @Override + public synchronized long observeResource(Set resourceNames, + Consumer> consumer) { + long id = getDefaultRequestId(); + if (resourceNames == null) { + resourceNames = new HashSet<>(); + } + requestResource.put(id, resourceNames); + try { + consumer.accept(doGetResource(id, resourceNames, consumer)); + } + catch (Exception e) { + log.error("error on get observe resource from xds", e); + } + if (needPolling) { + xdsScheduledThreadPool.scheduleAtFixedRate(() -> { + try { + consumer.accept(doGetResource(id, requestResource.get(id), consumer)); + } + catch (Exception e) { + log.error("error on get observe resource from xds", e); + } + }, xdsConfigProperties.getPollingTime(), xdsConfigProperties.getPollingTime(), + TimeUnit.SECONDS); + needPolling = false; + } + return id; + } + + @Override + public List getResource(Set resourceNames) { + long id = requestId.getAndDecrement(); + List source = doGetResource(id, resourceNames, null); + requestObserverMap.remove(id); + return source; + } + + public Set getResourceNames() { + return resourceNames; + } + + private List doGetResource(long id, Set resourceNames, + Consumer> consumer) { + if (resourceNames == null) { + resourceNames = new HashSet<>(); + } + CompletableFuture> future = new CompletableFuture<>(); + futureMap.put(id, future); + StreamObserver requestObserver = requestObserverMap.get(id); + if (requestObserver == null) { + // reuse observer + requestObserver = xdsChannel + .createDiscoveryRequest(new XdsObserver(id, consumer)); + // requestObserver may be null when testing + if (requestObserver != null) { + requestObserverMap.put(id, requestObserver); + } + } + sendXdsRequest(requestObserver, resourceNames); + try { + return future.get(); + } + catch (ExecutionException | InterruptedException e) { + return null; + } + finally { + futureMap.remove(id); + } + } + + protected abstract List decodeXdsResponse(DiscoveryResponse response); + + protected Set resolveResourceNames(List resources) { + return new HashSet<>(); + } + + protected void fireXdsFilters(List resources) { + try { + this.resourceNames = resolveResourceNames(resources); + } + catch (Exception e) { + log.error("Error on resolving resource names from {}", resources); + } + for (XdsResolveFilter> filter : filters) { + try { + if (!filter.resolve(resources)) { + return; + } + } + catch (Exception e) { + log.error("Error on executing Xds filter {}", filter.getClass().getName(), + e); + } + } + + } + + private void sendXdsRequest(StreamObserver observer, + Set resourceNames) { + DiscoveryRequest request = DiscoveryRequest.newBuilder().setNode(node) + .setTypeUrl(getTypeUrl()).addAllResourceNames(resourceNames).build(); + observer.onNext(request); + } + + private void sendAckRequest(long id, DiscoveryResponse response) { + StreamObserver observer = requestObserverMap.get(id); + if (observer == null) { + return; + } + DiscoveryRequest request = DiscoveryRequest.newBuilder() + .setVersionInfo(response.getVersionInfo()).setNode(node) + .addAllResourceNames(requestResource.get(id) == null ? new ArrayList<>() + : requestResource.get(id)) + .setTypeUrl(response.getTypeUrl()).setResponseNonce(response.getNonce()) + .build(); + observer.onNext(request); + } + + private int getDefaultRequestId() { + switch (getTypeUrl()) { + case IstioConstants.CDS_URL: + return -1; + case IstioConstants.EDS_URL: + return -2; + case IstioConstants.LDS_URL: + return -3; + case IstioConstants.RDS_URL: + return -4; + } + throw new UnsupportedOperationException("Unknown type url"); + } + + private class XdsObserver implements StreamObserver { + + private Consumer> consumer; + + private long id; + + XdsObserver(long id, Consumer> consumer) { + this.id = id; + this.consumer = consumer; + } + + @Override + public void onNext(DiscoveryResponse discoveryResponse) { + if (xdsChannel == null) { + return; + } + if (xdsConfigProperties.isLogXds()) { + log.info("receive notification from xds server, type: " + getTypeUrl() + + " requestId: " + id); + } + List responses = decodeXdsResponse(discoveryResponse); + CompletableFuture> future = futureMap.get(id); + if (future == null) { + // means it is push operation from xds, consume it directly + consumer.accept(responses); + sendAckRequest(id, discoveryResponse); + return; + } + future.complete(responses); + sendAckRequest(id, discoveryResponse); + } + + @Override + public void onError(Throwable throwable) { + if (xdsChannel == null) { + return; + } + if (xdsConfigProperties.isLogXds()) { + log.error("connect to xds server failed, reconnecting", throwable); + } + CompletableFuture> future = futureMap.get(id); + if (future != null) { + future.complete(null); + futureMap.remove(id); + } + requestResource.remove(id); + // refresh token again + xdsChannel.refreshIstiodToken(); + // reconnected immediately + StreamObserver observer = xdsChannel + .createDiscoveryRequest(new XdsObserver(id, consumer)); + if (observer != null) { + requestObserverMap.put(id, observer); + } + } + + @Override + public void onCompleted() { + log.info("xds connect completed"); + } + + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/protocol/XdsProtocol.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/protocol/XdsProtocol.java new file mode 100644 index 000000000..a1d750820 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/protocol/XdsProtocol.java @@ -0,0 +1,35 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.governance.istio.protocol; + +import java.util.List; +import java.util.Set; +import java.util.function.Consumer; + +/** + * @author musi + * @author + */ +public interface XdsProtocol { + + List getResource(Set resourceNames); + + String getTypeUrl(); + + long observeResource(Set resourceNames, Consumer> consumer); + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/protocol/impl/CdsProtocol.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/protocol/impl/CdsProtocol.java new file mode 100644 index 000000000..109288cb9 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/protocol/impl/CdsProtocol.java @@ -0,0 +1,79 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.governance.istio.protocol.impl; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import com.alibaba.cloud.governance.istio.XdsChannel; +import com.alibaba.cloud.governance.istio.XdsConfigProperties; +import com.alibaba.cloud.governance.istio.XdsScheduledThreadPool; +import com.alibaba.cloud.governance.istio.constant.IstioConstants; +import com.alibaba.cloud.governance.istio.protocol.AbstractXdsProtocol; +import io.envoyproxy.envoy.config.cluster.v3.Cluster; +import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse; + +/** + * @author musi + * @author CdsProtocol contains infomation about + * service. + */ +public class CdsProtocol extends AbstractXdsProtocol { + + public CdsProtocol(XdsChannel xdsChannel, + XdsScheduledThreadPool xdsScheduledThreadPool, + XdsConfigProperties xdsConfigProperties) { + super(xdsChannel, xdsScheduledThreadPool, xdsConfigProperties); + } + + @Override + protected List decodeXdsResponse(DiscoveryResponse response) { + List clusters = new ArrayList<>(); + for (com.google.protobuf.Any res : response.getResourcesList()) { + try { + Cluster cluster = res.unpack(Cluster.class); + clusters.add(cluster); + } + catch (Exception e) { + log.error("unpack cluster failed", e); + } + } + fireXdsFilters(clusters); + return clusters; + } + + @Override + protected Set resolveResourceNames(List resources) { + Set endpoints = new HashSet<>(); + if (resources == null) { + return endpoints; + } + for (Cluster cluster : resources) { + cluster.getEdsClusterConfig().getServiceName(); + endpoints.add(cluster.getEdsClusterConfig().getServiceName()); + } + return endpoints; + } + + @Override + public String getTypeUrl() { + return IstioConstants.CDS_URL; + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/protocol/impl/EdsProtocol.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/protocol/impl/EdsProtocol.java new file mode 100644 index 000000000..974c25053 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/protocol/impl/EdsProtocol.java @@ -0,0 +1,63 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.governance.istio.protocol.impl; + +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.cloud.governance.istio.XdsChannel; +import com.alibaba.cloud.governance.istio.XdsConfigProperties; +import com.alibaba.cloud.governance.istio.XdsScheduledThreadPool; +import com.alibaba.cloud.governance.istio.constant.IstioConstants; +import com.alibaba.cloud.governance.istio.protocol.AbstractXdsProtocol; +import io.envoyproxy.envoy.config.endpoint.v3.ClusterLoadAssignment; +import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse; + +/** + * @author musi + * @author TODO: Fetch all endpoints in EdsProtocol. + */ +public class EdsProtocol extends AbstractXdsProtocol { + + public EdsProtocol(XdsChannel xdsChannel, + XdsScheduledThreadPool xdsScheduledThreadPool, + XdsConfigProperties xdsConfigProperties) { + super(xdsChannel, xdsScheduledThreadPool, xdsConfigProperties); + } + + @Override + protected List decodeXdsResponse(DiscoveryResponse response) { + List endpoints = new ArrayList<>(); + for (com.google.protobuf.Any res : response.getResourcesList()) { + try { + ClusterLoadAssignment endpoint = res.unpack(ClusterLoadAssignment.class); + endpoints.add(endpoint); + } + catch (Exception e) { + log.error("unpack cluster failed", e); + } + } + fireXdsFilters(endpoints); + return endpoints; + } + + @Override + public String getTypeUrl() { + return IstioConstants.EDS_URL; + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/protocol/impl/LdsProtocol.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/protocol/impl/LdsProtocol.java new file mode 100644 index 000000000..9b7ced048 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/protocol/impl/LdsProtocol.java @@ -0,0 +1,105 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.governance.istio.protocol.impl; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +import com.alibaba.cloud.commons.lang.StringUtils; +import com.alibaba.cloud.governance.istio.XdsChannel; +import com.alibaba.cloud.governance.istio.XdsConfigProperties; +import com.alibaba.cloud.governance.istio.XdsScheduledThreadPool; +import com.alibaba.cloud.governance.istio.constant.IstioConstants; +import com.alibaba.cloud.governance.istio.filter.XdsResolveFilter; +import com.alibaba.cloud.governance.istio.protocol.AbstractXdsProtocol; +import com.google.protobuf.InvalidProtocolBufferException; +import io.envoyproxy.envoy.config.listener.v3.Filter; +import io.envoyproxy.envoy.config.listener.v3.Listener; +import io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager; +import io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.Rds; +import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse; + +/** + * @author musi + * @author LdsProtocol contains the authentication + * configuration and other configuration about security. + */ +public class LdsProtocol extends AbstractXdsProtocol { + + public LdsProtocol(XdsChannel xdsChannel, + XdsScheduledThreadPool xdsScheduledThreadPool, + XdsConfigProperties xdsConfigProperties, + List>> ldsFilters) { + super(xdsChannel, xdsScheduledThreadPool, xdsConfigProperties); + // init filters + for (XdsResolveFilter> filter : ldsFilters) { + if (IstioConstants.LDS_URL.equals(filter.getTypeUrl())) { + filters.add(filter); + } + } + } + + @Override + public String getTypeUrl() { + return IstioConstants.LDS_URL; + } + + @Override + public List decodeXdsResponse(DiscoveryResponse response) { + List listeners = new ArrayList<>(); + for (com.google.protobuf.Any res : response.getResourcesList()) { + try { + Listener listener = res.unpack(Listener.class); + if (listener != null) { + listeners.add(listener); + } + } + catch (Exception e) { + log.error("unpack listeners failed", e); + } + } + fireXdsFilters(listeners); + return listeners; + } + + @Override + protected Set resolveResourceNames(List resources) { + Set routeNames = new HashSet<>(); + resources.forEach(listener -> routeNames.addAll(listener.getFilterChainsList() + .stream().flatMap((e) -> e.getFiltersList().stream()) + .map(Filter::getTypedConfig).map(any -> { + try { + if (!any.is(HttpConnectionManager.class)) { + return null; + } + return any.unpack(HttpConnectionManager.class); + } + catch (InvalidProtocolBufferException e) { + return null; + } + }).filter(Objects::nonNull).map(HttpConnectionManager::getRds) + .map(Rds::getRouteConfigName).filter(StringUtils::isNotEmpty) + .collect(Collectors.toList()))); + return routeNames; + + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/protocol/impl/RdsProtocol.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/protocol/impl/RdsProtocol.java new file mode 100644 index 000000000..d9b8475ac --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/protocol/impl/RdsProtocol.java @@ -0,0 +1,70 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.governance.istio.protocol.impl; + +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.cloud.governance.istio.XdsChannel; +import com.alibaba.cloud.governance.istio.XdsConfigProperties; +import com.alibaba.cloud.governance.istio.XdsScheduledThreadPool; +import com.alibaba.cloud.governance.istio.constant.IstioConstants; +import com.alibaba.cloud.governance.istio.filter.XdsResolveFilter; +import com.alibaba.cloud.governance.istio.protocol.AbstractXdsProtocol; +import io.envoyproxy.envoy.config.route.v3.RouteConfiguration; +import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse; + +/** + * @author musi + * @author RdsProtocol contains route info. + */ +public class RdsProtocol extends AbstractXdsProtocol { + + public RdsProtocol(XdsChannel xdsChannel, + XdsScheduledThreadPool xdsScheduledThreadPool, + XdsConfigProperties xdsConfigProperties, + List>> rdsFilters) { + super(xdsChannel, xdsScheduledThreadPool, xdsConfigProperties); + for (XdsResolveFilter> filter : rdsFilters) { + if (IstioConstants.RDS_URL.equals(filter.getTypeUrl())) { + filters.add(filter); + } + } + } + + @Override + public List decodeXdsResponse(DiscoveryResponse response) { + List routes = new ArrayList<>(); + for (com.google.protobuf.Any res : response.getResourcesList()) { + try { + RouteConfiguration route = res.unpack(RouteConfiguration.class); + routes.add(route); + } + catch (Exception e) { + log.error("unpack cluster failed", e); + } + } + fireXdsFilters(routes); + return routes; + } + + @Override + public String getTypeUrl() { + return IstioConstants.RDS_URL; + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/util/ConvUtil.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/util/ConvUtil.java new file mode 100644 index 000000000..757c03f54 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/java/com/alibaba/cloud/governance/istio/util/ConvUtil.java @@ -0,0 +1,156 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.governance.istio.util; + +import com.alibaba.cloud.commons.governance.labelrouting.rule.HeaderRule; +import com.alibaba.cloud.commons.governance.labelrouting.rule.UrlRule; +import com.alibaba.cloud.commons.lang.StringUtils; +import com.alibaba.cloud.commons.matcher.IpMatcher; +import com.alibaba.cloud.commons.matcher.StringMatcher; +import com.alibaba.cloud.commons.matcher.StringMatcherType; +import io.envoyproxy.envoy.config.core.v3.CidrRange; +import io.envoyproxy.envoy.config.route.v3.HeaderMatcher; +import io.envoyproxy.envoy.config.route.v3.QueryParameterMatcher; +import io.envoyproxy.envoy.type.matcher.v3.RegexMatcher; + +/** + * @author musi + * @author + */ +public final class ConvUtil { + + private static final String HEADER = "header"; + + private static final String PARAMETER = "parameter"; + + private ConvUtil() { + + } + + public static StringMatcher convStringMatcher( + io.envoyproxy.envoy.type.matcher.v3.StringMatcher stringMatcher) { + if (stringMatcher == null) { + return null; + } + boolean isIgnoreCase = stringMatcher.getIgnoreCase(); + String exact = stringMatcher.getExact(); + String prefix = stringMatcher.getPrefix(); + String suffix = stringMatcher.getSuffix(); + String contains = stringMatcher.getContains(); + String regex = stringMatcher.getSafeRegex().getRegex(); + if (StringUtils.isNotBlank(exact)) { + return new StringMatcher(exact, StringMatcherType.EXACT, isIgnoreCase); + } + if (StringUtils.isNotBlank(prefix)) { + return new StringMatcher(prefix, StringMatcherType.PREFIX, isIgnoreCase); + } + if (StringUtils.isNotBlank(suffix)) { + return new StringMatcher(suffix, StringMatcherType.SUFFIX, isIgnoreCase); + } + if (StringUtils.isNotBlank(contains)) { + return new StringMatcher(contains, StringMatcherType.CONTAIN, isIgnoreCase); + } + if (StringUtils.isNotBlank(regex)) { + return new StringMatcher(regex); + } + return null; + } + + public static StringMatcher convStringMatcher( + io.envoyproxy.envoy.config.route.v3.HeaderMatcher headerMatcher) { + return convStringMatcher(headerMatch2StringMatch(headerMatcher)); + } + + public static IpMatcher convertIpMatcher(CidrRange cidrRange) { + return new IpMatcher(cidrRange.getPrefixLen().getValue(), + cidrRange.getAddressPrefix()); + } + + public static StringMatcher convertHeaderMatcher( + io.envoyproxy.envoy.config.route.v3.HeaderMatcher headerMatcher) { + return convStringMatcher(headerMatch2StringMatch(headerMatcher)); + } + + public static io.envoyproxy.envoy.type.matcher.v3.StringMatcher headerMatch2StringMatch( + io.envoyproxy.envoy.config.route.v3.HeaderMatcher headerMatcher) { + if (headerMatcher == null) { + return null; + } + if (headerMatcher.getPresentMatch()) { + io.envoyproxy.envoy.type.matcher.v3.StringMatcher.Builder builder = io.envoyproxy.envoy.type.matcher.v3.StringMatcher + .newBuilder(); + return builder.setSafeRegex(RegexMatcher.newBuilder().build()) + .setIgnoreCase(true).build(); + } + if (!headerMatcher.hasStringMatch()) { + io.envoyproxy.envoy.type.matcher.v3.StringMatcher.Builder builder = io.envoyproxy.envoy.type.matcher.v3.StringMatcher + .newBuilder(); + String exactMatch = headerMatcher.getExactMatch(); + String containsMatch = headerMatcher.getContainsMatch(); + String prefixMatch = headerMatcher.getPrefixMatch(); + String suffixMatch = headerMatcher.getSuffixMatch(); + RegexMatcher safeRegex = headerMatcher.getSafeRegexMatch(); + if (!StringUtils.isEmpty(exactMatch)) { + builder.setExact(exactMatch); + } + else if (!StringUtils.isEmpty(containsMatch)) { + builder.setContains(containsMatch); + } + else if (!StringUtils.isEmpty(prefixMatch)) { + builder.setPrefix(prefixMatch); + } + else if (!StringUtils.isEmpty(suffixMatch)) { + builder.setSuffix(suffixMatch); + } + else if (safeRegex.isInitialized()) { + builder.setSafeRegex(safeRegex); + } + return builder.setIgnoreCase(true).build(); + } + return headerMatcher.getStringMatch(); + } + + public static UrlRule.Parameter parameterMatcher2ParameterRule( + QueryParameterMatcher queryParameterMatcher) { + UrlRule.Parameter parameter = new UrlRule.Parameter(); + StringMatcher stringMatcher = ConvUtil + .convStringMatcher(queryParameterMatcher.getStringMatch()); + if (stringMatcher != null) { + parameter.setCondition(stringMatcher.getType().toString()); + parameter.setKey(queryParameterMatcher.getName()); + parameter.setValue(stringMatcher.getMatcher()); + parameter.setType(PARAMETER); + return parameter; + } + return null; + } + + public static HeaderRule headerMatcher2HeaderRule(HeaderMatcher headerMatcher) { + StringMatcher stringMatcher = ConvUtil + .convStringMatcher(ConvUtil.headerMatch2StringMatch(headerMatcher)); + if (stringMatcher != null) { + HeaderRule headerRule = new HeaderRule(); + headerRule.setCondition(stringMatcher.getType().toString()); + headerRule.setKey(headerMatcher.getName()); + headerRule.setValue(stringMatcher.getMatcher()); + headerRule.setType(HEADER); + return headerRule; + } + return null; + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/resources/META-INF/spring.factories b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/resources/META-INF/spring.factories new file mode 100644 index 000000000..62d6c1d93 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +com.alibaba.cloud.governance.istio.XdsAutoConfiguration diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/test/java/com/alibaba/cloud/governance/istio/XdsRulesTests.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/test/java/com/alibaba/cloud/governance/istio/XdsRulesTests.java new file mode 100644 index 000000000..a76e008a7 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/test/java/com/alibaba/cloud/governance/istio/XdsRulesTests.java @@ -0,0 +1,143 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.governance.istio; + +import java.io.File; +import java.io.FileInputStream; +import java.util.List; + +import com.alibaba.cloud.commons.io.FileUtils; +import com.alibaba.cloud.governance.auth.AuthenticationAutoConfiguration; +import com.alibaba.cloud.governance.auth.repository.AuthRepository; +import com.alibaba.cloud.governance.istio.protocol.impl.LdsProtocol; +import com.alibaba.cloud.governance.istio.protocol.impl.RdsProtocol; +import com.alibaba.cloud.router.LabelRoutingAutoConfiguration; +import com.alibaba.cloud.router.repository.FilterService; +import com.alibaba.cloud.router.repository.RouteDataRepository; +import com.alibaba.fastjson.JSONObject; +import io.envoyproxy.envoy.config.listener.v3.Listener; +import io.envoyproxy.envoy.config.route.v3.RouteConfiguration; +import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.ImportAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.NONE; + +/** + * @author musi + * @author + */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = XdsRulesTests.TestConfig.class, + properties = { "spring.cloud.istio.config.port=15010", + "spring.cloud.istio.config.enabled=true", + "spring.cloud.istio.config.log-xds=false", + "spring.cloud.nacos.discovery.watch.enabled=false" }, + webEnvironment = NONE) +@EnableFeignClients +public class XdsRulesTests { + + private static final Logger log = LoggerFactory.getLogger(XdsRulesTests.class); + + private static final String TARGET_SERVICE = "service-provider"; + + @Autowired + private AuthRepository authRepository; + + @Autowired + private RouteDataRepository routeDataRepository; + + @Autowired + private LdsProtocol ldsProtocol; + + @Autowired + private RdsProtocol rdsProtocol; + + private DiscoveryResponse decodeResponse(String path) throws Exception { + File file = new File(path); + FileInputStream stream = FileUtils.openInputStream(file); + byte[] bytes = new byte[(int) file.length()]; + int readBytes = stream.read(bytes); + if (readBytes == -1) { + throw new Exception("Unreadable response file"); + } + return DiscoveryResponse.parseFrom(bytes); + } + + @Test + public void testAuthTransform() throws Exception { + DiscoveryResponse discoveryResponse = decodeResponse( + "src/test/resources/LdsResponse.in"); + List listeners = ldsProtocol.decodeXdsResponse(discoveryResponse); + if (listeners == null) { + throw new Exception("Can not parse listeners from xds response"); + } + log.info("Auth rules are {}", JSONObject.toJSONString(authRepository)); + Assert.assertEquals(authRepository.getAllowAuthRules().size(), 1); + Assert.assertEquals(authRepository.getDenyAuthRules().size(), 1); + Assert.assertEquals(authRepository.getJwtRules().size(), 1); + } + + @Test + public void testLabelRoutingTransform() throws Exception { + DiscoveryResponse discoveryResponse = decodeResponse( + "src/test/resources/RdsResponse.in"); + List routeConfigurations = rdsProtocol + .decodeXdsResponse(discoveryResponse); + if (routeConfigurations == null) { + throw new Exception("Can not parse route configurations from xds response"); + } + if (routeDataRepository.getRouteRule(TARGET_SERVICE) == null) { + throw new Exception("Can not get target service from route configurations"); + } + log.info("Label routing rules are {}", JSONObject + .toJSONString(routeDataRepository.getRouteRule(TARGET_SERVICE))); + } + + /** + * dummy class for label routing filter service. + */ + static class Dummy { + + } + + @Configuration + @EnableAutoConfiguration + @ImportAutoConfiguration({ XdsAutoConfiguration.class, + AuthenticationAutoConfiguration.class, LabelRoutingAutoConfiguration.class }) + public static class TestConfig { + + @Bean(name = TARGET_SERVICE + FilterService.FEIGN_CLIENT_BEAN_SPECIFICATION) + public Dummy dummy() { + return new Dummy(); + } + + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/test/resources/LdsResponse.in b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/test/resources/LdsResponse.in new file mode 100644 index 0000000000000000000000000000000000000000..6101b1bc9b27b423d2e9957d525ed39108e4b874 GIT binary patch literal 128649 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 ZZwdRFF`>L9%J_gTV$Ux7iH|<|{{hX@Q+)sc literal 0 HcmV?d00001 diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/test/resources/RdsResponse.in b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-istio/src/test/resources/RdsResponse.in new file mode 100644 index 0000000000000000000000000000000000000000..233e92c05b2bcae2e7cb4f63a5b9899aabd119ba GIT binary patch literal 18190 zcmeI4UyS2M8NfH2%l+ZVt{vNP5+`>@%WaW*oRF%*1C)o8*LJeu z5<9Y;?5&zUK&2i?Kpzl1ph}b;2&ttl;*SJ~r=HsKfPe@3R3VijS|KD5Am{_C#5ZHd z>*So+j#u#Dds=(un{Q^n`OP*PG4i& zhSBb7a=&A-iqR`;`q0>cC%x5av!*eyHFj8GA0`hQG;v_ccE`~1FY_qRS2>=;@8-~2 z$AUzarGFS}$Fj8^YYi1v=?>u8WV?nUcbks@ zjzv5Lcp}|44e(MXdu^}@4rp135peuXUNA7v_ zuhD2vj7q)ot4UWVPwYdvhA39?FY-vBIJWx(M!ViOOtK`A&7Q^dO(STbLW?;FM=gUF zsp8%_&B7<*AluoT|NK5@A zC9(%7b6_Etq4}U(I}eTsBgWQl!!O8i0hF(hGH?6W)34w=Fcrs2-x_$>v}{a#I4g0$g^p4JWbE> z(5X3&?Irj)pp_hH0IeiAiJq1LFC#4Ty{0fFP!D zJtjFIiG<`{OvS%R>Uh7JL+35qFy%J1+0Z-iNF8uT(L!cso)=&w{wkZSJh(B1i(N= zPln3jsH)%t2(1#|I&gBa7ApR7GI&J85H5oMJMP z*?`Np!3UBL-g-OS2ZqYwsH*n6s_3zGxV!bF_T-|xx+X-)wrc_pAYkaoLHNBq!K z4v;n}>WF!y@e%}32)oA{&N~(I&$UxZ?vO?TG$w;4rsvvFlkLSJ#`ltLG~UW0-uwJE zK0o#n0{gka;TIMv1zV4Qy3=J9RlG`~*6Lr$J(spw^Sp1?Y`{t`j`iqfBC-JGYk+|-WNOlw<#sVMrw_KbA=K`2n$ly~rI+3{>}V@2ySvB)C`m&-~W%s`=4?Ctkkh|kHmn}tR zrqW6q=%dm9eS&5{!_#w3hg_g(_#Ny}7)3W<;df5Z$p59Ic~p+xQtE@!+^u|!ZUv{4 z`1aMSNlz1>$f3p}t0lN*Ch!=-UlNpt)5XW#aHxyu{KBp9&w4*f+p=xL^D4AQ+{;em z^;figG(BzHlc?t1Gfr}4NuL{|+Ne~jm_#+lE(F~8qMG9t_v4b{sAgh*|Dkkfhfzvy zCG|I7$RSD5TX3gVR<<-ob;6~>12Ah<2r#*oO;8x9`Mm3GE}-V3ZSd3c!?Zb@OP;eK z-7y+1U_?sM>q#m4W)6L19QCP~Dl?S39hF2PyGxajO((e=`C2xy3Lxq$E>V}z{mI(n z=jx|vSPpndbPI*|yNYz*LgC}zLTN4)6a92ciCepV<;sTiuy$GBz9`mZxvJJH zj3$X9BPuPKk-2)6X*DWUSrzN5ENa5b8T3--(c*^D>l?bJ+r?&)?se4SdhzlD-W#J- z8&#>cDb|@`tJ;-FuHG_=%}0xHLy+A52T`8oMV5yw6072-wWukPw=8=z5Q1;}%L>jdi9fM2xHAps7ac58g> GvHt=s+Z1X5 literal 0 HcmV?d00001 diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-opensergo/pom.xml b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-opensergo/pom.xml new file mode 100644 index 000000000..34e31c8b7 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-opensergo/pom.xml @@ -0,0 +1,65 @@ + + + 4.0.0 + + com.alibaba.cloud + spring-cloud-alibaba-starters + ${revision} + ../pom.xml + + + spring-cloud-starter-alibaba-controlplane-opensergo + Spring Cloud Alibaba OpenSergo Control Plane + + + + org.springframework.boot + spring-boot-starter + true + + + org.springframework.boot + spring-boot-autoconfigure + true + + + com.alibaba.cloud + spring-cloud-alibaba-commons + + + com.alibaba.cloud + spring-cloud-starter-alibaba-governance-routing + test + + + io.opensergo + opensergo-java-sdk + ${opensergo.version} + + + + + org.springframework.boot + spring-boot-starter-test + test + true + + + com.alibaba + fastjson + 2.0.16 + test + + + org.springframework + spring-test + test + + + + + 1.8 + + + diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-opensergo/src/main/java/com/alibaba/cloud/governance/opensergo/OpenSergoAutoConfig.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-opensergo/src/main/java/com/alibaba/cloud/governance/opensergo/OpenSergoAutoConfig.java new file mode 100644 index 000000000..3877dfb4f --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-opensergo/src/main/java/com/alibaba/cloud/governance/opensergo/OpenSergoAutoConfig.java @@ -0,0 +1,64 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.governance.opensergo; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.AutoConfigureOrder; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author panxiaojun233 + * @author + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnProperty(name = "spring.cloud.opensergo.config.enabled", + matchIfMissing = true) +@EnableConfigurationProperties(OpenSergoConfigProperties.class) +@AutoConfigureOrder(OpenSergoAutoConfig.OPENSERGO_RESOURCE_AUTO_CONFIG_ORDER) +public class OpenSergoAutoConfig { + + /** + * Order of OpenSergo auto config. + */ + public static final int OPENSERGO_RESOURCE_AUTO_CONFIG_ORDER = 101; + + @Autowired + private OpenSergoConfigProperties openSergoConfigProperties; + + @Bean + public OpenSergoTrafficRouterParser openSergoTrafficRouterParser() { + return new OpenSergoTrafficRouterParser(); + } + + @Bean + public OpenSergoTrafficExchanger openSergoTrafficExchanger( + OpenSergoTrafficRouterParser openSergoTrafficRouterParser) { + return new OpenSergoTrafficExchanger(openSergoConfigProperties, + openSergoTrafficRouterParser); + } + + @Bean + public TargetServiceChangedListener targetServiceChangedListener( + OpenSergoTrafficExchanger openSergoTrafficExchanger) { + return new TargetServiceChangedListener(openSergoConfigProperties, + openSergoTrafficExchanger); + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-opensergo/src/main/java/com/alibaba/cloud/governance/opensergo/OpenSergoConfigProperties.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-opensergo/src/main/java/com/alibaba/cloud/governance/opensergo/OpenSergoConfigProperties.java new file mode 100644 index 000000000..52f2ef128 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-opensergo/src/main/java/com/alibaba/cloud/governance/opensergo/OpenSergoConfigProperties.java @@ -0,0 +1,59 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.governance.opensergo; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * @author panxiaojun233 + * @author + */ +@ConfigurationProperties(OpenSergoConfigProperties.PREFIX) +public class OpenSergoConfigProperties { + + /** + * Prefix in yaml. + */ + public static final String PREFIX = "spring.cloud.opensergo"; + + /** + * Configurations about OpenSergo Server Endpoint. + */ + private String endpoint; + + /** + * Namespace Configuration about OpenSergo Config. + */ + private String namespace = "default"; + + public String getNamespace() { + return namespace; + } + + void setNamespace(String namespace) { + this.namespace = namespace; + } + + public String getEndpoint() { + return endpoint; + } + + public void setEndpoint(String endpoint) { + this.endpoint = endpoint; + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-opensergo/src/main/java/com/alibaba/cloud/governance/opensergo/OpenSergoTrafficExchanger.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-opensergo/src/main/java/com/alibaba/cloud/governance/opensergo/OpenSergoTrafficExchanger.java new file mode 100644 index 000000000..5ef1e8e12 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-opensergo/src/main/java/com/alibaba/cloud/governance/opensergo/OpenSergoTrafficExchanger.java @@ -0,0 +1,109 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.governance.opensergo; + +import java.util.Collection; +import java.util.List; + +import com.alibaba.cloud.commons.governance.event.LabelRoutingDataChangedEvent; +import com.alibaba.cloud.commons.governance.labelrouting.UnifiedRouteDataStructure; +import com.alibaba.cloud.commons.lang.StringUtils; +import com.alibaba.cloud.governance.opensergo.util.ConvUtils; +import com.google.protobuf.InvalidProtocolBufferException; +import io.envoyproxy.envoy.config.route.v3.RouteConfiguration; +import io.opensergo.ConfigKind; +import io.opensergo.OpenSergoClient; +import io.opensergo.subscribe.OpenSergoConfigSubscriber; +import io.opensergo.subscribe.SubscribeKey; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; + +/** + * OpenSergoTrafficExchanger is the class which communicate with OpenSergo control plane. + * + * @author panxiaojun233 + * @author + */ +public class OpenSergoTrafficExchanger implements ApplicationContextAware { + + protected static final Logger log = LoggerFactory + .getLogger(OpenSergoTrafficExchanger.class); + + private OpenSergoClient client; + + private ApplicationContext applicationContext; + + private OpenSergoTrafficRouterParser openSergoTrafficRouterParser; + + public OpenSergoTrafficExchanger(OpenSergoConfigProperties openSergoConfigProperties, + OpenSergoTrafficRouterParser openSergoTrafficRouterParser) { + Integer port = ConvUtils + .getOpenSergoPort(openSergoConfigProperties.getEndpoint()); + String host = ConvUtils.getOpenSergoHost(openSergoConfigProperties.getEndpoint()); + + this.openSergoTrafficRouterParser = openSergoTrafficRouterParser; + try { + if (port != null && StringUtils.isNotEmpty(host)) { + client = new OpenSergoClient(host, port); + client.start(); + } + else { + log.error("OpenSergo endpoint:" + openSergoConfigProperties.getEndpoint() + + " is illegal"); + } + } + catch (Exception e) { + log.error("start OpenSergo client enhance error", e); + } + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) + throws BeansException { + this.applicationContext = applicationContext; + } + + public void subscribeTrafficRouterConfig(String namespace, String appName) { + client.subscribeConfig( + new SubscribeKey(namespace, appName, ConfigKind.TRAFFIC_ROUTER_STRATEGY), + new OpenSergoConfigSubscriber() { + @Override + public boolean onConfigUpdate(SubscribeKey subscribeKey, + Object dataList) { + log.debug("OpenSergo client subscribeKey:{} receive message :{}", + subscribeKey, dataList); + try { + Collection rules = openSergoTrafficRouterParser + .resolveLabelRouting( + (List) dataList); + applicationContext.publishEvent( + new LabelRoutingDataChangedEvent(this, rules)); + } + catch (InvalidProtocolBufferException e) { + log.error("resolve label routing enhance error", e); + return false; + } + return true; + } + }); + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-opensergo/src/main/java/com/alibaba/cloud/governance/opensergo/OpenSergoTrafficRouterParser.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-opensergo/src/main/java/com/alibaba/cloud/governance/opensergo/OpenSergoTrafficRouterParser.java new file mode 100644 index 000000000..92e255d17 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-opensergo/src/main/java/com/alibaba/cloud/governance/opensergo/OpenSergoTrafficRouterParser.java @@ -0,0 +1,253 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.governance.opensergo; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.alibaba.cloud.commons.governance.labelrouting.LabelRouteRule; +import com.alibaba.cloud.commons.governance.labelrouting.MatchService; +import com.alibaba.cloud.commons.governance.labelrouting.UnifiedRouteDataStructure; +import com.alibaba.cloud.commons.governance.labelrouting.rule.HeaderRule; +import com.alibaba.cloud.commons.governance.labelrouting.rule.RouteRule; +import com.alibaba.cloud.commons.governance.labelrouting.rule.UrlRule; +import com.alibaba.cloud.commons.lang.StringUtils; +import com.alibaba.cloud.commons.matcher.StringMatcher; +import com.alibaba.cloud.commons.matcher.StringMatcherType; +import com.alibaba.cloud.governance.opensergo.util.ConvUtils; +import com.google.protobuf.InvalidProtocolBufferException; +import io.envoyproxy.envoy.config.route.v3.ClusterSpecifierPlugin; +import io.envoyproxy.envoy.config.route.v3.HeaderMatcher; +import io.envoyproxy.envoy.config.route.v3.QueryParameterMatcher; +import io.envoyproxy.envoy.config.route.v3.Route; +import io.envoyproxy.envoy.config.route.v3.RouteConfiguration; +import io.envoyproxy.envoy.config.route.v3.RouteMatch; +import io.envoyproxy.envoy.config.route.v3.VirtualHost; +import io.envoyproxy.envoy.config.route.v3.WeightedCluster; +import io.opensergo.proto.router.v1.ClusterFallbackConfig_ClusterConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author panxiaojun233 + * @author + */ +public class OpenSergoTrafficRouterParser { + + protected static final Logger log = LoggerFactory + .getLogger(OpenSergoTrafficRouterParser.class); + + private static final String HEADER = "header"; + + private static final String PARAMETER = "parameter"; + + private static final String PATH = "path"; + + public OpenSergoTrafficRouterParser() { + } + + /** + * transform rds RouterConfig list to spring cloud alibaba router data list. + * @param routeConfigurations the routerConfig list from OpenSergo control plane. + * @return spring cloud alibaba router rules. + * @throws InvalidProtocolBufferException transform exception. + */ + public Collection resolveLabelRouting( + List routeConfigurations) + throws InvalidProtocolBufferException { + if (routeConfigurations == null) { + return new ArrayList<>(); + } + Map unifiedRouteDataStructures = new HashMap<>(); + for (RouteConfiguration routeConfiguration : routeConfigurations) { + List virtualHosts = routeConfiguration.getVirtualHostsList(); + for (VirtualHost virtualHost : virtualHosts) { + UnifiedRouteDataStructure unifiedRouteDataStructure = new UnifiedRouteDataStructure(); + String targetService = ""; + String[] serviceAndPort = virtualHost.getName().split(":"); + if (serviceAndPort.length > 0) { + targetService = serviceAndPort[0].split("\\.")[0]; + } + unifiedRouteDataStructure.setTargetService(targetService); + List routes = virtualHost.getRoutesList(); + LabelRouteRule labelRouteRule = getLabelRouteData(routes); + unifiedRouteDataStructure.setLabelRouteRule(labelRouteRule); + unifiedRouteDataStructures.put( + unifiedRouteDataStructure.getTargetService(), + unifiedRouteDataStructure); + } + } + return unifiedRouteDataStructures.values(); + } + + private LabelRouteRule getLabelRouteData(List routes) + throws InvalidProtocolBufferException { + List matchServices = new ArrayList<>(); + LabelRouteRule labelRouteRule = new LabelRouteRule(); + for (Route route : routes) { + ClusterSpecifierPlugin clusterSpecifierPlugin = route.getRoute() + .getInlineClusterSpecifierPlugin(); + String cluster = ""; + String fallbackCluster = ""; + if (clusterSpecifierPlugin != null) { + ClusterFallbackConfig_ClusterConfig fallbackConfig = ConvUtils + .convFallbackClusterConfig(clusterSpecifierPlugin); + fallbackCluster = fallbackConfig.getFallbackCluster(); + cluster = fallbackConfig.getRoutingCluster(); + } + if (StringUtils.isEmpty(cluster)) { + cluster = route.getRoute().getCluster(); + } + + if (StringUtils.isNotEmpty(cluster)) { + MatchService matchService = null; + if (StringUtils.isNotEmpty(fallbackCluster)) { + matchService = getMatchService(route, cluster, 100, + getVersion(route, fallbackCluster)); + } + else { + matchService = getMatchService(route, cluster, 100); + } + + matchServices.add(matchService); + } + + WeightedCluster weightedCluster = route.getRoute().getWeightedClusters(); + for (WeightedCluster.ClusterWeight clusterWeight : weightedCluster + .getClustersList()) { + MatchService matchService = getMatchService(route, + clusterWeight.getName(), clusterWeight.getWeight().getValue()); + matchServices.add(matchService); + } + } + labelRouteRule.setMatchRouteList(matchServices); + if (!matchServices.isEmpty()) { + labelRouteRule.setDefaultRouteVersion( + matchServices.get(matchServices.size() - 1).getVersion()); + } + return labelRouteRule; + } + + private MatchService getMatchService(Route route, String cluster, int weight) { + return getMatchService(route, cluster, weight, null); + } + + private MatchService getMatchService(Route route, String cluster, int weight, + String fallback) { + String version = getVersion(route, cluster); + MatchService matchService = new MatchService(); + matchService.setVersion(version); + matchService.setRuleList(match2RouteRules(route.getMatch())); + matchService.setWeight(weight); + if (StringUtils.isNotEmpty(fallback)) { + matchService.setFallback(fallback); + } + return matchService; + } + + private String getVersion(Route route, String cluster) { + String version = ""; + try { + String[] info = cluster.split("\\|"); + version = info[2]; + } + catch (Exception e) { + log.error("invalid cluster info for route {}", route.getName()); + } + return version; + } + + private List match2RouteRules(RouteMatch routeMatch) { + List routeRules = new ArrayList<>(); + for (HeaderMatcher headerMatcher : routeMatch.getHeadersList()) { + HeaderRule headerRule = headerMatcher2HeaderRule(headerMatcher); + if (headerRule != null) { + routeRules.add(headerRule); + } + } + + for (QueryParameterMatcher parameterMatcher : routeMatch + .getQueryParametersList()) { + UrlRule.Parameter parameter = parameterMatcher2ParameterRule( + parameterMatcher); + if (parameter != null) { + routeRules.add(parameter); + } + } + + UrlRule.Path path = new UrlRule.Path(); + path.setType(PATH); + switch (routeMatch.getPathSpecifierCase()) { + case PREFIX: + path.setCondition(StringMatcherType.PREFIX.toString()); + path.setValue(routeMatch.getPrefix()); + break; + + case PATH: + path.setCondition(StringMatcherType.EXACT.toString()); + path.setValue(routeMatch.getPath()); + break; + + case SAFE_REGEX: + path.setCondition(StringMatcherType.REGEX.toString()); + path.setValue(routeMatch.getSafeRegex().getRegex()); + break; + + default: + // unknown type + path = null; + + } + if (path != null) { + routeRules.add(path); + } + return routeRules; + } + + private UrlRule.Parameter parameterMatcher2ParameterRule( + QueryParameterMatcher queryParameterMatcher) { + UrlRule.Parameter parameter = new UrlRule.Parameter(); + StringMatcher stringMatcher = ConvUtils + .convStringMatcher(queryParameterMatcher.getStringMatch()); + if (stringMatcher != null) { + parameter.setCondition(stringMatcher.getType().toString()); + parameter.setKey(queryParameterMatcher.getName()); + parameter.setValue(stringMatcher.getMatcher()); + parameter.setType(PARAMETER); + return parameter; + } + return null; + } + + private HeaderRule headerMatcher2HeaderRule(HeaderMatcher headerMatcher) { + StringMatcher stringMatcher = ConvUtils + .convStringMatcher(ConvUtils.headerMatch2StringMatch(headerMatcher)); + if (stringMatcher != null) { + HeaderRule headerRule = new HeaderRule(); + headerRule.setCondition(stringMatcher.getType().toString()); + headerRule.setKey(headerMatcher.getName()); + headerRule.setValue(stringMatcher.getMatcher()); + headerRule.setType(HEADER); + return headerRule; + } + return null; + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-opensergo/src/main/java/com/alibaba/cloud/governance/opensergo/TargetServiceChangedListener.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-opensergo/src/main/java/com/alibaba/cloud/governance/opensergo/TargetServiceChangedListener.java new file mode 100644 index 000000000..176749f97 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-opensergo/src/main/java/com/alibaba/cloud/governance/opensergo/TargetServiceChangedListener.java @@ -0,0 +1,53 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.governance.opensergo; + +import com.alibaba.cloud.commons.governance.event.TargetServiceChangedEvent; + +import org.springframework.context.ApplicationListener; + +/** + * Subscribe OpenSergo configuration when provider service changed. + * + * @author panxiaojun233 + * @author + */ +public class TargetServiceChangedListener + implements ApplicationListener { + + private OpenSergoTrafficExchanger openSergoTrafficExchanger; + + private OpenSergoConfigProperties openSergoConfigProperties; + + public TargetServiceChangedListener( + OpenSergoConfigProperties openSergoConfigProperties, + OpenSergoTrafficExchanger openSergoTrafficExchanger) { + this.openSergoConfigProperties = openSergoConfigProperties; + this.openSergoTrafficExchanger = openSergoTrafficExchanger; + } + + @Override + public void onApplicationEvent(TargetServiceChangedEvent targetServiceChangedEvent) { + Object source = targetServiceChangedEvent.getSource(); + if (source instanceof String) { + String targetService = (String) targetServiceChangedEvent.getSource(); + openSergoTrafficExchanger.subscribeTrafficRouterConfig( + openSergoConfigProperties.getNamespace(), targetService); + } + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-opensergo/src/main/java/com/alibaba/cloud/governance/opensergo/util/ConvUtils.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-opensergo/src/main/java/com/alibaba/cloud/governance/opensergo/util/ConvUtils.java new file mode 100644 index 000000000..0a446cacf --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-opensergo/src/main/java/com/alibaba/cloud/governance/opensergo/util/ConvUtils.java @@ -0,0 +1,132 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.governance.opensergo.util; + +import com.alibaba.cloud.commons.lang.StringUtils; +import com.alibaba.cloud.commons.matcher.StringMatcher; +import com.alibaba.cloud.commons.matcher.StringMatcherType; +import com.google.protobuf.Internal; +import com.google.protobuf.InvalidProtocolBufferException; +import com.google.protobuf.Message; +import io.envoyproxy.envoy.config.route.v3.ClusterSpecifierPlugin; +import io.envoyproxy.envoy.type.matcher.v3.RegexMatcher; +import io.opensergo.proto.router.v1.ClusterFallbackConfig_ClusterConfig; + +/** + * @author panxiaojun233 + * @author + */ +public final class ConvUtils { + + private ConvUtils() { + + } + + public static String getOpenSergoHost(String endpoint) { + if (StringUtils.isNotEmpty(endpoint)) { + return endpoint.split(":")[0]; + } + return null; + } + + public static Integer getOpenSergoPort(String endpoint) { + if (StringUtils.isNotEmpty(endpoint)) { + String portStr = endpoint.split(":")[1]; + return Integer.valueOf(portStr); + } + return null; + } + + public static ClusterFallbackConfig_ClusterConfig convFallbackClusterConfig( + ClusterSpecifierPlugin clusterSpecifierPlugin) + throws InvalidProtocolBufferException { + Message defaultInstance = Internal + .getDefaultInstance(ClusterFallbackConfig_ClusterConfig.class); + return (ClusterFallbackConfig_ClusterConfig) defaultInstance.getParserForType() + .parseFrom(clusterSpecifierPlugin.getExtension().getTypedConfig() + .getValue()); + } + + public static StringMatcher convStringMatcher( + io.envoyproxy.envoy.type.matcher.v3.StringMatcher stringMatcher) { + if (stringMatcher == null) { + return null; + } + boolean isIgnoreCase = stringMatcher.getIgnoreCase(); + String exact = stringMatcher.getExact(); + String prefix = stringMatcher.getPrefix(); + String suffix = stringMatcher.getSuffix(); + String contains = stringMatcher.getContains(); + String regex = stringMatcher.getSafeRegex().getRegex(); + if (StringUtils.isNotBlank(exact)) { + return new StringMatcher(exact, StringMatcherType.EXACT, isIgnoreCase); + } + if (StringUtils.isNotBlank(prefix)) { + return new StringMatcher(prefix, StringMatcherType.PREFIX, isIgnoreCase); + } + if (StringUtils.isNotBlank(suffix)) { + return new StringMatcher(suffix, StringMatcherType.SUFFIX, isIgnoreCase); + } + if (StringUtils.isNotBlank(contains)) { + return new StringMatcher(contains, StringMatcherType.CONTAIN, isIgnoreCase); + } + if (StringUtils.isNotBlank(regex)) { + return new StringMatcher(regex); + } + return null; + } + + public static io.envoyproxy.envoy.type.matcher.v3.StringMatcher headerMatch2StringMatch( + io.envoyproxy.envoy.config.route.v3.HeaderMatcher headerMatcher) { + if (headerMatcher == null) { + return null; + } + if (headerMatcher.getPresentMatch()) { + io.envoyproxy.envoy.type.matcher.v3.StringMatcher.Builder builder = io.envoyproxy.envoy.type.matcher.v3.StringMatcher + .newBuilder(); + return builder.setSafeRegex(RegexMatcher.newBuilder().build()) + .setIgnoreCase(true).build(); + } + if (!headerMatcher.hasStringMatch()) { + io.envoyproxy.envoy.type.matcher.v3.StringMatcher.Builder builder = io.envoyproxy.envoy.type.matcher.v3.StringMatcher + .newBuilder(); + String exactMatch = headerMatcher.getExactMatch(); + String containsMatch = headerMatcher.getContainsMatch(); + String prefixMatch = headerMatcher.getPrefixMatch(); + String suffixMatch = headerMatcher.getSuffixMatch(); + RegexMatcher safeRegex = headerMatcher.getSafeRegexMatch(); + if (!StringUtils.isEmpty(exactMatch)) { + builder.setExact(exactMatch); + } + else if (!StringUtils.isEmpty(containsMatch)) { + builder.setContains(containsMatch); + } + else if (!StringUtils.isEmpty(prefixMatch)) { + builder.setPrefix(prefixMatch); + } + else if (!StringUtils.isEmpty(suffixMatch)) { + builder.setSuffix(suffixMatch); + } + else if (safeRegex.isInitialized()) { + builder.setSafeRegex(safeRegex); + } + return builder.setIgnoreCase(true).build(); + } + return headerMatcher.getStringMatch(); + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-opensergo/src/main/resources/META-INF/spring.factories b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-opensergo/src/main/resources/META-INF/spring.factories new file mode 100644 index 000000000..45c2f32d3 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-opensergo/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +com.alibaba.cloud.governance.opensergo.OpenSergoAutoConfig diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-opensergo/src/test/java/com/alibaba/cloud/governance/opensergo/OpenSergoRuleTests.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-opensergo/src/test/java/com/alibaba/cloud/governance/opensergo/OpenSergoRuleTests.java new file mode 100644 index 000000000..5809d1e38 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-controlplane-opensergo/src/test/java/com/alibaba/cloud/governance/opensergo/OpenSergoRuleTests.java @@ -0,0 +1,72 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.governance.opensergo; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import com.alibaba.cloud.commons.governance.labelrouting.UnifiedRouteDataStructure; +import com.alibaba.fastjson.JSONObject; +import io.envoyproxy.envoy.config.route.v3.HeaderMatcher; +import io.envoyproxy.envoy.config.route.v3.Route; +import io.envoyproxy.envoy.config.route.v3.RouteAction; +import io.envoyproxy.envoy.config.route.v3.RouteConfiguration; +import io.envoyproxy.envoy.config.route.v3.RouteMatch; +import io.envoyproxy.envoy.config.route.v3.VirtualHost; +import io.envoyproxy.envoy.type.matcher.v3.StringMatcher; +import org.junit.Assert; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author panxiaojun233 + * @author + */ +public class OpenSergoRuleTests { + + private static final Logger log = LoggerFactory.getLogger(OpenSergoRuleTests.class); + + private OpenSergoTrafficRouterParser openSergoTrafficRouterParser = new OpenSergoTrafficRouterParser(); + + @Test + public void testOpenSergoTrafficRouterTransform() throws Exception { + HeaderMatcher headerMatcher = HeaderMatcher.newBuilder().setName("x-tag") + .setStringMatch(StringMatcher.newBuilder().setExact("v2").buildPartial()) + .build(); + RouteMatch routeMatch = RouteMatch.newBuilder().addHeaders(headerMatcher).build(); + Route route = Route.newBuilder().setMatch(routeMatch) + .setRoute(RouteAction.newBuilder() + .setCluster( + "outbound||v2|service-provider.default.svc.cluster.local") + .build()) + .build(); + VirtualHost virtualHost = VirtualHost.newBuilder().setName("service-provider") + .addDomains("service-provider.default.svc.cluster.local").addRoutes(route) + .build(); + RouteConfiguration routeConfiguration = RouteConfiguration.newBuilder() + .setName("service-provider").addVirtualHosts(virtualHost).build(); + List routeConfigurations = new ArrayList<>(); + routeConfigurations.add(routeConfiguration); + Collection rules = openSergoTrafficRouterParser + .resolveLabelRouting(routeConfigurations); + log.info("TrafficRouter rules are {}", JSONObject.toJSONString(rules)); + Assert.assertEquals(rules.size(), 1); + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/pom.xml b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/pom.xml new file mode 100644 index 000000000..4215a5ae0 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/pom.xml @@ -0,0 +1,64 @@ + + + 4.0.0 + + com.alibaba.cloud + spring-cloud-alibaba-starters + ${revision} + ../pom.xml + + spring-cloud-starter-alibaba-governance-auth + Spring Cloud Starter Alibaba Governance Authentication + + + + org.springframework.boot + spring-boot-autoconfigure + true + + + + javax.servlet + javax.servlet-api + provided + + + + org.bitbucket.b_c + jose4j + ${jose4j.version} + + + + io.projectreactor + reactor-core + + + + org.springframework.boot + spring-boot-starter-web + true + + + + com.alibaba.cloud + spring-cloud-alibaba-commons + + + + org.springframework.boot + spring-boot-starter-test + test + true + + + + com.alibaba.fastjson2 + fastjson2 + 2.0.16 + test + + + + diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/java/com/alibaba/cloud/governance/auth/AuthValidatorAutoConfiguration.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/java/com/alibaba/cloud/governance/auth/AuthValidatorAutoConfiguration.java new file mode 100644 index 000000000..a3ff69d12 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/java/com/alibaba/cloud/governance/auth/AuthValidatorAutoConfiguration.java @@ -0,0 +1,39 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.governance.auth; + +import com.alibaba.cloud.governance.auth.repository.AuthRepository; +import com.alibaba.cloud.governance.auth.validator.AuthValidator; + +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author musi + * @author + */ +@Configuration(proxyBeanMethods = false) +@AutoConfigureAfter(AuthenticationAutoConfiguration.class) +public class AuthValidatorAutoConfiguration { + + @Bean + public AuthValidator authValidator(AuthRepository authRepository) { + return new AuthValidator(authRepository); + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/java/com/alibaba/cloud/governance/auth/AuthenticationAutoConfiguration.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/java/com/alibaba/cloud/governance/auth/AuthenticationAutoConfiguration.java new file mode 100644 index 000000000..261e380f7 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/java/com/alibaba/cloud/governance/auth/AuthenticationAutoConfiguration.java @@ -0,0 +1,54 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.governance.auth; + +import com.alibaba.cloud.governance.auth.listener.AuthListener; +import com.alibaba.cloud.governance.auth.repository.AuthRepository; + +import org.springframework.boot.autoconfigure.AutoConfigureOrder; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author musi + * @author + */ +@Configuration(proxyBeanMethods = false) +// We need to auto config the class before spring cloud alibaba istio module, to prevent +// event publisher hang permanently. +@AutoConfigureOrder(AuthenticationAutoConfiguration.AUTH_AUTO_CONFIG_ORDER) + +public class AuthenticationAutoConfiguration { + + /** + * Order of auth auto config. + */ + public static final int AUTH_AUTO_CONFIG_ORDER = 9; + + @Bean + @ConditionalOnMissingBean + public AuthRepository authRepository() { + return new AuthRepository(); + } + + @Bean + public AuthListener authListener(AuthRepository authRepository) { + return new AuthListener(authRepository); + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/java/com/alibaba/cloud/governance/auth/XdsWebAutoConfiguration.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/java/com/alibaba/cloud/governance/auth/XdsWebAutoConfiguration.java new file mode 100644 index 000000000..e445c29d3 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/java/com/alibaba/cloud/governance/auth/XdsWebAutoConfiguration.java @@ -0,0 +1,53 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.governance.auth; + +import com.alibaba.cloud.governance.auth.validator.AuthValidator; +import com.alibaba.cloud.governance.auth.webmvc.AuthWebInterceptor; + +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author musi + * @author + */ +@Configuration(proxyBeanMethods = false) +@AutoConfigureAfter(AuthenticationAutoConfiguration.class) +@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) +@ConditionalOnProperty(name = "spring.cloud.governance.auth.enabled", + matchIfMissing = true) +public class XdsWebAutoConfiguration { + + @Bean + @ConditionalOnProperty(name = "spring.cloud.governance.auth.enabled", + matchIfMissing = true) + public AuthWebInterceptor authWebInterceptor(AuthValidator authValidator) { + return new AuthWebInterceptor(authValidator); + } + + @Bean + @ConditionalOnProperty(name = "spring.cloud.governance.auth.enabled", + matchIfMissing = true) + public XdsWebMvcConfigurer xdsWebMvcConfigurer() { + return new XdsWebMvcConfigurer(); + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/java/com/alibaba/cloud/governance/auth/XdsWebFluxAutoConfiguration.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/java/com/alibaba/cloud/governance/auth/XdsWebFluxAutoConfiguration.java new file mode 100644 index 000000000..9bbe3016d --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/java/com/alibaba/cloud/governance/auth/XdsWebFluxAutoConfiguration.java @@ -0,0 +1,46 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.governance.auth; + +import com.alibaba.cloud.governance.auth.validator.AuthValidator; +import com.alibaba.cloud.governance.auth.webflux.AuthWebFluxFilter; + +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author musi + * @author + */ +@Configuration(proxyBeanMethods = false) +@AutoConfigureAfter(AuthValidatorAutoConfiguration.class) +@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE) +@ConditionalOnProperty(name = "spring.cloud.governance.auth.enabled", + matchIfMissing = true) +public class XdsWebFluxAutoConfiguration { + + @Bean + @ConditionalOnProperty(name = "spring.cloud.governance.auth.enabled", + matchIfMissing = true) + public AuthWebFluxFilter authWebFluxFilter(AuthValidator authValidator) { + return new AuthWebFluxFilter(authValidator); + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/java/com/alibaba/cloud/governance/auth/XdsWebMvcConfigurer.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/java/com/alibaba/cloud/governance/auth/XdsWebMvcConfigurer.java new file mode 100644 index 000000000..173cbbbe5 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/java/com/alibaba/cloud/governance/auth/XdsWebMvcConfigurer.java @@ -0,0 +1,44 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.governance.auth; + +import java.util.Optional; + +import com.alibaba.cloud.governance.auth.webmvc.AuthWebInterceptor; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +/** + * @author musi + * @author + */ +public class XdsWebMvcConfigurer implements WebMvcConfigurer { + + @Autowired + private Optional authWebInterceptor; + + @Override + public void addInterceptors(InterceptorRegistry registry) { + if (!authWebInterceptor.isPresent()) { + return; + } + registry.addInterceptor(authWebInterceptor.get()); + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/java/com/alibaba/cloud/governance/auth/listener/AuthListener.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/java/com/alibaba/cloud/governance/auth/listener/AuthListener.java new file mode 100644 index 000000000..96a5f26db --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/java/com/alibaba/cloud/governance/auth/listener/AuthListener.java @@ -0,0 +1,52 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.governance.auth.listener; + +import com.alibaba.cloud.commons.governance.auth.rule.AuthRules; +import com.alibaba.cloud.commons.governance.event.AuthDataChangedEvent; +import com.alibaba.cloud.governance.auth.repository.AuthRepository; + +import org.springframework.context.ApplicationListener; + +/** + * @author musi + * @author To receive the auth data when it is + * changed by user. + */ +public class AuthListener implements ApplicationListener { + + private final AuthRepository authRepository; + + public AuthListener(AuthRepository authRepository) { + this.authRepository = authRepository; + } + + @Override + public void onApplicationEvent(AuthDataChangedEvent event) { + AuthRules authRules = event.getAuthRules(); + if (authRules.getAllowAuthRules() != null) { + authRepository.setAllowAuthRule(authRules.getAllowAuthRules()); + } + if (authRules.getDenyAuthRules() != null) { + authRepository.setDenyAuthRules(authRules.getDenyAuthRules()); + } + if (authRules.getJwtRules() != null) { + authRepository.setJwtRule(authRules.getJwtRules()); + } + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/java/com/alibaba/cloud/governance/auth/repository/AuthRepository.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/java/com/alibaba/cloud/governance/auth/repository/AuthRepository.java new file mode 100644 index 000000000..ff6768ab0 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/java/com/alibaba/cloud/governance/auth/repository/AuthRepository.java @@ -0,0 +1,73 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.governance.auth.repository; + +import java.util.HashMap; +import java.util.Map; + +import com.alibaba.cloud.commons.governance.auth.rule.AuthRule; +import com.alibaba.cloud.commons.governance.auth.rule.JwtRule; + +/** + * @author musi + * @author To store auth rules in Spring Cloud + * Alibaba. + */ +public class AuthRepository { + + private Map allowAuthRules = new HashMap<>(); + + private Map denyAuthRules = new HashMap<>(); + + private Map jwtRules = new HashMap<>(); + + public AuthRepository() { + + } + + public AuthRepository(Map allowAuthRules, + Map denyAuthRules, Map jwtRules) { + this.allowAuthRules = allowAuthRules; + this.denyAuthRules = denyAuthRules; + this.jwtRules = jwtRules; + } + + public Map getAllowAuthRules() { + return allowAuthRules; + } + + public Map getDenyAuthRules() { + return denyAuthRules; + } + + public Map getJwtRules() { + return jwtRules; + } + + public void setAllowAuthRule(Map allowAuthRules) { + this.allowAuthRules = allowAuthRules; + } + + public void setDenyAuthRules(Map denyAuthRules) { + this.denyAuthRules = denyAuthRules; + } + + public void setJwtRule(Map jwtRules) { + this.jwtRules = jwtRules; + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/java/com/alibaba/cloud/governance/auth/util/IpUtil.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/java/com/alibaba/cloud/governance/auth/util/IpUtil.java new file mode 100644 index 000000000..775bfb473 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/java/com/alibaba/cloud/governance/auth/util/IpUtil.java @@ -0,0 +1,73 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.governance.auth.util; + +import javax.servlet.ServletRequest; +import javax.servlet.http.HttpServletRequest; + +import com.alibaba.cloud.commons.lang.StringUtils; + +import org.springframework.http.server.reactive.ServerHttpRequest; + +/** + * @author musi + * @author + */ +public final class IpUtil { + + private static final String UNKNOWN = "unknown"; + + private IpUtil() { + + } + + public static String getRemoteIpAddress(ServerHttpRequest request) { + String ip = request.getHeaders().getFirst("X-Forwarded-For"); + if (StringUtils.isNotEmpty(ip) && !UNKNOWN.equalsIgnoreCase(ip)) { + if (ip.contains(",")) { + ip = ip.split(",")[0]; + } + } + if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) { + if (request.getRemoteAddress() != null + && request.getRemoteAddress().getAddress() != null) { + ip = request.getRemoteAddress().getAddress().getHostAddress(); + } + } + return StringUtils.isEmpty(ip) ? null : ip; + } + + public static String getRemoteIpAddress(ServletRequest request) { + if (!(request instanceof HttpServletRequest)) { + return null; + } + HttpServletRequest httpServletRequest = (HttpServletRequest) request; + String ip = httpServletRequest.getHeader("X-Forwarded-For"); + if (StringUtils.isNotEmpty(ip) && !UNKNOWN.equalsIgnoreCase(ip)) { + if (ip.contains(",")) { + ip = ip.split(",")[0]; + } + } + if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) { + if (httpServletRequest.getRemoteAddr() != null) { + ip = httpServletRequest.getRemoteAddr(); + } + } + return StringUtils.isEmpty(ip) ? null : ip; + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/java/com/alibaba/cloud/governance/auth/util/JwtUtil.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/java/com/alibaba/cloud/governance/auth/util/JwtUtil.java new file mode 100644 index 000000000..4f1bafa48 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/java/com/alibaba/cloud/governance/auth/util/JwtUtil.java @@ -0,0 +1,147 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.governance.auth.util; + +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.alibaba.cloud.commons.governance.auth.rule.JwtRule; +import com.alibaba.cloud.commons.lang.StringUtils; +import org.jose4j.jwk.JsonWebKeySet; +import org.jose4j.jws.JsonWebSignature; +import org.jose4j.jwt.JwtClaims; +import org.jose4j.jwt.MalformedClaimException; +import org.jose4j.jwt.consumer.InvalidJwtException; +import org.jose4j.jwt.consumer.JwtConsumer; +import org.jose4j.jwt.consumer.JwtConsumerBuilder; +import org.jose4j.jwt.consumer.JwtContext; +import org.jose4j.keys.resolvers.JwksVerificationKeyResolver; +import org.jose4j.lang.JoseException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.http.HttpHeaders; +import org.springframework.util.MultiValueMap; + +/** + * @author musi + * @author + */ +public final class JwtUtil { + + private static final Logger log = LoggerFactory.getLogger(JwtUtil.class); + + private static final String BEARER_PREFIX = "Bearer "; + + private JwtUtil() { + } + + public static String getTokenFromJwtRule(MultiValueMap params, + HttpHeaders headers, JwtRule jwtRule) { + if (headers == null) { + return ""; + } + try { + Map jwtHeaders = jwtRule.getFromHeaders(); + if (jwtHeaders != null && !jwtHeaders.isEmpty()) { + for (Map.Entry entry : jwtHeaders.entrySet()) { + String headerName = entry.getKey(); + String prefix = entry.getValue(); + if (headers.containsKey(headerName)) { + String token = headers.getFirst(headerName); + if (!StringUtils.isEmpty(token) && token.startsWith(prefix)) { + return token.substring(prefix.length()); + } + } + } + } + List fromParams = jwtRule.getFromParams(); + if (fromParams != null && !fromParams.isEmpty()) { + for (String fromParam : fromParams) { + if (params.containsKey(fromParam)) { + return params.getFirst(fromParam); + } + } + } + String token = headers.getFirst(HttpHeaders.AUTHORIZATION); + if (StringUtils.isEmpty(token)) { + return ""; + } + if (token.startsWith(BEARER_PREFIX)) { + return token.substring(BEARER_PREFIX.length()); + } + return token; + } + catch (Exception e) { + log.info("no jwt token extracted from header or params"); + } + return ""; + } + + public static JwtClaims extractJwtClaims(JwtRule jwtRule, String token) { + String jwks = jwtRule.getJwks(); + if (jwks == null || jwks.isEmpty()) { + return null; + } + JsonWebSignature jws = null; + try { + // don't validate jwt's attribute, just validate the sign + JwtConsumerBuilder jwtConsumerBuilder = new JwtConsumerBuilder() + .setSkipAllValidators(); + jws = new JsonWebSignature(); + jws.setCompactSerialization(token); + JsonWebKeySet jsonWebKeySet = new JsonWebKeySet(jwks); + JwksVerificationKeyResolver jwksResolver = new JwksVerificationKeyResolver( + jsonWebKeySet.getJsonWebKeys()); + jwtConsumerBuilder.setVerificationKeyResolver(jwksResolver); + JwtConsumer jwtConsumer = jwtConsumerBuilder.build(); + JwtContext jwtContext = jwtConsumer.process(token); + + String issuer = jwtContext.getJwtClaims().getIssuer(); + List audiences = jwtContext.getJwtClaims().getAudience(); + if (!StringUtils.isEmpty(jwtRule.getIssuer()) + && !jwtRule.getIssuer().equals(issuer)) { + return null; + } + + if (jwtRule.getAudiences() == null || jwtRule.getAudiences().isEmpty()) { + return jwtContext.getJwtClaims(); + } + + Set acceptAud = new HashSet<>(jwtRule.getAudiences()); + for (String aud : audiences) { + if (acceptAud.contains(aud)) { + return jwtContext.getJwtClaims(); + } + } + return null; + } + catch (JoseException e) { + log.warn("invalid jws from rule {}", jwtRule); + } + catch (InvalidJwtException e) { + log.warn("invalid jwt token {} for rule {}", token, jwtRule); + } + catch (MalformedClaimException e) { + log.warn("invalid jwt claims for rule {}", jwtRule); + } + return null; + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/java/com/alibaba/cloud/governance/auth/validator/AuthValidator.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/java/com/alibaba/cloud/governance/auth/validator/AuthValidator.java new file mode 100644 index 000000000..5383dc057 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/java/com/alibaba/cloud/governance/auth/validator/AuthValidator.java @@ -0,0 +1,366 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.governance.auth.validator; + +import java.util.List; +import java.util.Map; + +import com.alibaba.cloud.commons.governance.auth.condition.AuthCondition; +import com.alibaba.cloud.commons.governance.auth.rule.AuthRule; +import com.alibaba.cloud.commons.governance.auth.rule.JwtRule; +import com.alibaba.cloud.commons.lang.StringUtils; +import com.alibaba.cloud.commons.matcher.Matcher; +import com.alibaba.cloud.governance.auth.repository.AuthRepository; +import com.alibaba.cloud.governance.auth.util.JwtUtil; +import org.jose4j.jwt.JwtClaims; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.http.HttpHeaders; +import org.springframework.util.MultiValueMap; + +/** + * @author musi + * @author Use a abstract rule tree to validate the + * request. First, if the rules are all empty, we just return true. Secondly, if any deny + * rule matches the request, we just return false. Thirdly, if the allow rules are empty, + * we just return true. Last, if any allow rule matches the request, we just return true, + * or we return false. + */ +public class AuthValidator { + + private static final Logger log = LoggerFactory.getLogger(AuthValidator.class); + + private final AuthRepository authRepository; + + public AuthValidator(AuthRepository authRepository) { + this.authRepository = authRepository; + } + + public boolean validate(UnifiedHttpRequest request) { + if (request == null) { + return false; + } + + for (JwtRule jwtRule : authRepository.getJwtRules().values()) { + // extract jwt token from request first + String token = JwtUtil.getTokenFromJwtRule(request.getParams(), + request.getHeaders(), jwtRule); + if (!StringUtils.isEmpty(token)) { + JwtClaims jwtClaims = JwtUtil.extractJwtClaims(jwtRule, token); + if (jwtClaims == null) { + return false; + } + request.jwtClaims = jwtClaims; + break; + } + } + + Map denyRules = authRepository.getDenyAuthRules(); + for (AuthRule denyRule : denyRules.values()) { + if (validateRule(denyRule, request)) { + return false; + } + } + + Map allowRules = authRepository.getAllowAuthRules(); + + if (allowRules.isEmpty()) { + return true; + } + + for (AuthRule allowRule : allowRules.values()) { + if (validateRule(allowRule, request)) { + return true; + } + } + return false; + } + + private boolean validateRule(AuthRule rule, UnifiedHttpRequest request) { + if (rule.isLeaf()) { + return validateLeafRule(rule, request); + } + + List authRules = rule.getChildren(); + boolean flag = rule.getOp() != AuthRule.RuleOperation.OR; + for (AuthRule authRule : authRules) { + if (rule.getOp() == AuthRule.RuleOperation.OR && flag) { + break; + } + if (rule.getOp() == AuthRule.RuleOperation.AND && !flag) { + break; + } + boolean childFlag = validateRule(authRule, request); + switch (rule.getOp()) { + case OR: + flag |= childFlag; + break; + case AND: + flag &= childFlag; + break; + } + } + // if "is not" is true, reverse the flag + return rule.isNot() != flag; + } + + private boolean validateLeafRule(AuthRule rule, UnifiedHttpRequest request) { + try { + AuthCondition condition = rule.getCondition(); + Matcher matcher = condition.getMatcher(); + String key = condition.getKey(); + if (matcher == null) { + return false; + } + switch (condition.getType()) { + case SOURCE_IP: + return matcher.match(request.getSourceIp()); + case REMOTE_IP: + return matcher.match(request.getRemoteIp()); + case DEST_IP: + return matcher.match(request.getDestIp()); + // string + case HOSTS: + return matcher.match(request.getHost()); + case METHODS: + return matcher.match(request.getMethod()); + case PATHS: + return matcher.match(request.getPath()); + case REQUEST_PRINCIPALS: + case AUTH_AUDIENCES: + case AUTH_PRESENTERS: + // if there is an AuthorizationPolicy but there is not a + // RequestAuthentication, return false + if (request.getJwtClaims() == null) { + return false; + } + JwtClaims claims = request.getJwtClaims(); + switch (condition.getType()) { + case REQUEST_PRINCIPALS: + String issuer = claims.getIssuer(); + String subject = claims.getSubject(); + return matcher.match(issuer + "/" + subject); + case AUTH_AUDIENCES: + List audiences = claims.getAudience(); + for (String audience : audiences) { + if (matcher.match(audience)) { + return true; + } + } + return false; + case AUTH_PRESENTERS: + return matcher.match(claims.getClaimValueAsString("azp")); + } + return false; + // int + case PORTS: + return matcher.match(request.getPort()); + // header + case HEADER: + HttpHeaders headers = request.getHeaders(); + if (!headers.containsKey(key)) { + return false; + } + List headerList = headers.get(key); + if (headerList == null) { + return false; + } + for (String header : headerList) { + if (matcher.match(header)) { + return true; + } + } + return false; + case AUTH_CLAIMS: + claims = request.getJwtClaims(); + if (claims == null) { + return false; + } + Object claimValue = claims.getClaimValue(key); + + if (claimValue instanceof List) { + for (String claim : claims.getStringListClaimValue(key)) { + if (matcher.match(claim)) { + return true; + } + } + } + else { + return matcher.match(claims.getStringClaimValue(key)); + } + return false; + } + } + catch (Exception e) { + log.error("request {} doesn't match rule {}", request, rule); + } + return false; + } + + public final static class UnifiedHttpRequest { + + private final String sourceIp; + + private final String destIp; + + private final String remoteIp; + + private final String host; + + private final int port; + + private final String method; + + private final String path; + + private final HttpHeaders headers; + + private final MultiValueMap params; + + private JwtClaims jwtClaims; + + private UnifiedHttpRequest(String sourceIp, String destIp, String remoteIp, + String host, int port, String method, String path, HttpHeaders headers, + MultiValueMap params) { + this.sourceIp = sourceIp; + this.destIp = destIp; + this.remoteIp = remoteIp; + this.host = host; + this.port = port; + this.method = method; + this.path = path; + this.headers = headers; + this.params = params; + } + + public String getSourceIp() { + return sourceIp; + } + + public String getDestIp() { + return destIp; + } + + public String getRemoteIp() { + return remoteIp; + } + + public String getHost() { + return host; + } + + public int getPort() { + return port; + } + + public String getMethod() { + return method; + } + + public String getPath() { + return path; + } + + public HttpHeaders getHeaders() { + return headers; + } + + public MultiValueMap getParams() { + return params; + } + + public JwtClaims getJwtClaims() { + return jwtClaims; + } + + public static class UnifiedHttpRequestBuilder { + + private String sourceIp; + + private String destIp; + + private String remoteIp; + + private String host; + + private int port; + + private String method; + + private String path; + + private HttpHeaders headers; + + private MultiValueMap params; + + public UnifiedHttpRequestBuilder setSourceIp(String sourceIp) { + this.sourceIp = sourceIp; + return this; + } + + public UnifiedHttpRequestBuilder setDestIp(String destIp) { + this.destIp = destIp; + return this; + } + + public UnifiedHttpRequestBuilder setRemoteIp(String remoteIp) { + this.remoteIp = remoteIp; + return this; + } + + public UnifiedHttpRequestBuilder setHost(String host) { + this.host = host; + return this; + } + + public UnifiedHttpRequestBuilder setPort(int port) { + this.port = port; + return this; + } + + public UnifiedHttpRequestBuilder setMethod(String method) { + this.method = method; + return this; + } + + public UnifiedHttpRequestBuilder setPath(String path) { + this.path = path; + return this; + } + + public UnifiedHttpRequestBuilder setHeaders(HttpHeaders headers) { + this.headers = headers; + return this; + } + + public UnifiedHttpRequestBuilder setParams( + MultiValueMap params) { + this.params = params; + return this; + } + + public UnifiedHttpRequest build() { + return new UnifiedHttpRequest(sourceIp, destIp, remoteIp, host, port, + method, path, headers, params); + } + + } + + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/java/com/alibaba/cloud/governance/auth/webflux/AuthWebFluxFilter.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/java/com/alibaba/cloud/governance/auth/webflux/AuthWebFluxFilter.java new file mode 100644 index 000000000..b677ad520 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/java/com/alibaba/cloud/governance/auth/webflux/AuthWebFluxFilter.java @@ -0,0 +1,90 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.governance.auth.webflux; + +import java.nio.charset.StandardCharsets; + +import com.alibaba.cloud.governance.auth.util.IpUtil; +import com.alibaba.cloud.governance.auth.validator.AuthValidator; +import reactor.core.publisher.Mono; + +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.util.MultiValueMap; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.WebFilter; +import org.springframework.web.server.WebFilterChain; + +/** + * @author musi + * @author + */ +public class AuthWebFluxFilter implements WebFilter { + + private final AuthValidator authValidator; + + public AuthWebFluxFilter(AuthValidator authValidator) { + this.authValidator = authValidator; + } + + @Override + public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { + ServerHttpRequest request = exchange.getRequest(); + String destIp = null; + String remoteIp = null; + String sourceIp = null; + if (request.getRemoteAddress() != null) { + sourceIp = request.getRemoteAddress().getAddress().getHostAddress(); + } + if (request.getLocalAddress() != null) { + destIp = request.getLocalAddress().getAddress().getHostAddress(); + } + remoteIp = IpUtil.getRemoteIpAddress(request); + String host = request.getHeaders().getFirst(HttpHeaders.HOST); + int port = request.getLocalAddress().getPort(); + String method = request.getMethodValue(); + String path = request.getPath().value(); + HttpHeaders headers = request.getHeaders(); + MultiValueMap params = request.getQueryParams(); + AuthValidator.UnifiedHttpRequest.UnifiedHttpRequestBuilder builder = new AuthValidator.UnifiedHttpRequest.UnifiedHttpRequestBuilder(); + AuthValidator.UnifiedHttpRequest unifiedHttpRequest = builder.setDestIp(destIp) + .setRemoteIp(remoteIp).setSourceIp(sourceIp).setHost(host).setPort(port) + .setMethod(method).setPath(path).setHeaders(headers).setParams(params) + .build(); + + if (!authValidator.validate(unifiedHttpRequest)) { + return ret401(exchange); + } + return chain.filter(exchange); + } + + private Mono ret401(ServerWebExchange exchange) { + return ret401(exchange, "Auth failed, please check the request and auth rule"); + } + + private Mono ret401(ServerWebExchange exchange, String errorMsg) { + ServerHttpResponse response = exchange.getResponse(); + response.setStatusCode(HttpStatus.UNAUTHORIZED); + byte[] data = errorMsg.getBytes(StandardCharsets.UTF_8); + DataBuffer buffer = response.bufferFactory().wrap(data); + return response.writeWith(Mono.just(buffer)); + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/java/com/alibaba/cloud/governance/auth/webmvc/AuthWebInterceptor.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/java/com/alibaba/cloud/governance/auth/webmvc/AuthWebInterceptor.java new file mode 100644 index 000000000..21990df7b --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/java/com/alibaba/cloud/governance/auth/webmvc/AuthWebInterceptor.java @@ -0,0 +1,114 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.governance.auth.webmvc; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Enumeration; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.alibaba.cloud.governance.auth.util.IpUtil; +import com.alibaba.cloud.governance.auth.validator.AuthValidator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.http.HttpHeaders; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.servlet.HandlerInterceptor; + +/** + * @author musi + * @author + */ +public class AuthWebInterceptor implements HandlerInterceptor { + + private static final Logger log = LoggerFactory.getLogger(AuthWebInterceptor.class); + + private final AuthValidator authValidator; + + public AuthWebInterceptor(AuthValidator authValidator) { + this.authValidator = authValidator; + } + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, + Object handler) throws Exception { + String sourceIp = request.getRemoteAddr(); + String destIp = request.getLocalAddr(); + String remoteIp = IpUtil.getRemoteIpAddress(request); + String host = request.getHeader(HttpHeaders.HOST); + String method = request.getMethod(); + String path = request.getRequestURI(); + int port = request.getLocalPort(); + HttpHeaders headers = getHeaders(request); + MultiValueMap params = getQueryParams(request); + AuthValidator.UnifiedHttpRequest.UnifiedHttpRequestBuilder builder = new AuthValidator.UnifiedHttpRequest.UnifiedHttpRequestBuilder(); + AuthValidator.UnifiedHttpRequest unifiedHttpRequest = builder.setDestIp(destIp) + .setRemoteIp(remoteIp).setSourceIp(sourceIp).setHost(host).setPort(port) + .setMethod(method).setPath(path).setHeaders(headers).setParams(params) + .build(); + if (!authValidator.validate(unifiedHttpRequest)) { + return ret401(response); + } + return true; + } + + private HttpHeaders getHeaders(HttpServletRequest request) { + HttpHeaders headers = new HttpHeaders(); + Enumeration headerNames = request.getHeaderNames(); + while (headerNames.hasMoreElements()) { + try { + String key = headerNames.nextElement(); + key = key.substring(0, 1).toUpperCase() + key.substring(1); + Enumeration headerValues = request.getHeaders(key); + while (headerValues.hasMoreElements()) { + headers.add(key, headerValues.nextElement()); + } + } + catch (Exception e) { + log.error("Unknown header key", e); + } + } + return headers; + } + + private MultiValueMap getQueryParams(HttpServletRequest request) { + MultiValueMap params = new LinkedMultiValueMap<>(); + Enumeration paramNames = request.getParameterNames(); + while (paramNames.hasMoreElements()) { + String key = paramNames.nextElement(); + String[] paramValues = request.getParameterValues(key); + params.addAll(key, Arrays.asList(paramValues)); + } + return params; + } + + private boolean ret401(HttpServletResponse response) throws IOException { + return ret401(response, "Auth failed, please check the request and auth rule"); + } + + private boolean ret401(HttpServletResponse response, String errorMsg) + throws IOException { + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + response.getWriter().println(errorMsg); + return false; + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/resources/META-INF/spring.factories b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/resources/META-INF/spring.factories new file mode 100644 index 000000000..3a615cd91 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/main/resources/META-INF/spring.factories @@ -0,0 +1,5 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +com.alibaba.cloud.governance.auth.XdsWebAutoConfiguration,\ +com.alibaba.cloud.governance.auth.XdsWebFluxAutoConfiguration,\ +com.alibaba.cloud.governance.auth.AuthValidatorAutoConfiguration,\ +com.alibaba.cloud.governance.auth.AuthenticationAutoConfiguration diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/test/java/com/alibaba/cloud/governance/auth/AuthenticationTest.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/test/java/com/alibaba/cloud/governance/auth/AuthenticationTest.java new file mode 100644 index 000000000..abc36ac64 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/test/java/com/alibaba/cloud/governance/auth/AuthenticationTest.java @@ -0,0 +1,145 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.governance.auth; + +import java.io.File; +import java.nio.charset.Charset; + +import com.alibaba.cloud.commons.governance.auth.condition.AuthCondition; +import com.alibaba.cloud.commons.governance.auth.rule.AuthRule; +import com.alibaba.cloud.commons.io.FileUtils; +import com.alibaba.cloud.commons.matcher.IpMatcher; +import com.alibaba.cloud.commons.matcher.Matcher; +import com.alibaba.cloud.commons.matcher.PortMatcher; +import com.alibaba.cloud.commons.matcher.StringMatcher; +import com.alibaba.cloud.governance.auth.repository.AuthRepository; +import com.alibaba.cloud.governance.auth.validator.AuthValidator; +import com.alibaba.fastjson2.JSONObject; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.ImportAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class AuthenticationTest { + + @Autowired + private AuthValidator authValidator; + + @Autowired + private AuthRepository authRepository; + + @Test + public void judgeFromIpAllow() throws Exception { + AuthRepository authRepository = fromJSON(FileUtils.readFileToString( + new File("src/test/resources/from-ip-allow.json"), + Charset.defaultCharset())); + if (authRepository == null) { + throw new Exception("Can not load auth rules from auth repository"); + } + refreshAuthRepository(authRepository); + AuthValidator.UnifiedHttpRequest.UnifiedHttpRequestBuilder builder = new AuthValidator.UnifiedHttpRequest.UnifiedHttpRequestBuilder(); + builder.setSourceIp("127.0.0.1"); + Assert.assertTrue(authValidator.validate(builder.build())); + builder.setSourceIp("192.168.1.1"); + Assert.assertFalse(authValidator.validate(builder.build())); + } + + @Test + public void judgeMethodAllow() throws Exception { + AuthRepository authRepository = fromJSON(FileUtils.readFileToString( + new File("src/test/resources/method-allow.json"), + Charset.defaultCharset())); + if (authRepository == null) { + throw new Exception("Can not load auth rules from auth repository"); + } + refreshAuthRepository(authRepository); + AuthValidator.UnifiedHttpRequest.UnifiedHttpRequestBuilder builder = new AuthValidator.UnifiedHttpRequest.UnifiedHttpRequestBuilder(); + builder.setMethod("HEAD"); + Assert.assertTrue(authValidator.validate(builder.build())); + builder.setMethod("GET"); + Assert.assertTrue(authValidator.validate(builder.build())); + builder.setMethod("POST"); + Assert.assertFalse(authValidator.validate(builder.build())); + builder.setMethod("PUT"); + Assert.assertFalse(authValidator.validate(builder.build())); + } + + private void refreshAuthRepository(AuthRepository authRepository) { + this.authRepository.setDenyAuthRules(authRepository.getDenyAuthRules()); + this.authRepository.setAllowAuthRule(authRepository.getAllowAuthRules()); + this.authRepository.setJwtRule(authRepository.getJwtRules()); + } + + private AuthRepository fromJSON(String json) { + AuthRepository authRepository; + authRepository = JSONObject.parseObject(json, AuthRepository.class); + for (AuthRule allowRule : authRepository.getAllowAuthRules().values()) { + reloadAuthRule(allowRule); + } + for (AuthRule denyRule : authRepository.getDenyAuthRules().values()) { + reloadAuthRule(denyRule); + } + return authRepository; + } + + private void reloadAuthRule(AuthRule authRule) { + AuthCondition authCondition = authRule.getCondition(); + if (authCondition != null) { + Matcher matcher = authCondition.getMatcher(); + if (matcher != null) { + String matcherJSON = matcher.toString(); + switch (authCondition.getType()) { + case DEST_IP: + case REMOTE_IP: + case SOURCE_IP: + authCondition.setMatcher( + JSONObject.parseObject(matcherJSON, IpMatcher.class)); + break; + case PORTS: + authCondition.setMatcher( + JSONObject.parseObject(matcherJSON, PortMatcher.class)); + break; + default: + authCondition.setMatcher( + JSONObject.parseObject(matcherJSON, StringMatcher.class)); + break; + } + } + } + if (authRule.getChildren() != null) { + for (AuthRule rule : authRule.getChildren()) { + reloadAuthRule(rule); + } + } + } + + @Configuration + @EnableAutoConfiguration + @ImportAutoConfiguration({ AuthValidatorAutoConfiguration.class }) + public static class TestConfig { + + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/test/resources/from-ip-allow.json b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/test/resources/from-ip-allow.json new file mode 100644 index 000000000..564e61bae --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/test/resources/from-ip-allow.json @@ -0,0 +1,47 @@ +{ + "allowAuthRules": { + "ns[default]-policy[from-ip-allow]-rule[0]": { + "children": [ + { + "children": [ + { + "children": [ + { + "children": [ + { + "children": [], + "condition": { + "matcher": { + "ip": "127.0.0.1", + "prefixLen": 32 + }, + "type": "SOURCE_IP" + }, + "empty": false, + "not": false, + "op": "UNKNOWN" + } + ], + "empty": false, + "not": false, + "op": "OR" + } + ], + "empty": false, + "not": false, + "op": "AND" + } + ], + "empty": false, + "not": false, + "op": "OR" + } + ], + "empty": false, + "not": false, + "op": "AND" + } + }, + "denyAuthRules": {}, + "jwtRules": {} +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/test/resources/method-allow.json b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/test/resources/method-allow.json new file mode 100644 index 000000000..cfec6f6be --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-auth/src/test/resources/method-allow.json @@ -0,0 +1,68 @@ +{ + "allowAuthRules": { + "ns[default]-policy[method-allow]-rule[0]": { + "children": [ + { + "children": [ + { + "children": [ + { + "children": [ + { + "children": [], + "condition": { + "matcher": { + "ignoreCase": true, + "matcher": "GET", + "type": "EXACT" + }, + "type": "METHODS" + }, + "empty": false, + "leaf": true, + "not": false, + "op": "UNKNOWN" + }, + { + "children": [], + "condition": { + "matcher": { + "ignoreCase": true, + "matcher": "HEAD", + "type": "EXACT" + }, + "type": "METHODS" + }, + "empty": false, + "leaf": true, + "not": false, + "op": "UNKNOWN" + } + ], + "empty": false, + "leaf": false, + "not": false, + "op": "OR" + } + ], + "empty": false, + "leaf": false, + "not": false, + "op": "AND" + } + ], + "empty": false, + "leaf": false, + "not": false, + "op": "OR" + } + ], + "empty": false, + "leaf": false, + "not": false, + "op": "AND" + } + }, + "denyAuthRules": {}, + "jwtRules": {} +} \ No newline at end of file diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/pom.xml b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/pom.xml new file mode 100644 index 000000000..defddc721 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/pom.xml @@ -0,0 +1,52 @@ + + + 4.0.0 + + com.alibaba.cloud + spring-cloud-alibaba-starters + ${revision} + ../pom.xml + + spring-cloud-starter-alibaba-governance-routing + Spring Cloud Starter Alibaba Governance Label Routing + + + + com.netflix.ribbon + ribbon-loadbalancer + + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + + org.springframework + spring-webmvc + + + + javax.servlet + javax.servlet-api + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + org.springframework.boot + spring-boot-autoconfigure + + + + org.slf4j + slf4j-api + + + + + diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/LabelRoutingAutoConfiguration.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/LabelRoutingAutoConfiguration.java new file mode 100644 index 000000000..2080d3f4e --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/LabelRoutingAutoConfiguration.java @@ -0,0 +1,55 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.router; + +import com.alibaba.cloud.router.listener.LabelRouteDataListener; +import com.alibaba.cloud.router.repository.FilterService; +import com.alibaba.cloud.router.repository.RouteDataRepository; + +import org.springframework.boot.autoconfigure.AutoConfigureOrder; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration(proxyBeanMethods = false) +@AutoConfigureOrder(LabelRoutingAutoConfiguration.LABEL_ROUTING_AUTO_CONFIG_ORDER) +public class LabelRoutingAutoConfiguration { + + /** + * Order of label routing auto config. + */ + public static final int LABEL_ROUTING_AUTO_CONFIG_ORDER = 10; + + @Bean + @ConditionalOnMissingBean + public RouteDataRepository routeDataRepository() { + return new RouteDataRepository(); + } + + @Bean + @ConditionalOnMissingBean + public FilterService filterService() { + return new FilterService(); + } + + @Bean + public LabelRouteDataListener labelRouteDataListener( + RouteDataRepository routeDataRepository, FilterService filterService) { + return new LabelRouteDataListener(routeDataRepository, filterService); + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/RouterProperties.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/RouterProperties.java new file mode 100644 index 000000000..3c64b7e27 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/RouterProperties.java @@ -0,0 +1,45 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.router; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * @author HH + */ +@ConfigurationProperties(prefix = RouterProperties.PROPERTY_PREFIX) +public class RouterProperties { + + /** + * Properties prefix. + */ + public static final String PROPERTY_PREFIX = "spring.cloud.governance.router"; + + /** + * Load Balance Rule. + */ + private String rule; + + public String getRule() { + return rule; + } + + public void setRule(String rule) { + this.rule = rule; + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/RouterPropertiesAutoConfiguration.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/RouterPropertiesAutoConfiguration.java new file mode 100644 index 000000000..cfe2e3d8d --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/RouterPropertiesAutoConfiguration.java @@ -0,0 +1,44 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.router; + +import com.alibaba.cloud.router.publish.TargetServiceChangedPublisher; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author HH + */ +@Configuration(proxyBeanMethods = false) +@EnableConfigurationProperties({ RouterProperties.class }) +public class RouterPropertiesAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + public RouterProperties routerProperties() { + return new RouterProperties(); + } + + @Bean + public TargetServiceChangedPublisher targetServiceChangedPublisher() { + return new TargetServiceChangedPublisher(); + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/feign/FeignInterceptor.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/feign/FeignInterceptor.java new file mode 100644 index 000000000..d16e82da9 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/feign/FeignInterceptor.java @@ -0,0 +1,49 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.router.feign; + +import java.util.Enumeration; + +import javax.servlet.http.HttpServletRequest; + +import com.alibaba.cloud.router.util.RequestContext; +import feign.RequestInterceptor; +import feign.RequestTemplate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author HH + */ +public class FeignInterceptor implements RequestInterceptor { + + private static final Logger LOG = LoggerFactory.getLogger(FeignInterceptor.class); + + @Override + public void apply(RequestTemplate requestTemplate) { + final HttpServletRequest request = RequestContext.getRequest(); + final Enumeration headerNames = request.getHeaderNames(); + if (headerNames == null) { + return; + } + while (headerNames.hasMoreElements()) { + String headerName = headerNames.nextElement(); + requestTemplate.header(headerName, request.getHeader(headerName)); + } + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/feign/FeignInterceptorAutoConfiguration.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/feign/FeignInterceptorAutoConfiguration.java new file mode 100644 index 000000000..effd694fb --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/feign/FeignInterceptorAutoConfiguration.java @@ -0,0 +1,35 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.router.feign; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author HH + */ +@Configuration(proxyBeanMethods = false) +public class FeignInterceptorAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + public FeignInterceptor feignInterceptor() { + return new FeignInterceptor(); + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/listener/LabelRouteDataListener.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/listener/LabelRouteDataListener.java new file mode 100644 index 000000000..8e9592147 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/listener/LabelRouteDataListener.java @@ -0,0 +1,76 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.router.listener; + +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.stream.Collectors; + +import com.alibaba.cloud.commons.governance.event.LabelRoutingDataChangedEvent; +import com.alibaba.cloud.commons.governance.labelrouting.UnifiedRouteDataStructure; +import com.alibaba.cloud.router.repository.FilterService; +import com.alibaba.cloud.router.repository.RouteDataRepository; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.context.ApplicationListener; + +/** + * @author musi + * @author + */ +public class LabelRouteDataListener + implements ApplicationListener { + + private static final Logger log = LoggerFactory + .getLogger(LabelRouteDataListener.class); + + private RouteDataRepository routeDataRepository; + + private FilterService filterService; + + public LabelRouteDataListener(RouteDataRepository routeDataRepository, + FilterService filterService) { + this.routeDataRepository = routeDataRepository; + this.filterService = filterService; + } + + @Override + public void onApplicationEvent(LabelRoutingDataChangedEvent event) { + try { + Collection untiedRouterDataStructureList = event + .getUntiedRouterDataStructureList(); + + // Filter service. + // todo can cache the result + HashSet definitionFeignService = filterService + .getDefinitionFeignService(untiedRouterDataStructureList.size()); + List routeDatalist = untiedRouterDataStructureList + .stream() + .filter(unifiedRouteDataStructure -> definitionFeignService + .contains(unifiedRouteDataStructure.getTargetService())) + .collect(Collectors.toList()); + + routeDataRepository.updateRouteData(routeDatalist); + } + catch (Exception e) { + log.error("Failed to update route data", e); + } + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/publish/TargetServiceChangedPublisher.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/publish/TargetServiceChangedPublisher.java new file mode 100644 index 000000000..5080f06e1 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/publish/TargetServiceChangedPublisher.java @@ -0,0 +1,48 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.router.publish; + +import java.util.concurrent.ConcurrentHashMap; + +import com.alibaba.cloud.commons.governance.event.TargetServiceChangedEvent; + +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; + +public class TargetServiceChangedPublisher implements ApplicationContextAware { + + private ConcurrentHashMap targetServiceMap = new ConcurrentHashMap(); + + private final Object object = new Object(); + + public void addTargetService(String targetService) { + Object value = targetServiceMap.putIfAbsent(targetService, object); + if (value == null && applicationContext != null) { + applicationContext.publishEvent(new TargetServiceChangedEvent(targetService)); + } + } + + private ApplicationContext applicationContext; + + @Override + public void setApplicationContext(ApplicationContext applicationContext) + throws BeansException { + this.applicationContext = applicationContext; + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/repository/FilterService.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/repository/FilterService.java new file mode 100644 index 000000000..33b2ab386 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/repository/FilterService.java @@ -0,0 +1,81 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.router.repository; + +import java.util.HashSet; + +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; + +/** + * @author HH + */ +public class FilterService implements ApplicationContextAware { + + /** + * Feign bean name suffix. + */ + public static final String FEIGN_CLIENT_BEAN_SPECIFICATION = ".FeignClientSpecification"; + + /** + * Feign bean name prefix. + */ + public static final String FEIGN_CLIENT_BEAN_DEFAULT = "default."; + + /** + * Feign bean name start char. + */ + public static final String FEIGN_CLIENT_BEAN_START = "${"; + + /** + * Feign bean name end char. + */ + public static final String FEIGN_CLIENT_BEAN_END = "}"; + + /** + * Spring bean Container. + */ + private ApplicationContext applicationContext; + + @Override + public void setApplicationContext(ApplicationContext applicationContext) { + this.applicationContext = applicationContext; + } + + public HashSet getDefinitionFeignService(int size) { + String[] allBeanNames = applicationContext.getBeanDefinitionNames(); + HashSet serviceSet = new HashSet<>(size); + for (String beanName : allBeanNames) { + if (beanName.contains(FEIGN_CLIENT_BEAN_SPECIFICATION) + && !beanName.startsWith(FEIGN_CLIENT_BEAN_DEFAULT)) { + String feignName = beanName.substring(0, + beanName.indexOf(FEIGN_CLIENT_BEAN_SPECIFICATION)); + if (feignName.startsWith(FEIGN_CLIENT_BEAN_START)) { + String resolveFeignName = feignName.replace(FEIGN_CLIENT_BEAN_START, + ""); + resolveFeignName = resolveFeignName.replace(FEIGN_CLIENT_BEAN_END, + ""); + feignName = resolveFeignName; + } + serviceSet.add(feignName); + } + } + + return serviceSet; + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/repository/RouteDataRepository.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/repository/RouteDataRepository.java new file mode 100644 index 000000000..9eb4a914f --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/repository/RouteDataRepository.java @@ -0,0 +1,167 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.router.repository; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; + +import com.alibaba.cloud.commons.governance.labelrouting.LabelRouteRule; +import com.alibaba.cloud.commons.governance.labelrouting.MatchService; +import com.alibaba.cloud.commons.governance.labelrouting.UnifiedRouteDataStructure; +import com.alibaba.cloud.commons.governance.labelrouting.rule.RouteRule; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.util.CollectionUtils; + +/** + * @author HH + */ +public class RouteDataRepository { + + private static final Logger LOG = LoggerFactory.getLogger(RouteDataRepository.class); + + /** + * Key is service name,value is hashmap,which key is single RouteRule key,value is + * match service. Use double hash index to parse route rule. + */ + private ConcurrentHashMap>> routeCache = new ConcurrentHashMap<>(); + + /** + * The default version of each service. + */ + private ConcurrentHashMap defaultRouteVersion = new ConcurrentHashMap<>(); + + /** + * Sign of path. + */ + private static final String PATH = "path"; + + /** + * Contain rule of single path rule. + */ + private ConcurrentHashMap> pathRuleMap = new ConcurrentHashMap<>(); + + /** + * If do not set weight value,it will be set 100 by default. + */ + private static final int DEFAULT_WEIGHT = 100; + + /** + * Sum of all version's weight. + */ + public static final int SUM_WEIGHT = 100; + + /** + * Weight value can't less than it. + */ + public static final int MIN_WEIGHT = 0; + + public void updateRouteData(final List routeDataList) { + ConcurrentHashMap>> newRouteCache = new ConcurrentHashMap<>(); + ConcurrentHashMap> newPathRuleMap = new ConcurrentHashMap<>(); + for (UnifiedRouteDataStructure routeData : routeDataList) { + nonNullCheck(routeData); + buildHashIndex(routeData, newRouteCache, newPathRuleMap); + defaultRouteVersion.put(routeData.getTargetService(), + routeData.getLabelRouteRule().getDefaultRouteVersion()); + } + // Replace it atomically + this.routeCache = newRouteCache; + this.pathRuleMap = newPathRuleMap; + } + + private void nonNullCheck(UnifiedRouteDataStructure unifiedRouteDataStructure) { + String targetService = unifiedRouteDataStructure.getTargetService(); + if (targetService == null) { + LOG.error("Lose target Service name."); + } + final LabelRouteRule labelRouteData = unifiedRouteDataStructure + .getLabelRouteRule(); + final List matchServiceList = labelRouteData.getMatchRouteList(); + for (MatchService matchService : matchServiceList) { + final List ruleList = matchService.getRuleList(); + String version = matchService.getVersion(); + Integer weight = matchService.getWeight(); + if (CollectionUtils.isEmpty(ruleList)) { + LOG.error("Rule is empty in version = {} ", version); + } + if (version == null) { + LOG.error("Target service = {} lose version,please check it. ", + targetService); + } + if (weight == null) { + weight = DEFAULT_WEIGHT; + } + if (weight < MIN_WEIGHT || weight > SUM_WEIGHT) { + LOG.error( + "The weight of provider = {} version = {} had set error,please check it. ", + targetService, version); + } + } + } + + private void buildHashIndex(final UnifiedRouteDataStructure routerData, + ConcurrentHashMap>> newRouteCache, + ConcurrentHashMap> newPathRuleMap) { + final List matchRouteList = routerData.getLabelRouteRule() + .getMatchRouteList(); + HashMap> singleRuleMap = new HashMap<>(); + + for (MatchService matchService : matchRouteList) { + List ruleList = matchService.getRuleList(); + + // Take out the path label separately, because there is no key for hash index. + if (ruleList.size() == 1 + && PATH.equalsIgnoreCase(ruleList.get(0).getType())) { + List matchServiceList = newPathRuleMap + .get(routerData.getTargetService()); + if (matchServiceList == null) { + matchServiceList = new ArrayList<>(); + } + matchServiceList.add(matchService); + newPathRuleMap.put(routerData.getTargetService(), matchServiceList); + continue; + } + for (RouteRule routeRule : ruleList) { + List matchServiceList = singleRuleMap + .get(routeRule.getKey()); + if (matchServiceList == null) { + matchServiceList = new ArrayList<>(); + } + matchServiceList.add(matchService); + singleRuleMap.put(routeRule.getKey(), matchServiceList); + } + } + newRouteCache.put(routerData.getTargetService(), singleRuleMap); + } + + public HashMap> getRouteRule(String targetService) { + return routeCache.get(targetService); + } + + public String getDefaultRouteVersion(String targetService) { + return defaultRouteVersion.get(targetService); + } + + public List getPathRules(String targetService) { + return pathRuleMap.get(targetService); + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/ribbon/LabelRouteLBRule.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/ribbon/LabelRouteLBRule.java new file mode 100644 index 000000000..dd55cee4a --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/ribbon/LabelRouteLBRule.java @@ -0,0 +1,405 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.router.ribbon; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ThreadLocalRandom; + +import javax.servlet.http.HttpServletRequest; + +import com.alibaba.cloud.commons.governance.labelrouting.MatchService; +import com.alibaba.cloud.commons.governance.labelrouting.rule.RouteRule; +import com.alibaba.cloud.nacos.NacosDiscoveryProperties; +import com.alibaba.cloud.nacos.NacosServiceManager; +import com.alibaba.cloud.nacos.ribbon.NacosServer; +import com.alibaba.cloud.router.RouterProperties; +import com.alibaba.cloud.router.publish.TargetServiceChangedPublisher; +import com.alibaba.cloud.router.repository.RouteDataRepository; +import com.alibaba.cloud.router.util.ConditionMatchUtil; +import com.alibaba.cloud.router.util.LoadBalanceUtil; +import com.alibaba.cloud.router.util.RequestContext; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.pojo.Instance; +import com.netflix.loadbalancer.AbstractServerPredicate; +import com.netflix.loadbalancer.DynamicServerListLoadBalancer; +import com.netflix.loadbalancer.PredicateBasedRule; +import com.netflix.loadbalancer.Server; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.CollectionUtils; + +/** + * @author HH + */ +public class LabelRouteLBRule extends PredicateBasedRule { + + private static final Logger LOG = LoggerFactory.getLogger(LabelRouteLBRule.class); + + /** + * Support Parsing Rules from path,only URI at present. + */ + private static final String PATH = "path"; + + /** + * Support Parsing Rules from header. + */ + private static final String HEADER = "header"; + + /** + * Support Parsing Rules from parameter. + */ + private static final String PARAMETER = "parameter"; + + /** + * Filter base on version metadata. + */ + private static final String VERSION = "version"; + + /** + * Sign of no match any rule. + */ + private static final int NO_MATCH = -1; + + /** + * Avoid loss of accuracy. + */ + private static final double KEEP_ACCURACY = 1.0; + + /** + * Composite route. todo + */ + private AbstractServerPredicate predicate; + + /** + * Match condition util. + */ + private final ConditionMatchUtil conditionMatchUtil = new ConditionMatchUtil(); + + /** + * Load Balance Util. + */ + private final LoadBalanceUtil loadBalanceUtil = new LoadBalanceUtil(); + + @Autowired + private NacosDiscoveryProperties nacosDiscoveryProperties; + + @Autowired + private NacosServiceManager nacosServiceManager; + + @Autowired + private RouteDataRepository routeDataRepository; + + @Autowired + private RouterProperties routerProperties; + + @Autowired + private TargetServiceChangedPublisher targetServiceChangedPublisher; + + @Override + public Server choose(Object key) { + try { + final DynamicServerListLoadBalancer loadBalancer = (DynamicServerListLoadBalancer) getLoadBalancer(); + String targetServiceName = loadBalancer.getName(); + targetServiceChangedPublisher.addTargetService(targetServiceName); + + // If routeData isn't present, use normal load balance rule. + final HashMap> routeData = routeDataRepository + .getRouteRule(targetServiceName); + if (routeData == null) { + return loadBalanceUtil.loadBalanceByOrdinaryRule(loadBalancer, key, + routerProperties.getRule()); + } + + // Get instances from register-center. + String group = this.nacosDiscoveryProperties.getGroup(); + final NamingService namingService = nacosServiceManager.getNamingService(); + final List instances = namingService + .selectInstances(targetServiceName, group, true); + if (CollectionUtils.isEmpty(instances)) { + LOG.warn("no instance in service {} ", targetServiceName); + return null; + } + + // Filter by route rules,the result will be kept in versionSet and weightMap. + HashSet versionSet = new HashSet<>(); + HashMap weightMap = new HashMap<>(); + HashSet fallbackVersionSet = new HashSet<>(); + HashMap fallbackWeightMap = new HashMap<>(); + + serviceFilter(targetServiceName, versionSet, weightMap, fallbackVersionSet, + fallbackWeightMap); + + // Filter instances by versionSet and weightMap. + double[] weightArray = new double[instances.size()]; + HashMap> instanceMap = new HashMap<>(); + for (Instance instance : instances) { + String version = instance.getMetadata().get(VERSION); + if (versionSet.contains(version)) { + List instanceList = instanceMap.get(version); + if (instanceList == null) { + instanceList = new ArrayList<>(); + } + instanceList.add(instance); + instanceMap.put(version, instanceList); + } + } + + // None instance match rule + if (CollectionUtils.isEmpty(instanceMap)) { + LOG.warn("no instance match route rule"); + for (Instance instance : instances) { + String version = instance.getMetadata().get(VERSION); + if (fallbackVersionSet.contains(version)) { + List instanceList = instanceMap.get(version); + if (instanceList == null) { + instanceList = new ArrayList<>(); + } + instanceList.add(instance); + instanceMap.put(version, instanceList); + } + } + return chooseServerByWeight(instanceMap, fallbackWeightMap, weightArray); + } + + // Routing with Weight algorithm. + return chooseServerByWeight(instanceMap, weightMap, weightArray); + + } + catch (Exception e) { + LOG.warn("LabelRouteRule error", e); + return null; + } + } + + @Override + public AbstractServerPredicate getPredicate() { + return this.predicate; + } + + private void serviceFilter(String targetServiceName, HashSet versionSet, + HashMap weightMap, HashSet fallbackVersionSet, + HashMap fallbackWeightMap) { + // Get request metadata. + final HttpServletRequest request = RequestContext.getRequest(); + final Enumeration headerNames = request.getHeaderNames(); + HashMap requestHeaders = new HashMap<>(); + if (headerNames != null) { + while (headerNames.hasMoreElements()) { + String name = headerNames.nextElement(); + String value = request.getHeader(name); + requestHeaders.put(name, value); + } + } + final Map parameterMap = request.getParameterMap(); + int defaultVersionWeight = RouteDataRepository.SUM_WEIGHT; + boolean isMatch = false; + + // Parse rule. + if (requestHeaders.size() > 0) { + for (String keyName : requestHeaders.keySet()) { + int weight = matchRule(targetServiceName, keyName, requestHeaders, + parameterMap, request, versionSet, weightMap, fallbackVersionSet, + fallbackWeightMap); + if (weight != NO_MATCH) { + isMatch = true; + defaultVersionWeight -= weight; + break; + } + } + } + + if (!isMatch && parameterMap != null) { + for (String keyName : parameterMap.keySet()) { + int weight = matchRule(targetServiceName, keyName, requestHeaders, + parameterMap, request, versionSet, weightMap, fallbackVersionSet, + fallbackWeightMap); + if (weight != NO_MATCH) { + isMatch = true; + defaultVersionWeight -= weight; + break; + } + } + } + + final List pathRules = routeDataRepository + .getPathRules(targetServiceName); + if (!isMatch && pathRules != null) { + for (MatchService matchService : pathRules) { + if (matchService.getRuleList().get(0).getValue() + .equals(request.getRequestURI())) { + String version = matchService.getVersion(); + Integer weight = matchService.getWeight(); + versionSet.add(version); + weightMap.put(version, weight); + defaultVersionWeight -= weight; + } + } + } + + // Add default route + if (defaultVersionWeight > RouteDataRepository.MIN_WEIGHT) { + String defaultRouteVersion = routeDataRepository + .getDefaultRouteVersion(targetServiceName); + versionSet.add(defaultRouteVersion); + weightMap.put(defaultRouteVersion, defaultVersionWeight); + } + + } + + private int matchRule(String targetServiceName, String keyName, + final HashMap requestHeaders, + final Map parameterMap, final HttpServletRequest request, + HashSet versionSet, HashMap weightMap, + HashSet fallbackVersionSet, + HashMap fallbackWeightMap) { + final List matchServiceList = routeDataRepository + .getRouteRule(targetServiceName).get(keyName); + if (matchServiceList == null) { + return NO_MATCH; + } + for (MatchService matchService : matchServiceList) { + final List ruleList = matchService.getRuleList(); + String version = matchService.getVersion(); + Integer weight = matchService.getWeight(); + String fallback = matchService.getFallback(); + boolean isMatchRule = true; + for (RouteRule routeRule : ruleList) { + String type = routeRule.getType(); + switch (type) { + case PATH: + isMatchRule = parseRequestPath(routeRule, request); + break; + case HEADER: + isMatchRule = parseRequestHeader(routeRule, requestHeaders); + break; + case PARAMETER: + isMatchRule = parseRequestParameter(routeRule, parameterMap); + break; + default: + throw new UnsupportedOperationException( + "unsupported string compare operation"); + } + if (!isMatchRule) { + break; + } + } + if (!isMatchRule) { + continue; + } + versionSet.add(version); + fallbackVersionSet.add(fallback); + fallbackWeightMap.put(fallback, weight); + weightMap.put(version, weight); + return weight; + } + return NO_MATCH; + } + + private boolean parseRequestPath(final RouteRule routeRule, + final HttpServletRequest request) { + String condition = routeRule.getCondition(); + String value = routeRule.getValue(); + String uri = request.getRequestURI(); + return conditionMatch(condition, value, uri); + } + + private boolean parseRequestHeader(final RouteRule routeRule, + final HashMap requestHeaders) { + if (requestHeaders.size() == 0) { + return false; + } + String condition = routeRule.getCondition(); + String value = routeRule.getValue(); + String headerValue = requestHeaders.get(routeRule.getKey()); + return conditionMatch(condition, value, headerValue); + } + + private boolean parseRequestParameter(final RouteRule routeRule, + final Map parameterMap) { + if (parameterMap == null || parameterMap.size() == 0) { + return false; + } + String condition = routeRule.getCondition(); + String value = routeRule.getValue(); + String[] paramValues = parameterMap.get(routeRule.getKey()); + String paramValue = paramValues == null ? null : paramValues[0]; + return conditionMatch(condition, value, paramValue); + } + + private boolean conditionMatch(String condition, String str, String comparator) { + switch (condition) { + case ConditionMatchUtil.EXACT: + case ConditionMatchUtil.EQUAL: + return conditionMatchUtil.exactMatch(str, comparator); + case ConditionMatchUtil.REGEX: + return conditionMatchUtil.regexMatch(str, comparator); + case ConditionMatchUtil.PREFIX: + return conditionMatchUtil.prefixMatch(str, comparator); + case ConditionMatchUtil.CONTAIN: + return conditionMatchUtil.containMatch(str, comparator); + case ConditionMatchUtil.GREATER: + return conditionMatchUtil.greaterMatch(str, comparator); + case ConditionMatchUtil.LESS: + return conditionMatchUtil.lessMatch(str, comparator); + case ConditionMatchUtil.NOT_EQUAL: + return conditionMatchUtil.noEqualMatch(str, comparator); + default: + throw new UnsupportedOperationException( + "unsupported string compare operation"); + } + } + + private Server chooseServerByWeight(final HashMap> instanceMap, + final HashMap weightMap, final double[] weightArray) { + int index = 0; + double sum = 0.0D; + List instances = new ArrayList<>(); + + for (String version : instanceMap.keySet()) { + int weight = weightMap.get(version); + List instanceList = instanceMap.get(version); + for (Instance instance : instanceList) { + instances.add(instance); + weightArray[index] = KEEP_ACCURACY * weight / instanceList.size() + sum; + sum = weightArray[index]; + index++; + } + } + + if (sum > RouteDataRepository.SUM_WEIGHT) { + LOG.error("Sum of weight has over {} ", RouteDataRepository.SUM_WEIGHT); + } + + double random = ThreadLocalRandom.current().nextDouble( + RouteDataRepository.MIN_WEIGHT, RouteDataRepository.SUM_WEIGHT); + int chooseServiceIndex = Arrays.binarySearch(weightArray, random); + if (chooseServiceIndex < 0) { + chooseServiceIndex = -chooseServiceIndex - 1; + } + + return new NacosServer(instances.get(chooseServiceIndex)); + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/ribbon/LabelRouteLBRuleAutoConfiguration.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/ribbon/LabelRouteLBRuleAutoConfiguration.java new file mode 100644 index 000000000..260e0c3a3 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/ribbon/LabelRouteLBRuleAutoConfiguration.java @@ -0,0 +1,35 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.router.ribbon; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author HH + */ +@Configuration(proxyBeanMethods = false) +public class LabelRouteLBRuleAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + public LabelRouteLBRule labelRouteRule() { + return new LabelRouteLBRule(); + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/util/ConditionMatchUtil.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/util/ConditionMatchUtil.java new file mode 100644 index 000000000..775a5b09e --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/util/ConditionMatchUtil.java @@ -0,0 +1,94 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.router.util; + +import java.util.regex.Pattern; + +/** + * @author HH + */ +public class ConditionMatchUtil { + + /** + * Sign of exact. + */ + public static final String EXACT = "exact"; + + /** + * Sign of regex. + */ + public static final String REGEX = "regex"; + + /** + * Sign of contain. + */ + public static final String PREFIX = "prefix"; + + /** + * Sign of contain. + */ + public static final String CONTAIN = "contain"; + + /** + * Sign of greater. + */ + public static final String GREATER = ">"; + + /** + * Sign of less. + */ + public static final String LESS = "<"; + + /** + * Sign of equal. + */ + public static final String EQUAL = "="; + + /** + * Sign of no equal. + */ + public static final String NOT_EQUAL = "not_equal"; + + public boolean exactMatch(String one, String another) { + return one.equals(another); + } + + public boolean regexMatch(String regex, String path) { + return Pattern.matches(regex, path); + } + + public boolean containMatch(String sub, String base) { + return base.contains(sub); + } + + public boolean prefixMatch(String prefix, String str) { + return str.startsWith(prefix); + } + + public boolean greaterMatch(String str, String comparor) { + return Integer.parseInt(comparor) > Integer.parseInt(str); + } + + public boolean lessMatch(String str, String comparor) { + return Integer.parseInt(comparor) < Integer.parseInt(str); + } + + public boolean noEqualMatch(String str, String comparor) { + return Integer.parseInt(str) == Integer.parseInt(comparor); + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/util/LoadBalanceUtil.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/util/LoadBalanceUtil.java new file mode 100644 index 000000000..bf370d12e --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/util/LoadBalanceUtil.java @@ -0,0 +1,101 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.router.util; + +import com.netflix.loadbalancer.AvailabilityFilteringRule; +import com.netflix.loadbalancer.BestAvailableRule; +import com.netflix.loadbalancer.ILoadBalancer; +import com.netflix.loadbalancer.RandomRule; +import com.netflix.loadbalancer.RetryRule; +import com.netflix.loadbalancer.RoundRobinRule; +import com.netflix.loadbalancer.Server; +import com.netflix.loadbalancer.WeightedResponseTimeRule; +import com.netflix.loadbalancer.ZoneAvoidanceRule; + +/** + * @author HH + */ +public class LoadBalanceUtil { + + /** + * Default polling. + */ + public static final String ROUND_ROBIN_RULE = "RoundRobinRule"; + + /** + * Random choose a instance. + */ + public static final String RANDOM_RULE = "RandomRule"; + + /** + * The weight is allocated according to the response time. + */ + public static final String WEIGHTED_RESPONSE_TIME_RULE = "WeightedResponseTimeRule"; + + /** + * Select the method with the minimum concurrency. + */ + public static final String BEST_AVAILABLE_RULE = "BestAvailableRule"; + + /** + * Retry when fail. + */ + public static final String RETRY_RULE = "RetryRule"; + + /** + * Choose based on performance and availability. + */ + public static final String ZONE_AVOIDANCE_RULE = "ZoneAvoidanceRule"; + + /** + * Filter by Availability. + */ + public static final String AVAILABILITY_FILTERING_RULE = "AvailabilityFilteringRule"; + + public Server loadBalanceByOrdinaryRule(ILoadBalancer iLoadBalancer, Object key, + String rule) { + if (rule == null) { + rule = ZONE_AVOIDANCE_RULE; + } + switch (rule) { + case ROUND_ROBIN_RULE: + return new RoundRobinRule().choose(iLoadBalancer, key); + case RANDOM_RULE: + return new RandomRule().choose(iLoadBalancer, key); + case WEIGHTED_RESPONSE_TIME_RULE: + return new WeightedResponseTimeRule().choose(iLoadBalancer, key); + case BEST_AVAILABLE_RULE: + BestAvailableRule bestAvailableRule = new BestAvailableRule(); + bestAvailableRule.setLoadBalancer(iLoadBalancer); + return bestAvailableRule.choose(key); + case RETRY_RULE: + return new RetryRule().choose(iLoadBalancer, key); + case ZONE_AVOIDANCE_RULE: + ZoneAvoidanceRule zoneAvoidanceRule = new ZoneAvoidanceRule(); + zoneAvoidanceRule.setLoadBalancer(iLoadBalancer); + return zoneAvoidanceRule.choose(key); + case AVAILABILITY_FILTERING_RULE: + AvailabilityFilteringRule availabilityFilteringRule = new AvailabilityFilteringRule(); + availabilityFilteringRule.setLoadBalancer(iLoadBalancer); + return availabilityFilteringRule.choose(key); + default: + throw new UnsupportedOperationException( + "unsupported string compare operation"); + } + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/util/RequestContext.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/util/RequestContext.java new file mode 100644 index 000000000..6ef3d15c8 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/util/RequestContext.java @@ -0,0 +1,48 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.router.util; + +import javax.servlet.http.HttpServletRequest; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author HH + */ +public final class RequestContext { + + private static final Logger LOG = LoggerFactory.getLogger(RequestContext.class); + + private static final ThreadLocal REQUEST_THREAD_HOLDER = new ThreadLocal<>(); + + private RequestContext() { + } + + public static HttpServletRequest getRequest() { + return REQUEST_THREAD_HOLDER.get(); + } + + public static void setRequest(HttpServletRequest request) { + REQUEST_THREAD_HOLDER.set(request); + } + + public static void removeRequest() { + REQUEST_THREAD_HOLDER.remove(); + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/web/WebMvcAutoConfiguration.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/web/WebMvcAutoConfiguration.java new file mode 100644 index 000000000..37d03b050 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/web/WebMvcAutoConfiguration.java @@ -0,0 +1,41 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.router.web; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author HH + */ +@Configuration(proxyBeanMethods = false) +public class WebMvcAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + public WebMvcInterceptor webMvcInterceptor() { + return new WebMvcInterceptor(); + } + + @Bean + @ConditionalOnMissingBean + public WebMvcConfig webMvcConfig() { + return new WebMvcConfig(); + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/web/WebMvcConfig.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/web/WebMvcConfig.java new file mode 100644 index 000000000..9a4a4e481 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/web/WebMvcConfig.java @@ -0,0 +1,36 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.router.web; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +/** + * @author HH + */ +public class WebMvcConfig implements WebMvcConfigurer { + + @Autowired + private WebMvcInterceptor webMvcInterceptor; + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(webMvcInterceptor); + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/web/WebMvcInterceptor.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/web/WebMvcInterceptor.java new file mode 100644 index 000000000..154415cf9 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/router/web/WebMvcInterceptor.java @@ -0,0 +1,45 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * 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 + * + * https://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.alibaba.cloud.router.web; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.alibaba.cloud.router.util.RequestContext; + +import org.springframework.lang.Nullable; +import org.springframework.web.servlet.HandlerInterceptor; + +/** + * @author HH + */ +public class WebMvcInterceptor implements HandlerInterceptor { + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, + Object handler) { + RequestContext.setRequest(request); + return true; + } + + @Override + public void afterCompletion(HttpServletRequest request, HttpServletResponse response, + Object handler, @Nullable Exception ex) { + RequestContext.removeRequest(); + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/resources/META-INF/spring.factories b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/resources/META-INF/spring.factories new file mode 100644 index 000000000..8c8247703 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/resources/META-INF/spring.factories @@ -0,0 +1,6 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ + com.alibaba.cloud.router.LabelRoutingAutoConfiguration,\ + com.alibaba.cloud.router.feign.FeignInterceptorAutoConfiguration,\ + com.alibaba.cloud.router.ribbon.LabelRouteLBRuleAutoConfiguration,\ + com.alibaba.cloud.router.web.WebMvcAutoConfiguration,\ + com.alibaba.cloud.router.RouterPropertiesAutoConfiguration \ No newline at end of file