From 4783d45c082257cecb559364154c0f6cbdc4aaa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jare=C5=A1?= Date: Thu, 10 Aug 2023 16:22:48 +0200 Subject: [PATCH 01/27] draft MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pavel Jareš --- .github/workflows/containers.yml | 2 - .../config/RoutingConfig.java | 21 -- .../filters/HeaderRouteStepFilterFactory.java | 64 ++++++ .../service/ProxyRouteLocator.java | 62 ------ .../service/RouteLocator.java | 200 +++++++----------- .../service/routing/ByBasePath.java | 65 ++++++ .../service/routing/ByHeader.java | 68 ++++++ .../routing/RouteDefinitionProducer.java | 91 ++++++++ .../service/scheme/HttpBasicPassticket.java | 47 ++++ .../service/scheme/SchemeHandler.java | 35 +++ .../service/scheme/X509.java | 52 +++++ .../src/main/resources/application.yml | 3 - .../service/RouteLocatorTest.java | 78 +++---- .../src/test/resources/application-test.yml | 3 - .../src/test/resources/application.yml | 3 - .../constants/EurekaMetadataDefinition.java | 1 + .../apiml/gateway/config/GatewayConfig.java | 7 + 17 files changed, 550 insertions(+), 252 deletions(-) create mode 100644 cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/filters/HeaderRouteStepFilterFactory.java delete mode 100644 cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/ProxyRouteLocator.java create mode 100644 cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByBasePath.java create mode 100644 cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByHeader.java create mode 100644 cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/RouteDefinitionProducer.java create mode 100644 cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/scheme/HttpBasicPassticket.java create mode 100644 cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/scheme/SchemeHandler.java create mode 100644 cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/scheme/X509.java diff --git a/.github/workflows/containers.yml b/.github/workflows/containers.yml index 661b6870d3..dc88b7d0db 100644 --- a/.github/workflows/containers.yml +++ b/.github/workflows/containers.yml @@ -192,8 +192,6 @@ jobs: image: ghcr.io/balhar-jakub/gateway-service:${{ github.run_id }}-${{ github.run_number }} cloud-gateway-service: image: ghcr.io/balhar-jakub/cloud-gateway-service:${{ github.run_id }}-${{ github.run_number }} - env: - APIML_SERVICE_GATEWAY_PROXY_ENABLED: false discoverable-client: image: ghcr.io/balhar-jakub/discoverable-client:${{ github.run_id }}-${{ github.run_number }} mock-services: diff --git a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/config/RoutingConfig.java b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/config/RoutingConfig.java index 7807952719..3f0cc15db8 100644 --- a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/config/RoutingConfig.java +++ b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/config/RoutingConfig.java @@ -11,16 +11,9 @@ package org.zowe.apiml.cloudgatewayservice.config; import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient; -import org.springframework.cloud.gateway.discovery.DiscoveryLocatorProperties; import org.springframework.cloud.gateway.filter.FilterDefinition; -import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.zowe.apiml.cloudgatewayservice.service.ProxyRouteLocator; -import org.zowe.apiml.cloudgatewayservice.service.RouteLocator; -import org.zowe.apiml.util.CorsUtils; import java.util.ArrayList; import java.util.HashMap; @@ -33,20 +26,6 @@ public class RoutingConfig { @Value("${apiml.service.ignoredHeadersWhenCorsEnabled:-}") private String ignoredHeadersWhenCorsEnabled; - @Bean - @ConditionalOnProperty(name = "apiml.service.gateway.proxy.enabled", havingValue = "false") - public RouteLocator apimlDiscoveryRouteDefLocator( - ReactiveDiscoveryClient discoveryClient, DiscoveryLocatorProperties properties, List filters, ApplicationContext context, CorsUtils corsUtils) { - return new RouteLocator(discoveryClient, properties, filters, context, corsUtils); - } - - @Bean - @ConditionalOnProperty(name = "apiml.service.gateway.proxy.enabled", havingValue = "true") - public RouteLocator proxyRouteDefLocator( - ReactiveDiscoveryClient discoveryClient, DiscoveryLocatorProperties properties, List filters, ApplicationContext context, CorsUtils corsUtils) { - return new ProxyRouteLocator(discoveryClient, properties, filters, context, corsUtils); - } - @Bean public List filters() { FilterDefinition circuitBreakerFilter = new FilterDefinition(); diff --git a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/filters/HeaderRouteStepFilterFactory.java b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/filters/HeaderRouteStepFilterFactory.java new file mode 100644 index 0000000000..11ac153e5a --- /dev/null +++ b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/filters/HeaderRouteStepFilterFactory.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2022 Broadcom. All Rights Reserved. The term + * "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * + * This software and all information contained therein is + * confidential and proprietary and shall not be duplicated, + * used, disclosed, or disseminated in any way except as + * authorized by the applicable license agreement, without the + * express written permission of Broadcom. All authorized + * reproductions must be marked with this language. + * + * EXCEPT AS SET FORTH IN THE APPLICABLE LICENSE AGREEMENT, TO + * THE EXTENT PERMITTED BY APPLICABLE LAW, BROADCOM PROVIDES THIS + * SOFTWARE WITHOUT WARRANTY OF ANY KIND, INCLUDING WITHOUT + * LIMITATION, ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL BROADCOM + * BE LIABLE TO THE END USER OR ANY THIRD PARTY FOR ANY LOSS OR + * DAMAGE, DIRECT OR INDIRECT, FROM THE USE OF THIS SOFTWARE, + * INCLUDING WITHOUT LIMITATION, LOST PROFITS, BUSINESS + * INTERRUPTION, GOODWILL, OR LOST DATA, EVEN IF BROADCOM IS + * EXPRESSLY ADVISED OF SUCH LOSS OR DAMAGE. + */ +package org.zowe.apiml.cloudgatewayservice.filters; + +import lombok.Data; +import org.springframework.cloud.gateway.filter.GatewayFilter; +import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; +import org.springframework.stereotype.Service; + +@Service +public class HeaderRouteStepFilterFactory extends AbstractGatewayFilterFactory { + + public HeaderRouteStepFilterFactory() { + super(Config.class); + } + + @Override + public GatewayFilter apply(Config config) { + String header = config.getHeader(); + return (exchange, chain) -> { + if (exchange.getRequest().getHeaders().containsKey(header)) { + exchange.mutate().request(request -> exchange.getRequest().mutate().headers(headers -> { + String headerValue = headers.getFirst(header); + int index = headerValue.indexOf("/"); + if ((index >= 0) && (index + 1 < headerValue.length())) { + headers.set(header, headerValue.substring(index + 1)); + } else { + headers.remove(header); + } + })); + } + + return chain.filter(exchange); + }; + } + + @Data + public static class Config { + + private String header; + + } + +} diff --git a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/ProxyRouteLocator.java b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/ProxyRouteLocator.java deleted file mode 100644 index 1bd2d2c70e..0000000000 --- a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/ProxyRouteLocator.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v20.html - * - * SPDX-License-Identifier: EPL-2.0 - * - * Copyright Contributors to the Zowe Project. - */ - -package org.zowe.apiml.cloudgatewayservice.service; - -import org.springframework.cloud.client.ServiceInstance; -import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient; -import org.springframework.cloud.gateway.discovery.DiscoveryLocatorProperties; -import org.springframework.cloud.gateway.filter.FilterDefinition; -import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition; -import org.springframework.cloud.gateway.route.RouteDefinition; -import org.springframework.context.ApplicationContext; -import org.springframework.expression.Expression; -import org.zowe.apiml.product.routing.RoutedService; -import org.zowe.apiml.util.CorsUtils; - -import java.net.URI; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Locale; - -public class ProxyRouteLocator extends RouteLocator { - - public ProxyRouteLocator(ReactiveDiscoveryClient discoveryClient, DiscoveryLocatorProperties properties, List filters, ApplicationContext context, CorsUtils corsUtils) { - super(discoveryClient, properties, filters, context, corsUtils); - - } - - @Override - protected void setProperties(RouteDefinition routeDefinition, ServiceInstance instance, RoutedService service) { - PredicateDefinition predicate = new PredicateDefinition(); - - predicate.setName("Header"); - predicate.addArg("header", "X-Request-Id"); - predicate.addArg("regexp", (instance.getServiceId() + instance.getHost()).toLowerCase(Locale.ROOT)); - - routeDefinition.getPredicates().add(predicate); - - for (FilterDefinition filter : getFilters()) { - routeDefinition.getFilters().add(filter); - } - } - - @Override - protected RouteDefinition buildRouteDefinition(Expression urlExpr, ServiceInstance serviceInstance, String routeId) { - String serviceId = serviceInstance.getInstanceId(); - RouteDefinition routeDefinition = new RouteDefinition(); - routeDefinition.setId(getRouteIdPrefix() + serviceId + routeId); - String uri = String.format("%s://%s:%d", serviceInstance.getScheme(), serviceInstance.getHost(), serviceInstance.getPort()); - routeDefinition.setUri(URI.create(uri)); - // add instance metadata - routeDefinition.setMetadata(new LinkedHashMap<>(serviceInstance.getMetadata())); - return routeDefinition; - } -} diff --git a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocator.java b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocator.java index 94c40af26d..cecf470f60 100644 --- a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocator.java +++ b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocator.java @@ -1,162 +1,124 @@ /* - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v20.html + * Copyright (c) 2022 Broadcom. All Rights Reserved. The term + * "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. * - * SPDX-License-Identifier: EPL-2.0 + * This software and all information contained therein is + * confidential and proprietary and shall not be duplicated, + * used, disclosed, or disseminated in any way except as + * authorized by the applicable license agreement, without the + * express written permission of Broadcom. All authorized + * reproductions must be marked with this language. * - * Copyright Contributors to the Zowe Project. + * EXCEPT AS SET FORTH IN THE APPLICABLE LICENSE AGREEMENT, TO + * THE EXTENT PERMITTED BY APPLICABLE LAW, BROADCOM PROVIDES THIS + * SOFTWARE WITHOUT WARRANTY OF ANY KIND, INCLUDING WITHOUT + * LIMITATION, ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL BROADCOM + * BE LIABLE TO THE END USER OR ANY THIRD PARTY FOR ANY LOSS OR + * DAMAGE, DIRECT OR INDIRECT, FROM THE USE OF THIS SOFTWARE, + * INCLUDING WITHOUT LIMITATION, LOST PROFITS, BUSINESS + * INTERRUPTION, GOODWILL, OR LOST DATA, EVEN IF BROADCOM IS + * EXPRESSLY ADVISED OF SUCH LOSS OR DAMAGE. */ - package org.zowe.apiml.cloudgatewayservice.service; -import lombok.extern.slf4j.Slf4j; +import lombok.AccessLevel; +import lombok.Getter; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient; -import org.springframework.cloud.gateway.discovery.DiscoveryLocatorProperties; import org.springframework.cloud.gateway.filter.FilterDefinition; -import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition; import org.springframework.cloud.gateway.route.RouteDefinition; import org.springframework.cloud.gateway.route.RouteDefinitionLocator; import org.springframework.context.ApplicationContext; -import org.springframework.expression.Expression; -import org.springframework.expression.spel.standard.SpelExpressionParser; -import org.springframework.expression.spel.support.SimpleEvaluationContext; +import org.springframework.stereotype.Service; import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource; import org.zowe.apiml.auth.Authentication; -import org.zowe.apiml.auth.AuthenticationScheme; +import org.zowe.apiml.cloudgatewayservice.service.routing.RouteDefinitionProducer; +import org.zowe.apiml.cloudgatewayservice.service.scheme.SchemeHandler; import org.zowe.apiml.eurekaservice.client.util.EurekaMetadataParser; -import org.zowe.apiml.product.routing.RoutedService; import org.zowe.apiml.util.CorsUtils; import reactor.core.publisher.Flux; -import java.net.URI; -import java.util.*; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; -@Slf4j +@Service public class RouteLocator implements RouteDefinitionLocator { - private final DiscoveryLocatorProperties properties; - - private final String routeIdPrefix; + private final ApplicationContext context; - private final SimpleEvaluationContext evalCtxt; + private final CorsUtils corsUtils; + private final ReactiveDiscoveryClient discoveryClient; - private Flux> serviceInstances; - private List filters; - private ApplicationContext context; + private final List commonFilters; + private final List routeDefinitionProducers; + private final Map schemeHandlers = new HashMap<>(); - private UrlBasedCorsConfigurationSource corsConfigurationSource; - private CorsUtils corsUtils; - private EurekaMetadataParser metadataParser = new EurekaMetadataParser(); + @Getter(lazy=true, value = AccessLevel.PRIVATE) + private final UrlBasedCorsConfigurationSource corsConfigurationSource = context.getBean(UrlBasedCorsConfigurationSource.class); - public RouteLocator(ReactiveDiscoveryClient discoveryClient, - DiscoveryLocatorProperties properties, List filters, ApplicationContext context, CorsUtils corsUtils) { - this(properties); - this.filters = filters; - serviceInstances = discoveryClient.getServices() - .flatMap(service -> discoveryClient.getInstances(service).collectList()); + public RouteLocator( + ApplicationContext context, + CorsUtils corsUtils, + ReactiveDiscoveryClient discoveryClient, + List commonFilters, + List schemeHandlersList, + List routeDefinitionProducers + ) { this.context = context; this.corsUtils = corsUtils; - } - + this.discoveryClient = discoveryClient; + this.commonFilters = commonFilters; + this.routeDefinitionProducers = routeDefinitionProducers; - public UrlBasedCorsConfigurationSource getConfigSource() { - if (corsConfigurationSource != null) { - return corsConfigurationSource; + for (SchemeHandler schemeHandler : schemeHandlersList) { + schemeHandlers.put(schemeHandler.getAuthenticationScheme().getScheme(), schemeHandler); } - corsConfigurationSource = context.getBean(UrlBasedCorsConfigurationSource.class); - return corsConfigurationSource; } - private RouteLocator(DiscoveryLocatorProperties properties) { - this.properties = properties; - routeIdPrefix = this.getClass().getSimpleName() + "_"; - evalCtxt = SimpleEvaluationContext.forReadOnlyDataBinding().withInstanceMethods().build(); + private Flux> getServiceInstances() { + return discoveryClient.getServices() + .flatMap(service -> discoveryClient.getInstances(service) + .collectList()); } - public List getFilters() { - return filters; - } - - @Override - public Flux getRouteDefinitions() { - - SpelExpressionParser parser = new SpelExpressionParser(); - Expression urlExpr = parser.parseExpression(properties.getUrlExpression()); - - return serviceInstances.filter(instances -> !instances.isEmpty()).flatMap(Flux::fromIterable) - .collectMap(ServiceInstance::getInstanceId) - // remove duplicates - .flatMapMany(map -> Flux.fromIterable(map.values())).map(instance -> { - - List routedServices = metadataParser.parseToListRoute(instance.getMetadata()); - - - List definitionsForInstance = new ArrayList<>(); - for (RoutedService service : routedServices) { - RouteDefinition routeDefinition = buildRouteDefinition(urlExpr, instance, service.getSubServiceId()); - - setProperties(routeDefinition, instance, service); - - definitionsForInstance.add(routeDefinition); - } - corsUtils.setCorsConfiguration(instance.getServiceId().toLowerCase(), instance.getMetadata(), (prefix, serviceId, config) -> getConfigSource().registerCorsConfiguration("/" + serviceId + "/**", config)); - return definitionsForInstance; - }).flatMapIterable(list -> list); - } - - protected void setProperties(RouteDefinition routeDefinition, ServiceInstance instance, RoutedService service) { - PredicateDefinition predicate = new PredicateDefinition(); - predicate.setName("Path"); - String predicateValue = "/" + instance.getServiceId().toLowerCase() + "/" + service.getGatewayUrl() + "/**"; - predicate.addArg("pattern", predicateValue); - routeDefinition.getPredicates().add(predicate); - - FilterDefinition filter = new FilterDefinition(); - filter.setName("RewritePath"); - - filter.addArg("regexp", predicateValue.replace("/**", "/?(?.*)")); - filter.addArg("replacement", service.getServiceUrl() + "/${remaining}"); - - routeDefinition.getFilters().add(filter); - Authentication auth = metadataParser.parseAuthentication(instance.getMetadata()); - + protected void setAuth(RouteDefinition routeDefinition, Authentication auth) { if (auth != null && auth.getScheme() != null) { - String schemeName = auth.getScheme().getScheme(); - if (AuthenticationScheme.HTTP_BASIC_PASSTICKET.getScheme().equals(schemeName)) { - FilterDefinition filerDef = new FilterDefinition(); - filerDef.setName("PassticketFilterFactory"); - filerDef.addArg("applicationName", auth.getApplid()); - routeDefinition.getFilters().add(filerDef); - } else if (AuthenticationScheme.X509.getScheme().equals(schemeName)) { - FilterDefinition x509filter = new FilterDefinition(); - x509filter.setName("X509FilterFactory"); - Map m = new HashMap<>(); - m.put("headers",auth.getHeaders()); - x509filter.setArgs(m); - routeDefinition.getFilters().add(x509filter); + SchemeHandler schemeHandler = schemeHandlers.get(auth.getScheme()); + if (schemeHandler != null) { + schemeHandler.apply(routeDefinition, auth); } } - - for (FilterDefinition defaultFilter : getFilters()) { - routeDefinition.getFilters().add(defaultFilter); - } } - protected RouteDefinition buildRouteDefinition(Expression urlExpr, ServiceInstance serviceInstance, String routeId) { - String serviceId = serviceInstance.getServiceId(); - RouteDefinition routeDefinition = new RouteDefinition(); - routeDefinition.setId(this.routeIdPrefix + serviceId + routeId); - String uri = urlExpr.getValue(this.evalCtxt, serviceInstance, String.class); - routeDefinition.setUri(URI.create(uri)); - // add instance metadata - routeDefinition.setMetadata(new LinkedHashMap<>(serviceInstance.getMetadata())); - return routeDefinition; + private void setCors(ServiceInstance serviceInstance) { + corsUtils.setCorsConfiguration( + serviceInstance.getServiceId().toLowerCase(), + serviceInstance.getMetadata(), + (prefix, serviceId, config) -> getCorsConfigurationSource().registerCorsConfiguration("/" + serviceId + "/**", config)); } - public String getRouteIdPrefix() { - return routeIdPrefix; + @Override + public Flux getRouteDefinitions() { + EurekaMetadataParser metadataParser = new EurekaMetadataParser(); + return getServiceInstances().flatMap(Flux::fromIterable).map(serviceInstance -> { + Authentication auth = metadataParser.parseAuthentication(serviceInstance.getMetadata()); + setCors(serviceInstance); + return metadataParser.parseToListRoute(serviceInstance.getMetadata()).stream().map(routedService -> + routeDefinitionProducers.stream() + .map(rdp -> { + RouteDefinition routeDefinition = rdp.get(serviceInstance, routedService); + routeDefinition.getFilters().addAll(commonFilters); + setAuth(routeDefinition, auth); + + return routeDefinition; + }).collect(Collectors.toList()) + ).collect(Collectors.toList()); + }) + .flatMapIterable(list -> list) + .flatMapIterable(list -> list); } } diff --git a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByBasePath.java b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByBasePath.java new file mode 100644 index 0000000000..d4e62b3b58 --- /dev/null +++ b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByBasePath.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2022 Broadcom. All Rights Reserved. The term + * "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * + * This software and all information contained therein is + * confidential and proprietary and shall not be duplicated, + * used, disclosed, or disseminated in any way except as + * authorized by the applicable license agreement, without the + * express written permission of Broadcom. All authorized + * reproductions must be marked with this language. + * + * EXCEPT AS SET FORTH IN THE APPLICABLE LICENSE AGREEMENT, TO + * THE EXTENT PERMITTED BY APPLICABLE LAW, BROADCOM PROVIDES THIS + * SOFTWARE WITHOUT WARRANTY OF ANY KIND, INCLUDING WITHOUT + * LIMITATION, ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL BROADCOM + * BE LIABLE TO THE END USER OR ANY THIRD PARTY FOR ANY LOSS OR + * DAMAGE, DIRECT OR INDIRECT, FROM THE USE OF THIS SOFTWARE, + * INCLUDING WITHOUT LIMITATION, LOST PROFITS, BUSINESS + * INTERRUPTION, GOODWILL, OR LOST DATA, EVEN IF BROADCOM IS + * EXPRESSLY ADVISED OF SUCH LOSS OR DAMAGE. + */ +package org.zowe.apiml.cloudgatewayservice.service.routing; + +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.gateway.discovery.DiscoveryLocatorProperties; +import org.springframework.cloud.gateway.filter.FilterDefinition; +import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition; +import org.springframework.cloud.gateway.route.RouteDefinition; +import org.springframework.stereotype.Component; +import org.zowe.apiml.product.routing.RoutedService; + +@Component +public class ByBasePath extends RouteDefinitionProducer { + + public ByBasePath(DiscoveryLocatorProperties properties) { + super(properties); + } + + @Override + public void setCondition(RouteDefinition routeDefinition, ServiceInstance serviceInstance, RoutedService routedService) { + PredicateDefinition predicate = new PredicateDefinition(); + predicate.setName("Path"); + String predicateValue = "/" + serviceInstance.getServiceId().toLowerCase() + "/" + routedService.getGatewayUrl() + "/**"; + predicate.addArg("pattern", predicateValue); + routeDefinition.getPredicates().add(predicate); + } + + @Override + public void setFilters(RouteDefinition routeDefinition, ServiceInstance serviceInstance, RoutedService routedService) { + FilterDefinition filter = new FilterDefinition(); + filter.setName("RewritePath"); + + filter.addArg("regexp", "/" + serviceInstance.getServiceId().toLowerCase() + "/" + routedService.getGatewayUrl() + "/?(?.*)"); + filter.addArg("replacement", routedService.getServiceUrl() + "/${remaining}"); + + routeDefinition.getFilters().add(filter); + } + + @Override + public int getOrder() { + return 1; + } + +} diff --git a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByHeader.java b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByHeader.java new file mode 100644 index 0000000000..0e0d80cdac --- /dev/null +++ b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByHeader.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2022 Broadcom. All Rights Reserved. The term + * "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * + * This software and all information contained therein is + * confidential and proprietary and shall not be duplicated, + * used, disclosed, or disseminated in any way except as + * authorized by the applicable license agreement, without the + * express written permission of Broadcom. All authorized + * reproductions must be marked with this language. + * + * EXCEPT AS SET FORTH IN THE APPLICABLE LICENSE AGREEMENT, TO + * THE EXTENT PERMITTED BY APPLICABLE LAW, BROADCOM PROVIDES THIS + * SOFTWARE WITHOUT WARRANTY OF ANY KIND, INCLUDING WITHOUT + * LIMITATION, ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL BROADCOM + * BE LIABLE TO THE END USER OR ANY THIRD PARTY FOR ANY LOSS OR + * DAMAGE, DIRECT OR INDIRECT, FROM THE USE OF THIS SOFTWARE, + * INCLUDING WITHOUT LIMITATION, LOST PROFITS, BUSINESS + * INTERRUPTION, GOODWILL, OR LOST DATA, EVEN IF BROADCOM IS + * EXPRESSLY ADVISED OF SUCH LOSS OR DAMAGE. + */ +package org.zowe.apiml.cloudgatewayservice.service.routing; + +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.gateway.discovery.DiscoveryLocatorProperties; +import org.springframework.cloud.gateway.filter.FilterDefinition; +import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition; +import org.springframework.cloud.gateway.route.RouteDefinition; +import org.springframework.stereotype.Component; +import org.zowe.apiml.product.routing.RoutedService; + +import java.util.Locale; + +@Component +public class ByHeader extends RouteDefinitionProducer { + + private static final String TARGET_HEADER_NAME = "X-Request-Id"; + + public ByHeader(DiscoveryLocatorProperties properties) { + super(properties); + } + + @Override + public void setCondition(RouteDefinition routeDefinition, ServiceInstance serviceInstance, RoutedService routedService) { + PredicateDefinition predicate = new PredicateDefinition(); + + predicate.setName("Header"); + predicate.addArg("header", TARGET_HEADER_NAME); + predicate.addArg("regexp", serviceInstance.getServiceId().toLowerCase(Locale.ROOT) + "(/.*)?"); + + routeDefinition.getPredicates().add(predicate); + } + + @Override + public void setFilters(RouteDefinition routeDefinition, ServiceInstance serviceInstance, RoutedService routedService) { + FilterDefinition filter = new FilterDefinition(); + filter.setName("HeaderRouteStepFilterFactory"); + filter.addArg("header", TARGET_HEADER_NAME); + routeDefinition.getFilters().add(filter); + } + + @Override + public int getOrder() { + return 0; + } + +} diff --git a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/RouteDefinitionProducer.java b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/RouteDefinitionProducer.java new file mode 100644 index 0000000000..e517dd7a2f --- /dev/null +++ b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/RouteDefinitionProducer.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2022 Broadcom. All Rights Reserved. The term + * "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * + * This software and all information contained therein is + * confidential and proprietary and shall not be duplicated, + * used, disclosed, or disseminated in any way except as + * authorized by the applicable license agreement, without the + * express written permission of Broadcom. All authorized + * reproductions must be marked with this language. + * + * EXCEPT AS SET FORTH IN THE APPLICABLE LICENSE AGREEMENT, TO + * THE EXTENT PERMITTED BY APPLICABLE LAW, BROADCOM PROVIDES THIS + * SOFTWARE WITHOUT WARRANTY OF ANY KIND, INCLUDING WITHOUT + * LIMITATION, ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL BROADCOM + * BE LIABLE TO THE END USER OR ANY THIRD PARTY FOR ANY LOSS OR + * DAMAGE, DIRECT OR INDIRECT, FROM THE USE OF THIS SOFTWARE, + * INCLUDING WITHOUT LIMITATION, LOST PROFITS, BUSINESS + * INTERRUPTION, GOODWILL, OR LOST DATA, EVEN IF BROADCOM IS + * EXPRESSLY ADVISED OF SUCH LOSS OR DAMAGE. + */ +package org.zowe.apiml.cloudgatewayservice.service.routing; + +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.gateway.discovery.DiscoveryLocatorProperties; +import org.springframework.cloud.gateway.route.RouteDefinition; +import org.springframework.expression.Expression; +import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.expression.spel.support.SimpleEvaluationContext; +import org.zowe.apiml.product.routing.RoutedService; + +import java.net.URI; +import java.util.LinkedHashMap; +import java.util.Map; + +import static org.zowe.apiml.constants.EurekaMetadataDefinition.SERVICE_EXTERNAL_URL; + +public abstract class RouteDefinitionProducer { + + protected final SimpleEvaluationContext evalCtxt = SimpleEvaluationContext.forReadOnlyDataBinding().withInstanceMethods().build(); + protected final Expression urlExpr; + + public RouteDefinitionProducer(DiscoveryLocatorProperties properties) { + SpelExpressionParser parser = new SpelExpressionParser(); + urlExpr = parser.parseExpression(properties.getUrlExpression()); + } + + protected String evalHostname(ServiceInstance serviceInstance) { + return urlExpr.getValue(this.evalCtxt, serviceInstance, String.class); + } + + protected String getHostname(ServiceInstance serviceInstance) { + String output = null; + Map metadata = serviceInstance.getMetadata(); + if (metadata != null) { + output = metadata.get(SERVICE_EXTERNAL_URL); + } + if (output == null) { + output = evalHostname(serviceInstance); + } + return output; + } + + protected RouteDefinition buildRouteDefinition(ServiceInstance serviceInstance, String routeId) { + RouteDefinition routeDefinition = new RouteDefinition(); + routeDefinition.setId(serviceInstance.getInstanceId() + ":" + routeId); + routeDefinition.setOrder(getOrder()); + routeDefinition.setUri(URI.create(getHostname(serviceInstance))); + + // add instance metadata + routeDefinition.setMetadata(new LinkedHashMap<>(serviceInstance.getMetadata())); + return routeDefinition; + } + + protected abstract int getOrder(); + + protected abstract void setCondition(RouteDefinition routeDefinition, ServiceInstance serviceInstance, RoutedService routedService); + + protected abstract void setFilters(RouteDefinition routeDefinition, ServiceInstance serviceInstance, RoutedService routedService); + + public RouteDefinition get(ServiceInstance serviceInstance, RoutedService routedService) { + RouteDefinition routeDefinition = buildRouteDefinition(serviceInstance, routedService.getSubServiceId()); + + setCondition(routeDefinition, serviceInstance, routedService); + setFilters(routeDefinition, serviceInstance, routedService); + + return routeDefinition; + } + +} diff --git a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/scheme/HttpBasicPassticket.java b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/scheme/HttpBasicPassticket.java new file mode 100644 index 0000000000..9b2c10a109 --- /dev/null +++ b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/scheme/HttpBasicPassticket.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2022 Broadcom. All Rights Reserved. The term + * "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * + * This software and all information contained therein is + * confidential and proprietary and shall not be duplicated, + * used, disclosed, or disseminated in any way except as + * authorized by the applicable license agreement, without the + * express written permission of Broadcom. All authorized + * reproductions must be marked with this language. + * + * EXCEPT AS SET FORTH IN THE APPLICABLE LICENSE AGREEMENT, TO + * THE EXTENT PERMITTED BY APPLICABLE LAW, BROADCOM PROVIDES THIS + * SOFTWARE WITHOUT WARRANTY OF ANY KIND, INCLUDING WITHOUT + * LIMITATION, ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL BROADCOM + * BE LIABLE TO THE END USER OR ANY THIRD PARTY FOR ANY LOSS OR + * DAMAGE, DIRECT OR INDIRECT, FROM THE USE OF THIS SOFTWARE, + * INCLUDING WITHOUT LIMITATION, LOST PROFITS, BUSINESS + * INTERRUPTION, GOODWILL, OR LOST DATA, EVEN IF BROADCOM IS + * EXPRESSLY ADVISED OF SUCH LOSS OR DAMAGE. + */ +package org.zowe.apiml.cloudgatewayservice.service.scheme; + +import org.springframework.cloud.gateway.filter.FilterDefinition; +import org.springframework.cloud.gateway.route.RouteDefinition; +import org.springframework.stereotype.Component; +import org.zowe.apiml.auth.Authentication; +import org.zowe.apiml.auth.AuthenticationScheme; + +@Component +public class HttpBasicPassticket implements SchemeHandler { + + @Override + public AuthenticationScheme getAuthenticationScheme() { + return AuthenticationScheme.HTTP_BASIC_PASSTICKET; + } + + @Override + public void apply(RouteDefinition routeDefinition, Authentication auth) { + FilterDefinition filerDef = new FilterDefinition(); + filerDef.setName("PassticketFilterFactory"); + filerDef.addArg("applicationName", auth.getApplid()); + routeDefinition.getFilters().add(filerDef); + } + +} diff --git a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/scheme/SchemeHandler.java b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/scheme/SchemeHandler.java new file mode 100644 index 0000000000..9652346d0b --- /dev/null +++ b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/scheme/SchemeHandler.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2022 Broadcom. All Rights Reserved. The term + * "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * + * This software and all information contained therein is + * confidential and proprietary and shall not be duplicated, + * used, disclosed, or disseminated in any way except as + * authorized by the applicable license agreement, without the + * express written permission of Broadcom. All authorized + * reproductions must be marked with this language. + * + * EXCEPT AS SET FORTH IN THE APPLICABLE LICENSE AGREEMENT, TO + * THE EXTENT PERMITTED BY APPLICABLE LAW, BROADCOM PROVIDES THIS + * SOFTWARE WITHOUT WARRANTY OF ANY KIND, INCLUDING WITHOUT + * LIMITATION, ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL BROADCOM + * BE LIABLE TO THE END USER OR ANY THIRD PARTY FOR ANY LOSS OR + * DAMAGE, DIRECT OR INDIRECT, FROM THE USE OF THIS SOFTWARE, + * INCLUDING WITHOUT LIMITATION, LOST PROFITS, BUSINESS + * INTERRUPTION, GOODWILL, OR LOST DATA, EVEN IF BROADCOM IS + * EXPRESSLY ADVISED OF SUCH LOSS OR DAMAGE. + */ +package org.zowe.apiml.cloudgatewayservice.service.scheme; + +import org.springframework.cloud.gateway.route.RouteDefinition; +import org.zowe.apiml.auth.Authentication; +import org.zowe.apiml.auth.AuthenticationScheme; + +public interface SchemeHandler { + + AuthenticationScheme getAuthenticationScheme(); + + void apply(RouteDefinition routeDefinition, Authentication auth); + +} diff --git a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/scheme/X509.java b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/scheme/X509.java new file mode 100644 index 0000000000..d01f356471 --- /dev/null +++ b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/scheme/X509.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2022 Broadcom. All Rights Reserved. The term + * "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * + * This software and all information contained therein is + * confidential and proprietary and shall not be duplicated, + * used, disclosed, or disseminated in any way except as + * authorized by the applicable license agreement, without the + * express written permission of Broadcom. All authorized + * reproductions must be marked with this language. + * + * EXCEPT AS SET FORTH IN THE APPLICABLE LICENSE AGREEMENT, TO + * THE EXTENT PERMITTED BY APPLICABLE LAW, BROADCOM PROVIDES THIS + * SOFTWARE WITHOUT WARRANTY OF ANY KIND, INCLUDING WITHOUT + * LIMITATION, ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL BROADCOM + * BE LIABLE TO THE END USER OR ANY THIRD PARTY FOR ANY LOSS OR + * DAMAGE, DIRECT OR INDIRECT, FROM THE USE OF THIS SOFTWARE, + * INCLUDING WITHOUT LIMITATION, LOST PROFITS, BUSINESS + * INTERRUPTION, GOODWILL, OR LOST DATA, EVEN IF BROADCOM IS + * EXPRESSLY ADVISED OF SUCH LOSS OR DAMAGE. + */ +package org.zowe.apiml.cloudgatewayservice.service.scheme; + +import org.springframework.cloud.gateway.filter.FilterDefinition; +import org.springframework.cloud.gateway.route.RouteDefinition; +import org.springframework.stereotype.Component; +import org.zowe.apiml.auth.Authentication; +import org.zowe.apiml.auth.AuthenticationScheme; + +import java.util.HashMap; +import java.util.Map; + +@Component +public class X509 implements SchemeHandler { + + @Override + public AuthenticationScheme getAuthenticationScheme() { + return AuthenticationScheme.X509; + } + + @Override + public void apply(RouteDefinition routeDefinition, Authentication auth) { + FilterDefinition x509filter = new FilterDefinition(); + x509filter.setName("X509FilterFactory"); + Map m = new HashMap<>(); + m.put("headers",auth.getHeaders()); + x509filter.setArgs(m); + routeDefinition.getFilters().add(x509filter); + } + +} diff --git a/cloud-gateway-service/src/main/resources/application.yml b/cloud-gateway-service/src/main/resources/application.yml index 6ccaff375c..bb325edcc5 100644 --- a/cloud-gateway-service/src/main/resources/application.yml +++ b/cloud-gateway-service/src/main/resources/application.yml @@ -12,9 +12,6 @@ apiml: hostname: localhost corsEnabled: true ignoredHeadersWhenCorsEnabled: Access-Control-Request-Method,Access-Control-Request-Headers,Access-Control-Allow-Origin,Access-Control-Allow-Methods,Access-Control-Allow-Headers,Access-Control-Allow-Credentials,Origin - gateway: - proxy: - enabled: true security: ssl: nonStrictVerifySslCertificatesOfServices: true diff --git a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocatorTest.java b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocatorTest.java index 24783e0516..d11aca381e 100644 --- a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocatorTest.java +++ b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocatorTest.java @@ -43,45 +43,45 @@ class RouteLocatorTest { ReactiveDiscoveryClient dc = mock(ReactiveDiscoveryClient.class); DiscoveryLocatorProperties properties = new DiscoveryLocatorProperties(); - @Nested - class GivenRouteLocator { - @Test - void givenServiceWithDefinedMetadata_thenLocateRoutes() { - Flux services = Flux.fromIterable(Collections.singleton("gateway")); - Flux serviceInstances = Flux.fromIterable(Collections.singleton(instance)); - when(dc.getServices()).thenReturn(services); - when(dc.getInstances("gateway")).thenReturn(serviceInstances); - CorsUtils corsUtils = new CorsUtils(false); - RouteLocator locator = new RouteLocator(dc, properties, Collections.singletonList(new FilterDefinition("name=value")), null, corsUtils); - Flux definitionFlux = locator.getRouteDefinitions(); - List definitions = definitionFlux.collectList().block(); - assertNotNull(definitions); - assertEquals(1, definitions.size()); - } - } - - @Nested - class GivenProxyRouteLocator { - @Test - void whenServiceIsMatched_thenCreateRouteWithCorrectPredicate() { - Flux services = Flux.fromIterable(Collections.singleton("gateway")); - List instances = Arrays.asList(instance, instance2); - Flux serviceInstances = Flux.fromIterable(instances); - when(dc.getServices()).thenReturn(services); - when(dc.getInstances("gateway")).thenReturn(serviceInstances); - CorsUtils corsUtils = new CorsUtils(false); - ProxyRouteLocator locator = new ProxyRouteLocator(dc, properties, Collections.emptyList(), null, corsUtils); - Flux definitionFlux = locator.getRouteDefinitions(); - List definitions = definitionFlux.collectList().block(); - assertNotNull(definitions); - assertEquals(2, definitions.size()); - for (int i = 0; i < definitions.size(); i++) { - RouteDefinition def = definitions.get(i); - String expression = def.getPredicates().get(0).getArgs().get("regexp"); - assertTrue(Pattern.matches(expression, instances.get(i).getServiceId() + instances.get(i).getHost())); - } - } - } +// @Nested +// class GivenRouteLocator { +// @Test +// void givenServiceWithDefinedMetadata_thenLocateRoutes() { +// Flux services = Flux.fromIterable(Collections.singleton("gateway")); +// Flux serviceInstances = Flux.fromIterable(Collections.singleton(instance)); +// when(dc.getServices()).thenReturn(services); +// when(dc.getInstances("gateway")).thenReturn(serviceInstances); +// CorsUtils corsUtils = new CorsUtils(false); +// RouteLocator locator = new RouteLocator(dc, properties, Collections.singletonList(new FilterDefinition("name=value")), null, corsUtils); +// Flux definitionFlux = locator.getRouteDefinitions(); +// List definitions = definitionFlux.collectList().block(); +// assertNotNull(definitions); +// assertEquals(1, definitions.size()); +// } +// } +// +// @Nested +// class GivenProxyRouteLocator { +// @Test +// void whenServiceIsMatched_thenCreateRouteWithCorrectPredicate() { +// Flux services = Flux.fromIterable(Collections.singleton("gateway")); +// List instances = Arrays.asList(instance, instance2); +// Flux serviceInstances = Flux.fromIterable(instances); +// when(dc.getServices()).thenReturn(services); +// when(dc.getInstances("gateway")).thenReturn(serviceInstances); +// CorsUtils corsUtils = new CorsUtils(false); +// ProxyRouteLocator locator = new ProxyRouteLocator(dc, properties, Collections.emptyList(), null, corsUtils); +// Flux definitionFlux = locator.getRouteDefinitions(); +// List definitions = definitionFlux.collectList().block(); +// assertNotNull(definitions); +// assertEquals(2, definitions.size()); +// for (int i = 0; i < definitions.size(); i++) { +// RouteDefinition def = definitions.get(i); +// String expression = def.getPredicates().get(0).getArgs().get("regexp"); +// assertTrue(Pattern.matches(expression, instances.get(i).getServiceId() + instances.get(i).getHost())); +// } +// } +// } } diff --git a/cloud-gateway-service/src/test/resources/application-test.yml b/cloud-gateway-service/src/test/resources/application-test.yml index 28f0e10511..dc6ddcbb68 100644 --- a/cloud-gateway-service/src/test/resources/application-test.yml +++ b/cloud-gateway-service/src/test/resources/application-test.yml @@ -9,9 +9,6 @@ apiml: id: cloud-gateway port: 10023 hostname: localhost - gateway: - proxy: - enabled: true server: port: ${apiml.service.port} ssl: diff --git a/cloud-gateway-service/src/test/resources/application.yml b/cloud-gateway-service/src/test/resources/application.yml index 475259891d..5727fd2375 100644 --- a/cloud-gateway-service/src/test/resources/application.yml +++ b/cloud-gateway-service/src/test/resources/application.yml @@ -11,9 +11,6 @@ apiml: hostname: localhost corsEnabled: true ignoredHeadersWhenCorsEnabled: Access-Control-Request-Method,Access-Control-Request-Headers,Access-Control-Allow-Origin,Access-Control-Allow-Methods,Access-Control-Allow-Headers,Access-Control-Allow-Credentials,Origin - gateway: - proxy: - enabled: true server: port: ${apiml.service.port} ssl: diff --git a/common-service-core/src/main/java/org/zowe/apiml/constants/EurekaMetadataDefinition.java b/common-service-core/src/main/java/org/zowe/apiml/constants/EurekaMetadataDefinition.java index 7c9d58aa06..76f0e91baf 100644 --- a/common-service-core/src/main/java/org/zowe/apiml/constants/EurekaMetadataDefinition.java +++ b/common-service-core/src/main/java/org/zowe/apiml/constants/EurekaMetadataDefinition.java @@ -31,6 +31,7 @@ private EurekaMetadataDefinition() { public static final String SERVICE_TITLE = "apiml.service.title"; public static final String SERVICE_DESCRIPTION = "apiml.service.description"; + public static final String SERVICE_EXTERNAL_URL = "apiml.service.externalUrl"; public static final String API_INFO = "apiml.apiInfo"; public static final String API_INFO_API_ID = "apiId"; diff --git a/gateway-service/src/main/java/org/zowe/apiml/gateway/config/GatewayConfig.java b/gateway-service/src/main/java/org/zowe/apiml/gateway/config/GatewayConfig.java index 84f92810d4..2745535dfc 100644 --- a/gateway-service/src/main/java/org/zowe/apiml/gateway/config/GatewayConfig.java +++ b/gateway-service/src/main/java/org/zowe/apiml/gateway/config/GatewayConfig.java @@ -34,6 +34,8 @@ import java.util.Map; +import static org.zowe.apiml.constants.EurekaMetadataDefinition.SERVICE_EXTERNAL_URL; + @Configuration @RequiredArgsConstructor @EnableRetry @@ -97,9 +99,14 @@ public EurekaInstanceConfigBean eurekaInstanceConfigBean(InetUtils inetUtils, } + String externalUrl = getProperty("apiml.service.external-url"); if (StringUtils.hasText(hostname)) { instance.setHostname(hostname); + if (!StringUtils.hasText(externalUrl)) { + externalUrl = (isSecurePortEnabled ? "https" : "http") + "://" + hostname + ":" + serverPort; + } } + instance.getMetadataMap().put(SERVICE_EXTERNAL_URL, externalUrl); String statusPageUrlPath = getProperty("eureka.instance.status-page-url-path"); String healthCheckUrlPath = getProperty("eureka.instance.health-check-url-path"); From e44f966920aa582927fc6251ca5ca7c9b7686aa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jare=C5=A1?= Date: Fri, 11 Aug 2023 13:17:08 +0200 Subject: [PATCH 02/27] fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pavel Jareš --- .../service/RouteLocator.java | 36 +++++++++++++++-- .../service/routing/ByBasePath.java | 20 ++++++++-- .../service/routing/ByHeader.java | 4 +- .../routing/RouteDefinitionProducer.java | 39 ++++++++++++++++++- .../constants/EurekaMetadataDefinition.java | 1 + .../java/org/zowe/apiml/util/StringUtils.java | 23 ++++++----- .../org/zowe/apiml/util/StringUtilsTest.java | 32 ++++++++++----- config/local/gateway-service.yml | 1 + .../apiml/gateway/config/GatewayConfig.java | 18 ++++++--- .../src/main/resources/application.yml | 3 ++ 10 files changed, 141 insertions(+), 36 deletions(-) diff --git a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocator.java b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocator.java index cecf470f60..881875b8ba 100644 --- a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocator.java +++ b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocator.java @@ -36,14 +36,20 @@ import org.zowe.apiml.cloudgatewayservice.service.routing.RouteDefinitionProducer; import org.zowe.apiml.cloudgatewayservice.service.scheme.SchemeHandler; import org.zowe.apiml.eurekaservice.client.util.EurekaMetadataParser; +import org.zowe.apiml.product.routing.RoutedService; import org.zowe.apiml.util.CorsUtils; +import org.zowe.apiml.util.StringUtils; import reactor.core.publisher.Flux; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; +import static org.zowe.apiml.constants.EurekaMetadataDefinition.APIML_ID; + @Service public class RouteLocator implements RouteDefinitionLocator { @@ -97,19 +103,43 @@ private void setCors(ServiceInstance serviceInstance) { corsUtils.setCorsConfiguration( serviceInstance.getServiceId().toLowerCase(), serviceInstance.getMetadata(), - (prefix, serviceId, config) -> getCorsConfigurationSource().registerCorsConfiguration("/" + serviceId + "/**", config)); + (prefix, serviceId, config) -> { + serviceId = serviceInstance.getMetadata().getOrDefault(APIML_ID, serviceInstance.getServiceId()); + getCorsConfigurationSource().registerCorsConfiguration("/" + serviceId + "/**", config); + }); + } + + private String normalizeUrl(String in) { + in = in.replaceAll("\\*", ""); + in = StringUtils.removeFirstAndLastOccurrence(in, "/"); + return in; } @Override public Flux getRouteDefinitions() { EurekaMetadataParser metadataParser = new EurekaMetadataParser(); + AtomicInteger order = new AtomicInteger(); + /** + * It generates each rule for each combination of instance x routing x generator ({@link RouteDefinitionProducer}) + * + * The routes are sorted by serviceUrl to avoid clashing between multiple levels of paths, ie. /** vs /a, stars are + * removed in {@link #normalizeUrl(String)}. + * + * Sorting routes and generators by order allows to redefine order of each rule. There is no possible to have + * multiple valid rules for the same case at one moment. + */ return getServiceInstances().flatMap(Flux::fromIterable).map(serviceInstance -> { Authentication auth = metadataParser.parseAuthentication(serviceInstance.getMetadata()); setCors(serviceInstance); - return metadataParser.parseToListRoute(serviceInstance.getMetadata()).stream().map(routedService -> - routeDefinitionProducers.stream() + return metadataParser.parseToListRoute(serviceInstance.getMetadata()).stream() + // sorting avoid a conflict with the more general pattern + .sorted(Comparator.comparingInt(x -> normalizeUrl(x.getServiceUrl()).length()).reversed()) + .map(routedService -> + routeDefinitionProducers.stream() + .sorted(Comparator.comparingInt(x -> x.getOrder())) .map(rdp -> { RouteDefinition routeDefinition = rdp.get(serviceInstance, routedService); + routeDefinition.setOrder(order.getAndIncrement()); routeDefinition.getFilters().addAll(commonFilters); setAuth(routeDefinition, auth); diff --git a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByBasePath.java b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByBasePath.java index d4e62b3b58..c7403eb73a 100644 --- a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByBasePath.java +++ b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByBasePath.java @@ -29,6 +29,7 @@ import org.springframework.cloud.gateway.route.RouteDefinition; import org.springframework.stereotype.Component; import org.zowe.apiml.product.routing.RoutedService; +import org.zowe.apiml.util.StringUtils; @Component public class ByBasePath extends RouteDefinitionProducer { @@ -37,13 +38,26 @@ public ByBasePath(DiscoveryLocatorProperties properties) { super(properties); } + private String constructUrl(String...parts) { + StringBuilder sb = new StringBuilder(); + for (String part : parts) { + part = StringUtils.removeFirstAndLastOccurrence(part, "/"); + if (part.isEmpty()) continue; + + sb.append('/'); + sb.append(part); + } + return sb.toString(); + } + @Override public void setCondition(RouteDefinition routeDefinition, ServiceInstance serviceInstance, RoutedService routedService) { PredicateDefinition predicate = new PredicateDefinition(); predicate.setName("Path"); - String predicateValue = "/" + serviceInstance.getServiceId().toLowerCase() + "/" + routedService.getGatewayUrl() + "/**"; + String predicateValue = constructUrl(serviceInstance.getServiceId(), routedService.getGatewayUrl(), "**"); predicate.addArg("pattern", predicateValue); routeDefinition.getPredicates().add(predicate); + routeDefinition.setOrder(routeDefinition.getOrder() + routedService.getServiceUrl().length()); } @Override @@ -51,8 +65,8 @@ public void setFilters(RouteDefinition routeDefinition, ServiceInstance serviceI FilterDefinition filter = new FilterDefinition(); filter.setName("RewritePath"); - filter.addArg("regexp", "/" + serviceInstance.getServiceId().toLowerCase() + "/" + routedService.getGatewayUrl() + "/?(?.*)"); - filter.addArg("replacement", routedService.getServiceUrl() + "/${remaining}"); + filter.addArg("regexp", constructUrl(serviceInstance.getServiceId(), routedService.getGatewayUrl(), "?(?.*)")); + filter.addArg("replacement", constructUrl(routedService.getServiceUrl(), "${remaining}")); routeDefinition.getFilters().add(filter); } diff --git a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByHeader.java b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByHeader.java index 0e0d80cdac..564174fcf6 100644 --- a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByHeader.java +++ b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByHeader.java @@ -30,8 +30,6 @@ import org.springframework.stereotype.Component; import org.zowe.apiml.product.routing.RoutedService; -import java.util.Locale; - @Component public class ByHeader extends RouteDefinitionProducer { @@ -47,7 +45,7 @@ public void setCondition(RouteDefinition routeDefinition, ServiceInstance servic predicate.setName("Header"); predicate.addArg("header", TARGET_HEADER_NAME); - predicate.addArg("regexp", serviceInstance.getServiceId().toLowerCase(Locale.ROOT) + "(/.*)?"); + predicate.addArg("regexp", serviceInstance.getServiceId() + "(/.*)?"); routeDefinition.getPredicates().add(predicate); } diff --git a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/RouteDefinitionProducer.java b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/RouteDefinitionProducer.java index e517dd7a2f..96896c0af2 100644 --- a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/RouteDefinitionProducer.java +++ b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/RouteDefinitionProducer.java @@ -22,18 +22,22 @@ */ package org.zowe.apiml.cloudgatewayservice.service.routing; +import lombok.RequiredArgsConstructor; +import lombok.experimental.Delegate; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.gateway.discovery.DiscoveryLocatorProperties; import org.springframework.cloud.gateway.route.RouteDefinition; import org.springframework.expression.Expression; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.SimpleEvaluationContext; +import org.springframework.util.StringUtils; import org.zowe.apiml.product.routing.RoutedService; import java.net.URI; import java.util.LinkedHashMap; import java.util.Map; +import static org.zowe.apiml.constants.EurekaMetadataDefinition.APIML_ID; import static org.zowe.apiml.constants.EurekaMetadataDefinition.SERVICE_EXTERNAL_URL; public abstract class RouteDefinitionProducer { @@ -62,6 +66,20 @@ protected String getHostname(ServiceInstance serviceInstance) { return output; } + protected ServiceInstance getEvalServiceInstance(ServiceInstance serviceInstance) { + String serviceId = serviceInstance.getServiceId(); + + Map metadata = serviceInstance.getMetadata(); + if (metadata != null) { + String apimlId = metadata.get(APIML_ID); + if (StringUtils.hasText(apimlId)) { + serviceId = apimlId; + } + } + + return new ServiceInstanceEval(serviceInstance, serviceId.toLowerCase()); + } + protected RouteDefinition buildRouteDefinition(ServiceInstance serviceInstance, String routeId) { RouteDefinition routeDefinition = new RouteDefinition(); routeDefinition.setId(serviceInstance.getInstanceId() + ":" + routeId); @@ -73,13 +91,14 @@ protected RouteDefinition buildRouteDefinition(ServiceInstance serviceInstance, return routeDefinition; } - protected abstract int getOrder(); + public abstract int getOrder(); protected abstract void setCondition(RouteDefinition routeDefinition, ServiceInstance serviceInstance, RoutedService routedService); protected abstract void setFilters(RouteDefinition routeDefinition, ServiceInstance serviceInstance, RoutedService routedService); public RouteDefinition get(ServiceInstance serviceInstance, RoutedService routedService) { + serviceInstance = getEvalServiceInstance(serviceInstance); RouteDefinition routeDefinition = buildRouteDefinition(serviceInstance, routedService.getSubServiceId()); setCondition(routeDefinition, serviceInstance, routedService); @@ -88,4 +107,22 @@ public RouteDefinition get(ServiceInstance serviceInstance, RoutedService routed return routeDefinition; } + @RequiredArgsConstructor + static class ServiceInstanceEval implements ServiceInstance { + + @Delegate(excludes = Overridden.class) + private final ServiceInstance original; + private final String evalServiceId; + + @Override + public String getServiceId() { + return evalServiceId; + } + + private static interface Overridden { + String getServiceId(); + } + + } + } diff --git a/common-service-core/src/main/java/org/zowe/apiml/constants/EurekaMetadataDefinition.java b/common-service-core/src/main/java/org/zowe/apiml/constants/EurekaMetadataDefinition.java index 76f0e91baf..0456d290a5 100644 --- a/common-service-core/src/main/java/org/zowe/apiml/constants/EurekaMetadataDefinition.java +++ b/common-service-core/src/main/java/org/zowe/apiml/constants/EurekaMetadataDefinition.java @@ -32,6 +32,7 @@ private EurekaMetadataDefinition() { public static final String SERVICE_TITLE = "apiml.service.title"; public static final String SERVICE_DESCRIPTION = "apiml.service.description"; public static final String SERVICE_EXTERNAL_URL = "apiml.service.externalUrl"; + public static final String APIML_ID = "apiml.service.id"; public static final String API_INFO = "apiml.apiInfo"; public static final String API_INFO_API_ID = "apiId"; diff --git a/common-service-core/src/main/java/org/zowe/apiml/util/StringUtils.java b/common-service-core/src/main/java/org/zowe/apiml/util/StringUtils.java index 3a2e1d8497..9c0cf11f01 100644 --- a/common-service-core/src/main/java/org/zowe/apiml/util/StringUtils.java +++ b/common-service-core/src/main/java/org/zowe/apiml/util/StringUtils.java @@ -26,7 +26,7 @@ public class StringUtils { * Remove from parameter 'input' the first and the last occurrence of parameter 'str' * @param input * @param str - * @return the the input string without the initial and final occurrence of str + * @return the input string without the initial and final occurrence of str */ public static String removeFirstAndLastOccurrence(String input, String str) { if (input == null) { @@ -34,24 +34,23 @@ public static String removeFirstAndLastOccurrence(String input, String str) { } input = input.trim(); - if (input.isEmpty()) { - return ""; + + if (str == null) { + return input; } - int start = 0; - int stop = input.length(); + int startIndex = input.startsWith(str) ? str.length() : 0; + int endIndex = input.endsWith(str) ? str.length() : 0; - if (input.startsWith(str)) { - start = 1; + if ((startIndex == 0) && (endIndex == 0)) { + return input; } - if (input.endsWith(str)) { - stop = input.length() - 1; + if (startIndex + endIndex >= input.length()) { + return ""; } - input = input.substring(start, stop); - - return input; + return input.substring(startIndex, input.length() - endIndex); } /** diff --git a/common-service-core/src/test/java/org/zowe/apiml/util/StringUtilsTest.java b/common-service-core/src/test/java/org/zowe/apiml/util/StringUtilsTest.java index 65007cedef..7cf090a85e 100644 --- a/common-service-core/src/test/java/org/zowe/apiml/util/StringUtilsTest.java +++ b/common-service-core/src/test/java/org/zowe/apiml/util/StringUtilsTest.java @@ -11,6 +11,8 @@ package org.zowe.apiml.util; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import java.util.HashMap; import java.util.Map; @@ -21,15 +23,27 @@ class StringUtilsTest { - @Test - void removeFirstAndLastOccurrenceTest() { - assertNull(StringUtils.removeFirstAndLastOccurrence(null, "any-string")); - - String whiteSpace = " "; - assertEquals("", StringUtils.removeFirstAndLastOccurrence(whiteSpace, "any-string")); - - String hasSlashes = " /blah/ "; - assertEquals("blah", StringUtils.removeFirstAndLastOccurrence(hasSlashes, "/")); + @ParameterizedTest(name = "removeFirstAndLastOccurrence({0}, {1}) should return {2}") + @CsvSource(value = { + "' ',any-string,''", + "' /blah/ ',/,blah", + "' //x// ',/,/x/", + "/path/a/b,/,path/a/b", + "path/a/b/,/,path/a/b", + "/path/a/b/,/,path/a/b", + "/,/,''", + "abcdefabc,abc,def", + "'',/,''", + ",/,", + "anything,,anything", + "' anything ',,anything", + "x,x,''", + "xx,x,''", + "xxx,x,x", + "xxx,xx,''" + }) + void removeFirstAndLastOccurrenceTest(String input, String substring, String output) { + assertEquals(output, StringUtils.removeFirstAndLastOccurrence(input, substring)); } @Test diff --git a/config/local/gateway-service.yml b/config/local/gateway-service.yml index 00c5066e82..cf19bb4adc 100644 --- a/config/local/gateway-service.yml +++ b/config/local/gateway-service.yml @@ -1,6 +1,7 @@ spring.profiles.include: diag apiml: service: + apimlId: local1 hostname: localhost ipAddress: 127.0.0.1 port: 10010 diff --git a/gateway-service/src/main/java/org/zowe/apiml/gateway/config/GatewayConfig.java b/gateway-service/src/main/java/org/zowe/apiml/gateway/config/GatewayConfig.java index 2745535dfc..25b6e48949 100644 --- a/gateway-service/src/main/java/org/zowe/apiml/gateway/config/GatewayConfig.java +++ b/gateway-service/src/main/java/org/zowe/apiml/gateway/config/GatewayConfig.java @@ -34,6 +34,7 @@ import java.util.Map; +import static org.zowe.apiml.constants.EurekaMetadataDefinition.APIML_ID; import static org.zowe.apiml.constants.EurekaMetadataDefinition.SERVICE_EXTERNAL_URL; @Configuration @@ -98,15 +99,22 @@ public EurekaInstanceConfigBean eurekaInstanceConfigBean(InetUtils inetUtils, instance.setIpAddress(ipAddress); } - - String externalUrl = getProperty("apiml.service.external-url"); if (StringUtils.hasText(hostname)) { instance.setHostname(hostname); - if (!StringUtils.hasText(externalUrl)) { - externalUrl = (isSecurePortEnabled ? "https" : "http") + "://" + hostname + ":" + serverPort; - } + } + + String externalUrl = getProperty("apiml.service.external-url"); + if (!StringUtils.hasText(externalUrl)) { + externalUrl = (isSecurePortEnabled ? "https" : "http") + "://" + hostname + ":" + serverPort; } instance.getMetadataMap().put(SERVICE_EXTERNAL_URL, externalUrl); + + String apimlId = getProperty("apiml.service.apiml-id"); + if (!StringUtils.hasText(apimlId)) { + apimlId = hostname + "_" + serverPort; + } + instance.getMetadataMap().put(APIML_ID, apimlId); + String statusPageUrlPath = getProperty("eureka.instance.status-page-url-path"); String healthCheckUrlPath = getProperty("eureka.instance.health-check-url-path"); diff --git a/gateway-service/src/main/resources/application.yml b/gateway-service/src/main/resources/application.yml index 4b60506a47..c089c117ff 100644 --- a/gateway-service/src/main/resources/application.yml +++ b/gateway-service/src/main/resources/application.yml @@ -244,6 +244,9 @@ eureka: api_v1: gatewayUrl: /api/v1 serviceUrl: /gateway + zuul: + gatewayUrl: / + serviceUrl: / apiInfo: - apiId: zowe.apiml.gateway version: 1.0.0 From d0376df84752399b27f3406e44fc387017f77b62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jare=C5=A1?= Date: Fri, 11 Aug 2023 14:54:05 +0200 Subject: [PATCH 03/27] HeaderRouteStepFilterFactoryTest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pavel Jareš --- .../filters/HeaderRouteStepFilterFactory.java | 1 + .../HeaderRouteStepFilterFactoryTest.java | 61 +++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/filters/HeaderRouteStepFilterFactoryTest.java diff --git a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/filters/HeaderRouteStepFilterFactory.java b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/filters/HeaderRouteStepFilterFactory.java index 11ac153e5a..f17e5d83aa 100644 --- a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/filters/HeaderRouteStepFilterFactory.java +++ b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/filters/HeaderRouteStepFilterFactory.java @@ -20,6 +20,7 @@ * INTERRUPTION, GOODWILL, OR LOST DATA, EVEN IF BROADCOM IS * EXPRESSLY ADVISED OF SUCH LOSS OR DAMAGE. */ + package org.zowe.apiml.cloudgatewayservice.filters; import lombok.Data; diff --git a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/filters/HeaderRouteStepFilterFactoryTest.java b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/filters/HeaderRouteStepFilterFactoryTest.java new file mode 100644 index 0000000000..7996b4a1ff --- /dev/null +++ b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/filters/HeaderRouteStepFilterFactoryTest.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2022 Broadcom. All Rights Reserved. The term + * "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * + * This software and all information contained therein is + * confidential and proprietary and shall not be duplicated, + * used, disclosed, or disseminated in any way except as + * authorized by the applicable license agreement, without the + * express written permission of Broadcom. All authorized + * reproductions must be marked with this language. + * + * EXCEPT AS SET FORTH IN THE APPLICABLE LICENSE AGREEMENT, TO + * THE EXTENT PERMITTED BY APPLICABLE LAW, BROADCOM PROVIDES THIS + * SOFTWARE WITHOUT WARRANTY OF ANY KIND, INCLUDING WITHOUT + * LIMITATION, ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL BROADCOM + * BE LIABLE TO THE END USER OR ANY THIRD PARTY FOR ANY LOSS OR + * DAMAGE, DIRECT OR INDIRECT, FROM THE USE OF THIS SOFTWARE, + * INCLUDING WITHOUT LIMITATION, LOST PROFITS, BUSINESS + * INTERRUPTION, GOODWILL, OR LOST DATA, EVEN IF BROADCOM IS + * EXPRESSLY ADVISED OF SUCH LOSS OR DAMAGE. + */ + +package org.zowe.apiml.cloudgatewayservice.filters; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.springframework.cloud.gateway.filter.GatewayFilter; +import org.springframework.mock.http.server.reactive.MockServerHttpRequest; +import org.springframework.mock.web.server.MockServerWebExchange; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class HeaderRouteStepFilterFactoryTest { + + @ParameterizedTest(name = "When header {1} is set to {2} and the filter works with header {0} the value after processing should be {3}") + @CsvSource({ + "another,header-name,some-value,some-value", + "header-name,header-name,GW,", + "header-name,header-name,GW/,", + "header-name,header-name,GW/x,x", + "header-name,header-name,GW/x/y,x/y", + }) + void test(String headerNameConfig, String headerName, String headerValueOrigin, String headerValueNew) { + HeaderRouteStepFilterFactory.Config config = new HeaderRouteStepFilterFactory.Config(); + config.setHeader(headerNameConfig); + HeaderRouteStepFilterFactory headerRouteStepFilterFactory = new HeaderRouteStepFilterFactory(); + GatewayFilter filter = headerRouteStepFilterFactory.apply(config); + + MockServerHttpRequest request = MockServerHttpRequest + .get("/") + .header(headerName, headerValueOrigin).build(); + MockServerWebExchange exchange = MockServerWebExchange.from(request); + + filter.filter(exchange, exchange2 -> { + assertEquals(headerValueNew, exchange2.getRequest().getHeaders().getFirst(headerName)); + return null; + }); + } + +} \ No newline at end of file From b206dfd3c9d0e78b10a7e812137559b897b2ddf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jare=C5=A1?= Date: Fri, 11 Aug 2023 15:39:03 +0200 Subject: [PATCH 04/27] ByBasePathTest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pavel Jareš --- .../service/routing/ByBasePath.java | 7 +- .../service/routing/ByHeader.java | 4 +- .../service/routing/ByBasePathTest.java | 113 ++++++++++++++++++ 3 files changed, 118 insertions(+), 6 deletions(-) create mode 100644 cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByBasePathTest.java diff --git a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByBasePath.java b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByBasePath.java index c7403eb73a..fbb2fe1d4c 100644 --- a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByBasePath.java +++ b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByBasePath.java @@ -38,7 +38,7 @@ public ByBasePath(DiscoveryLocatorProperties properties) { super(properties); } - private String constructUrl(String...parts) { + static String constructUrl(String...parts) { StringBuilder sb = new StringBuilder(); for (String part : parts) { part = StringUtils.removeFirstAndLastOccurrence(part, "/"); @@ -51,17 +51,16 @@ private String constructUrl(String...parts) { } @Override - public void setCondition(RouteDefinition routeDefinition, ServiceInstance serviceInstance, RoutedService routedService) { + protected void setCondition(RouteDefinition routeDefinition, ServiceInstance serviceInstance, RoutedService routedService) { PredicateDefinition predicate = new PredicateDefinition(); predicate.setName("Path"); String predicateValue = constructUrl(serviceInstance.getServiceId(), routedService.getGatewayUrl(), "**"); predicate.addArg("pattern", predicateValue); routeDefinition.getPredicates().add(predicate); - routeDefinition.setOrder(routeDefinition.getOrder() + routedService.getServiceUrl().length()); } @Override - public void setFilters(RouteDefinition routeDefinition, ServiceInstance serviceInstance, RoutedService routedService) { + protected void setFilters(RouteDefinition routeDefinition, ServiceInstance serviceInstance, RoutedService routedService) { FilterDefinition filter = new FilterDefinition(); filter.setName("RewritePath"); diff --git a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByHeader.java b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByHeader.java index 564174fcf6..093f93a982 100644 --- a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByHeader.java +++ b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByHeader.java @@ -40,7 +40,7 @@ public ByHeader(DiscoveryLocatorProperties properties) { } @Override - public void setCondition(RouteDefinition routeDefinition, ServiceInstance serviceInstance, RoutedService routedService) { + protected void setCondition(RouteDefinition routeDefinition, ServiceInstance serviceInstance, RoutedService routedService) { PredicateDefinition predicate = new PredicateDefinition(); predicate.setName("Header"); @@ -51,7 +51,7 @@ public void setCondition(RouteDefinition routeDefinition, ServiceInstance servic } @Override - public void setFilters(RouteDefinition routeDefinition, ServiceInstance serviceInstance, RoutedService routedService) { + protected void setFilters(RouteDefinition routeDefinition, ServiceInstance serviceInstance, RoutedService routedService) { FilterDefinition filter = new FilterDefinition(); filter.setName("HeaderRouteStepFilterFactory"); filter.addArg("header", TARGET_HEADER_NAME); diff --git a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByBasePathTest.java b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByBasePathTest.java new file mode 100644 index 0000000000..757c068c26 --- /dev/null +++ b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByBasePathTest.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2022 Broadcom. All Rights Reserved. The term + * "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * + * This software and all information contained therein is + * confidential and proprietary and shall not be duplicated, + * used, disclosed, or disseminated in any way except as + * authorized by the applicable license agreement, without the + * express written permission of Broadcom. All authorized + * reproductions must be marked with this language. + * + * EXCEPT AS SET FORTH IN THE APPLICABLE LICENSE AGREEMENT, TO + * THE EXTENT PERMITTED BY APPLICABLE LAW, BROADCOM PROVIDES THIS + * SOFTWARE WITHOUT WARRANTY OF ANY KIND, INCLUDING WITHOUT + * LIMITATION, ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL BROADCOM + * BE LIABLE TO THE END USER OR ANY THIRD PARTY FOR ANY LOSS OR + * DAMAGE, DIRECT OR INDIRECT, FROM THE USE OF THIS SOFTWARE, + * INCLUDING WITHOUT LIMITATION, LOST PROFITS, BUSINESS + * INTERRUPTION, GOODWILL, OR LOST DATA, EVEN IF BROADCOM IS + * EXPRESSLY ADVISED OF SUCH LOSS OR DAMAGE. + */ + +package org.zowe.apiml.cloudgatewayservice.service.routing; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.gateway.discovery.DiscoveryLocatorProperties; +import org.springframework.cloud.gateway.filter.FilterDefinition; +import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition; +import org.springframework.cloud.gateway.route.RouteDefinition; +import org.zowe.apiml.product.routing.RoutedService; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; + +class ByBasePathTest { + + @Nested + class CommonParts { + + @ParameterizedTest(name = "constructUrl([{0}, {0}] returns {1}") + @CsvSource({ + "a,/a/a", + "/a,/a/a", + "a/,/a/a", + "/a/,/a/a", + "//,''" + }) + void givenUrlPath_whenConstructUrl_thenFixSlashes(String part, String path) { + assertEquals(path, ByBasePath.constructUrl(part, part)); + } + + @Test + void givenInstance_whenOrder_thenReturnsTheDefaultOrder() { + assertEquals(1, new ByBasePath(new DiscoveryLocatorProperties()).getOrder()); + } + + } + + @Nested + class RuleConstruction { + + @ParameterizedTest(name = "condition for service {0} with gatewayUrl {1} is {2}") + @CsvSource({ + "service,/path/,/service/path/**", + "service,path,/service/path/**" + }) + void givenInstanceConfig_whenSetCondition_thenConstructRegexCondition( + String serviceId, String gatewayUrl, String pattern + ) { + RouteDefinition routeDefinition = new RouteDefinition(); + ServiceInstance serviceInstance = mock(ServiceInstance.class); + doReturn(serviceId).when(serviceInstance).getServiceId(); + RoutedService routedService = new RoutedService(null, gatewayUrl, null); + + new ByBasePath(new DiscoveryLocatorProperties()).setCondition(routeDefinition, serviceInstance, routedService); + + assertEquals(1, routeDefinition.getPredicates().size()); + PredicateDefinition predicateDefinition = routeDefinition.getPredicates().get(0); + assertEquals("Path", predicateDefinition.getName()); + assertEquals(pattern, predicateDefinition.getArgs().get("pattern")); + } + + @ParameterizedTest(name = "to map URLs of service {0} from {1} to {2} is constructed pattern {3} and replacement {4} arguments") + @CsvSource({ + "service,/api/v1/,/x/,/service/api/v1/?(?.*),/x/${remaining}", + "service,api/v1,x,/service/api/v1/?(?.*),/x/${remaining}", + }) + void givenInstanceConfig_whenSetFilters_thenConstructRegexFilter( + String serviceId, String gatewayUrl, String serviceUrl, String pattern, String replacement + ) { + RouteDefinition routeDefinition = new RouteDefinition(); + ServiceInstance serviceInstance = mock(ServiceInstance.class); + doReturn(serviceId).when(serviceInstance).getServiceId(); + RoutedService routedService = new RoutedService(null, gatewayUrl, serviceUrl); + + new ByBasePath(new DiscoveryLocatorProperties()).setFilters(routeDefinition, serviceInstance, routedService); + + assertEquals(1, routeDefinition.getFilters().size()); + FilterDefinition filterDefinition = routeDefinition.getFilters().get(0); + assertEquals("RewritePath", filterDefinition.getName()); + assertEquals(pattern, filterDefinition.getArgs().get("regexp")); + assertEquals(replacement, filterDefinition.getArgs().get("replacement")); + } + + } + +} \ No newline at end of file From f5ef78f52553adc5210d09db740c4a1da6463591 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jare=C5=A1?= Date: Fri, 11 Aug 2023 16:05:49 +0200 Subject: [PATCH 05/27] ByHeaderTest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pavel Jareš --- .../service/routing/ByHeader.java | 1 + .../service/routing/ByHeaderTest.java | 84 +++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByHeaderTest.java diff --git a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByHeader.java b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByHeader.java index 093f93a982..b9db66fa0e 100644 --- a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByHeader.java +++ b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByHeader.java @@ -20,6 +20,7 @@ * INTERRUPTION, GOODWILL, OR LOST DATA, EVEN IF BROADCOM IS * EXPRESSLY ADVISED OF SUCH LOSS OR DAMAGE. */ + package org.zowe.apiml.cloudgatewayservice.service.routing; import org.springframework.cloud.client.ServiceInstance; diff --git a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByHeaderTest.java b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByHeaderTest.java new file mode 100644 index 0000000000..ae502bb811 --- /dev/null +++ b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByHeaderTest.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2022 Broadcom. All Rights Reserved. The term + * "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * + * This software and all information contained therein is + * confidential and proprietary and shall not be duplicated, + * used, disclosed, or disseminated in any way except as + * authorized by the applicable license agreement, without the + * express written permission of Broadcom. All authorized + * reproductions must be marked with this language. + * + * EXCEPT AS SET FORTH IN THE APPLICABLE LICENSE AGREEMENT, TO + * THE EXTENT PERMITTED BY APPLICABLE LAW, BROADCOM PROVIDES THIS + * SOFTWARE WITHOUT WARRANTY OF ANY KIND, INCLUDING WITHOUT + * LIMITATION, ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL BROADCOM + * BE LIABLE TO THE END USER OR ANY THIRD PARTY FOR ANY LOSS OR + * DAMAGE, DIRECT OR INDIRECT, FROM THE USE OF THIS SOFTWARE, + * INCLUDING WITHOUT LIMITATION, LOST PROFITS, BUSINESS + * INTERRUPTION, GOODWILL, OR LOST DATA, EVEN IF BROADCOM IS + * EXPRESSLY ADVISED OF SUCH LOSS OR DAMAGE. + */ + +package org.zowe.apiml.cloudgatewayservice.service.routing; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.gateway.discovery.DiscoveryLocatorProperties; +import org.springframework.cloud.gateway.filter.FilterDefinition; +import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition; +import org.springframework.cloud.gateway.route.RouteDefinition; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; + +class ByHeaderTest { + + @Nested + class CommonParts { + + @Test + void givenInstance_whenOrder_thenReturnsTheDefaultOrder() { + assertEquals(0, new ByHeader(new DiscoveryLocatorProperties()).getOrder()); + } + + } + + @Nested + class RuleConstruction { + + private static final String TARGET_HEADER_NAME = "X-Request-Id"; + + @Test + void givenInstanceConfig_whenSetCondition_thenConstructRegexCondition() { + RouteDefinition routeDefinition = new RouteDefinition(); + ServiceInstance serviceInstance = mock(ServiceInstance.class); + doReturn("myservice").when(serviceInstance).getServiceId(); + + new ByHeader(new DiscoveryLocatorProperties()).setCondition(routeDefinition, serviceInstance, null); + + assertEquals(1, routeDefinition.getPredicates().size()); + PredicateDefinition predicateDefinition = routeDefinition.getPredicates().get(0); + assertEquals("Header", predicateDefinition.getName()); + assertEquals(TARGET_HEADER_NAME, predicateDefinition.getArgs().get("header")); + assertEquals("myservice(/.*)?", predicateDefinition.getArgs().get("regexp")); + } + + @Test + void givenInstanceConfig_whenSetFilters_thenConstructHeaderRouteStepFilter() { + RouteDefinition routeDefinition = new RouteDefinition(); + + new ByHeader(new DiscoveryLocatorProperties()).setFilters(routeDefinition, null, null); + + assertEquals(1, routeDefinition.getFilters().size()); + FilterDefinition filterDefinition = routeDefinition.getFilters().get(0); + assertEquals("HeaderRouteStepFilterFactory", filterDefinition.getName()); + assertEquals(TARGET_HEADER_NAME, filterDefinition.getArgs().get("header")); + } + + } + +} \ No newline at end of file From 8a82aaaeca94209acdf0bdbd3dbb1c16d6f7b062 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jare=C5=A1?= Date: Fri, 11 Aug 2023 16:53:16 +0200 Subject: [PATCH 06/27] RouteDefinitionProducerTest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pavel Jareš --- .../routing/RouteDefinitionProducer.java | 1 + .../routing/RouteDefinitionProducerTest.java | 122 ++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/routing/RouteDefinitionProducerTest.java diff --git a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/RouteDefinitionProducer.java b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/RouteDefinitionProducer.java index 96896c0af2..0b835c5e37 100644 --- a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/RouteDefinitionProducer.java +++ b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/RouteDefinitionProducer.java @@ -20,6 +20,7 @@ * INTERRUPTION, GOODWILL, OR LOST DATA, EVEN IF BROADCOM IS * EXPRESSLY ADVISED OF SUCH LOSS OR DAMAGE. */ + package org.zowe.apiml.cloudgatewayservice.service.routing; import lombok.RequiredArgsConstructor; diff --git a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/routing/RouteDefinitionProducerTest.java b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/routing/RouteDefinitionProducerTest.java new file mode 100644 index 0000000000..ee3dbf5f28 --- /dev/null +++ b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/routing/RouteDefinitionProducerTest.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2022 Broadcom. All Rights Reserved. The term + * "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * + * This software and all information contained therein is + * confidential and proprietary and shall not be duplicated, + * used, disclosed, or disseminated in any way except as + * authorized by the applicable license agreement, without the + * express written permission of Broadcom. All authorized + * reproductions must be marked with this language. + * + * EXCEPT AS SET FORTH IN THE APPLICABLE LICENSE AGREEMENT, TO + * THE EXTENT PERMITTED BY APPLICABLE LAW, BROADCOM PROVIDES THIS + * SOFTWARE WITHOUT WARRANTY OF ANY KIND, INCLUDING WITHOUT + * LIMITATION, ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL BROADCOM + * BE LIABLE TO THE END USER OR ANY THIRD PARTY FOR ANY LOSS OR + * DAMAGE, DIRECT OR INDIRECT, FROM THE USE OF THIS SOFTWARE, + * INCLUDING WITHOUT LIMITATION, LOST PROFITS, BUSINESS + * INTERRUPTION, GOODWILL, OR LOST DATA, EVEN IF BROADCOM IS + * EXPRESSLY ADVISED OF SUCH LOSS OR DAMAGE. + */ + +package org.zowe.apiml.cloudgatewayservice.service.routing; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.gateway.discovery.DiscoveryLocatorProperties; +import org.springframework.cloud.gateway.route.RouteDefinition; +import org.zowe.apiml.product.routing.RoutedService; + +import java.util.Collections; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; +import static org.zowe.apiml.constants.EurekaMetadataDefinition.APIML_ID; +import static org.zowe.apiml.constants.EurekaMetadataDefinition.SERVICE_EXTERNAL_URL; + +class RouteDefinitionProducerTest { + + private static final String EXTERNAL_URL = "https://external.url"; + + private DiscoveryLocatorProperties properties = new DiscoveryLocatorProperties(); + private final RouteDefinitionProducer routeDefinitionProducer = mock( + RouteDefinitionProducer.class, withSettings().useConstructor(properties).defaultAnswer(CALLS_REAL_METHODS) + ); + + ServiceInstance serviceInstance; + + @BeforeEach + void initServiceInstance() { + serviceInstance = mock(ServiceInstance.class); + doReturn("myservice").when(serviceInstance).getServiceId(); + } + + @Nested + class HostnameEvaluation { + + + @Test + void givenServiceInstance_whenEvalHostname_thenConstructUrl() { + assertEquals("lb://myservice", routeDefinitionProducer.evalHostname(serviceInstance)); + } + + @Test + void givenExternalUrl_whenGetHostname_thenReturnExternalUrl() { + doReturn(Collections.singletonMap(SERVICE_EXTERNAL_URL, EXTERNAL_URL)).when(serviceInstance).getMetadata(); + assertEquals(EXTERNAL_URL, routeDefinitionProducer.getHostname(serviceInstance)); + } + + @Test + void givenNoExternalUrl_whenGetHostname_thenReturnDefaultUrl() { + assertEquals("lb://myservice", routeDefinitionProducer.getHostname(serviceInstance)); + } + + @Test + void givenApimlId_whenGetEvalServiceInstance_thenMockGetServiceId() { + doReturn(Collections.singletonMap(APIML_ID, "myinstance")).when(serviceInstance).getMetadata(); + ServiceInstance modified = routeDefinitionProducer.getEvalServiceInstance(serviceInstance); + assertNotSame(modified, serviceInstance); + assertEquals("myinstance", modified.getServiceId()); + } + + @Test + void givenNoApimlId_whenGetEvalServiceInstance_thenReturnLowerCaseCopy() { + doReturn("myService").when(serviceInstance).getServiceId(); + ServiceInstance modified = routeDefinitionProducer.getEvalServiceInstance(serviceInstance); + assertNotSame(modified, serviceInstance); + assertEquals("myservice", modified.getServiceId()); + } + + } + + @Nested + class ConstructRule { + + @Test + void givenInstanceData_whenGet_thenConstructDraftOfRule() { + RoutedService routedService = mock(RoutedService.class); + doReturn("routeId").when(routedService).getSubServiceId(); + doReturn(Collections.singletonMap(SERVICE_EXTERNAL_URL, EXTERNAL_URL)).when(serviceInstance).getMetadata(); + doReturn(123).when(routeDefinitionProducer).getOrder(); + doReturn("original:instance:id").when(serviceInstance).getInstanceId(); + ServiceInstance serviceInstanceModified = new RouteDefinitionProducer.ServiceInstanceEval(serviceInstance, "anotherId"); + doReturn(serviceInstanceModified).when(routeDefinitionProducer).getEvalServiceInstance(serviceInstance); + + RouteDefinition routeDefinition = routeDefinitionProducer.get(serviceInstance, routedService); + + assertEquals(123, routeDefinition.getOrder()); + assertEquals("anotherId", serviceInstanceModified.getServiceId()); + assertEquals("original:instance:id:routeId", routeDefinition.getId()); + assertEquals(EXTERNAL_URL, routeDefinition.getUri().toString()); + assertArrayEquals(routeDefinition.getMetadata().entrySet().toArray(), serviceInstance.getMetadata().entrySet().toArray()); + verify(routeDefinitionProducer).setCondition(routeDefinition, serviceInstanceModified, routedService); + verify(routeDefinitionProducer).setFilters(routeDefinition, serviceInstanceModified, routedService); + } + + } + +} \ No newline at end of file From df346d14bbbfdc4cd1d4807b248fd981204db2c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jare=C5=A1?= Date: Fri, 11 Aug 2023 17:02:01 +0200 Subject: [PATCH 07/27] X509Test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pavel Jareš --- .../service/scheme/X509.java | 3 +- .../service/scheme/X509Test.java | 55 +++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/scheme/X509Test.java diff --git a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/scheme/X509.java b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/scheme/X509.java index d01f356471..9904dacfc3 100644 --- a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/scheme/X509.java +++ b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/scheme/X509.java @@ -20,6 +20,7 @@ * INTERRUPTION, GOODWILL, OR LOST DATA, EVEN IF BROADCOM IS * EXPRESSLY ADVISED OF SUCH LOSS OR DAMAGE. */ + package org.zowe.apiml.cloudgatewayservice.service.scheme; import org.springframework.cloud.gateway.filter.FilterDefinition; @@ -44,7 +45,7 @@ public void apply(RouteDefinition routeDefinition, Authentication auth) { FilterDefinition x509filter = new FilterDefinition(); x509filter.setName("X509FilterFactory"); Map m = new HashMap<>(); - m.put("headers",auth.getHeaders()); + m.put("headers", auth.getHeaders()); x509filter.setArgs(m); routeDefinition.getFilters().add(x509filter); } diff --git a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/scheme/X509Test.java b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/scheme/X509Test.java new file mode 100644 index 0000000000..cbeac97548 --- /dev/null +++ b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/scheme/X509Test.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2022 Broadcom. All Rights Reserved. The term + * "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * + * This software and all information contained therein is + * confidential and proprietary and shall not be duplicated, + * used, disclosed, or disseminated in any way except as + * authorized by the applicable license agreement, without the + * express written permission of Broadcom. All authorized + * reproductions must be marked with this language. + * + * EXCEPT AS SET FORTH IN THE APPLICABLE LICENSE AGREEMENT, TO + * THE EXTENT PERMITTED BY APPLICABLE LAW, BROADCOM PROVIDES THIS + * SOFTWARE WITHOUT WARRANTY OF ANY KIND, INCLUDING WITHOUT + * LIMITATION, ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL BROADCOM + * BE LIABLE TO THE END USER OR ANY THIRD PARTY FOR ANY LOSS OR + * DAMAGE, DIRECT OR INDIRECT, FROM THE USE OF THIS SOFTWARE, + * INCLUDING WITHOUT LIMITATION, LOST PROFITS, BUSINESS + * INTERRUPTION, GOODWILL, OR LOST DATA, EVEN IF BROADCOM IS + * EXPRESSLY ADVISED OF SUCH LOSS OR DAMAGE. + */ + +package org.zowe.apiml.cloudgatewayservice.service.scheme; + +import org.junit.jupiter.api.Test; +import org.springframework.cloud.gateway.filter.FilterDefinition; +import org.springframework.cloud.gateway.route.RouteDefinition; +import org.zowe.apiml.auth.Authentication; +import org.zowe.apiml.auth.AuthenticationScheme; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class X509Test { + + @Test + void givenX509_whenGetAuthenticationScheme_thenReturnProperType() { + assertEquals(AuthenticationScheme.X509, new X509().getAuthenticationScheme()); + } + + @Test + void givenX509_whenApply_thenFulfillFilterFactorArgs() { + RouteDefinition routeDefinition = new RouteDefinition(); + Authentication authentication = new Authentication(); + authentication.setHeaders("header1,header2"); + + new X509().apply(routeDefinition, authentication); + + assertEquals(1, routeDefinition.getFilters().size()); + FilterDefinition filterDefinition = routeDefinition.getFilters().get(0); + assertEquals("header1,header2", filterDefinition.getArgs().get("headers")); + assertEquals("X509FilterFactory", filterDefinition.getName()); + } + +} \ No newline at end of file From 561c9e0b37869377aed2104efef763ba6e1d9410 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jare=C5=A1?= Date: Fri, 11 Aug 2023 17:04:19 +0200 Subject: [PATCH 08/27] HttpBasicPassticketTest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pavel Jareš --- .../service/scheme/HttpBasicPassticket.java | 1 + .../scheme/HttpBasicPassticketTest.java | 55 +++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/scheme/HttpBasicPassticketTest.java diff --git a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/scheme/HttpBasicPassticket.java b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/scheme/HttpBasicPassticket.java index 9b2c10a109..a5f701285a 100644 --- a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/scheme/HttpBasicPassticket.java +++ b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/scheme/HttpBasicPassticket.java @@ -20,6 +20,7 @@ * INTERRUPTION, GOODWILL, OR LOST DATA, EVEN IF BROADCOM IS * EXPRESSLY ADVISED OF SUCH LOSS OR DAMAGE. */ + package org.zowe.apiml.cloudgatewayservice.service.scheme; import org.springframework.cloud.gateway.filter.FilterDefinition; diff --git a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/scheme/HttpBasicPassticketTest.java b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/scheme/HttpBasicPassticketTest.java new file mode 100644 index 0000000000..c608f1917e --- /dev/null +++ b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/scheme/HttpBasicPassticketTest.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2022 Broadcom. All Rights Reserved. The term + * "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * + * This software and all information contained therein is + * confidential and proprietary and shall not be duplicated, + * used, disclosed, or disseminated in any way except as + * authorized by the applicable license agreement, without the + * express written permission of Broadcom. All authorized + * reproductions must be marked with this language. + * + * EXCEPT AS SET FORTH IN THE APPLICABLE LICENSE AGREEMENT, TO + * THE EXTENT PERMITTED BY APPLICABLE LAW, BROADCOM PROVIDES THIS + * SOFTWARE WITHOUT WARRANTY OF ANY KIND, INCLUDING WITHOUT + * LIMITATION, ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL BROADCOM + * BE LIABLE TO THE END USER OR ANY THIRD PARTY FOR ANY LOSS OR + * DAMAGE, DIRECT OR INDIRECT, FROM THE USE OF THIS SOFTWARE, + * INCLUDING WITHOUT LIMITATION, LOST PROFITS, BUSINESS + * INTERRUPTION, GOODWILL, OR LOST DATA, EVEN IF BROADCOM IS + * EXPRESSLY ADVISED OF SUCH LOSS OR DAMAGE. + */ + +package org.zowe.apiml.cloudgatewayservice.service.scheme; + +import org.junit.jupiter.api.Test; +import org.springframework.cloud.gateway.filter.FilterDefinition; +import org.springframework.cloud.gateway.route.RouteDefinition; +import org.zowe.apiml.auth.Authentication; +import org.zowe.apiml.auth.AuthenticationScheme; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class HttpBasicPassticketTest { + + @Test + void givenInstance_whenGetAuthenticationScheme_thenReturnProperType() { + assertEquals(AuthenticationScheme.HTTP_BASIC_PASSTICKET, new HttpBasicPassticket().getAuthenticationScheme()); + } + + @Test + void givenInstance_whenApply_thenFulfillFilterFactorArgs() { + RouteDefinition routeDefinition = new RouteDefinition(); + Authentication authentication = new Authentication(); + authentication.setApplid("applid"); + + new HttpBasicPassticket().apply(routeDefinition, authentication); + + assertEquals(1, routeDefinition.getFilters().size()); + FilterDefinition filterDefinition = routeDefinition.getFilters().get(0); + assertEquals("applid", filterDefinition.getArgs().get("applicationName")); + assertEquals("PassticketFilterFactory", filterDefinition.getName()); + } + +} \ No newline at end of file From 8262f49f3a23d27db6f4e38fd92e200ff07a8167 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jare=C5=A1?= Date: Mon, 14 Aug 2023 15:49:32 +0200 Subject: [PATCH 09/27] RouteLocatorTest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pavel Jareš --- .../service/RouteLocator.java | 23 +- .../service/RouteLocatorTest.java | 250 ++++++++++++++---- 2 files changed, 204 insertions(+), 69 deletions(-) diff --git a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocator.java b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocator.java index 881875b8ba..0b245dfb31 100644 --- a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocator.java +++ b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocator.java @@ -20,6 +20,7 @@ * INTERRUPTION, GOODWILL, OR LOST DATA, EVEN IF BROADCOM IS * EXPRESSLY ADVISED OF SUCH LOSS OR DAMAGE. */ + package org.zowe.apiml.cloudgatewayservice.service; import lombok.AccessLevel; @@ -33,6 +34,7 @@ import org.springframework.stereotype.Service; import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource; import org.zowe.apiml.auth.Authentication; +import org.zowe.apiml.auth.AuthenticationScheme; import org.zowe.apiml.cloudgatewayservice.service.routing.RouteDefinitionProducer; import org.zowe.apiml.cloudgatewayservice.service.scheme.SchemeHandler; import org.zowe.apiml.eurekaservice.client.util.EurekaMetadataParser; @@ -60,7 +62,7 @@ public class RouteLocator implements RouteDefinitionLocator { private final List commonFilters; private final List routeDefinitionProducers; - private final Map schemeHandlers = new HashMap<>(); + private final Map schemeHandlers = new HashMap<>(); @Getter(lazy=true, value = AccessLevel.PRIVATE) private final UrlBasedCorsConfigurationSource corsConfigurationSource = context.getBean(UrlBasedCorsConfigurationSource.class); @@ -80,17 +82,17 @@ public RouteLocator( this.routeDefinitionProducers = routeDefinitionProducers; for (SchemeHandler schemeHandler : schemeHandlersList) { - schemeHandlers.put(schemeHandler.getAuthenticationScheme().getScheme(), schemeHandler); + schemeHandlers.put(schemeHandler.getAuthenticationScheme(), schemeHandler); } } - private Flux> getServiceInstances() { + Flux> getServiceInstances() { return discoveryClient.getServices() .flatMap(service -> discoveryClient.getInstances(service) .collectList()); } - protected void setAuth(RouteDefinition routeDefinition, Authentication auth) { + void setAuth(RouteDefinition routeDefinition, Authentication auth) { if (auth != null && auth.getScheme() != null) { SchemeHandler schemeHandler = schemeHandlers.get(auth.getScheme()); if (schemeHandler != null) { @@ -99,7 +101,7 @@ protected void setAuth(RouteDefinition routeDefinition, Authentication auth) { } } - private void setCors(ServiceInstance serviceInstance) { + void setCors(ServiceInstance serviceInstance) { corsUtils.setCorsConfiguration( serviceInstance.getServiceId().toLowerCase(), serviceInstance.getMetadata(), @@ -109,12 +111,6 @@ private void setCors(ServiceInstance serviceInstance) { }); } - private String normalizeUrl(String in) { - in = in.replaceAll("\\*", ""); - in = StringUtils.removeFirstAndLastOccurrence(in, "/"); - return in; - } - @Override public Flux getRouteDefinitions() { EurekaMetadataParser metadataParser = new EurekaMetadataParser(); @@ -122,8 +118,7 @@ public Flux getRouteDefinitions() { /** * It generates each rule for each combination of instance x routing x generator ({@link RouteDefinitionProducer}) * - * The routes are sorted by serviceUrl to avoid clashing between multiple levels of paths, ie. /** vs /a, stars are - * removed in {@link #normalizeUrl(String)}. + * The routes are sorted by serviceUrl to avoid clashing between multiple levels of paths, ie. / vs. /a. * * Sorting routes and generators by order allows to redefine order of each rule. There is no possible to have * multiple valid rules for the same case at one moment. @@ -133,7 +128,7 @@ public Flux getRouteDefinitions() { setCors(serviceInstance); return metadataParser.parseToListRoute(serviceInstance.getMetadata()).stream() // sorting avoid a conflict with the more general pattern - .sorted(Comparator.comparingInt(x -> normalizeUrl(x.getServiceUrl()).length()).reversed()) + .sorted(Comparator.comparingInt(x -> StringUtils.removeFirstAndLastOccurrence(x.getGatewayUrl(), "/").length()).reversed()) .map(routedService -> routeDefinitionProducers.stream() .sorted(Comparator.comparingInt(x -> x.getOrder())) diff --git a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocatorTest.java b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocatorTest.java index d11aca381e..00e28253de 100644 --- a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocatorTest.java +++ b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocatorTest.java @@ -10,78 +10,218 @@ package org.zowe.apiml.cloudgatewayservice.service; +import org.apache.logging.log4j.util.TriConsumer; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import org.springframework.cloud.client.DefaultServiceInstance; +import org.mockito.ArgumentCaptor; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient; -import org.springframework.cloud.gateway.discovery.DiscoveryLocatorProperties; import org.springframework.cloud.gateway.filter.FilterDefinition; import org.springframework.cloud.gateway.route.RouteDefinition; +import org.springframework.context.ApplicationContext; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource; +import org.zowe.apiml.auth.Authentication; +import org.zowe.apiml.auth.AuthenticationScheme; +import org.zowe.apiml.cloudgatewayservice.service.routing.RouteDefinitionProducer; +import org.zowe.apiml.cloudgatewayservice.service.scheme.SchemeHandler; +import org.zowe.apiml.eurekaservice.client.util.EurekaMetadataParser; +import org.zowe.apiml.product.routing.RoutedService; import org.zowe.apiml.util.CorsUtils; import reactor.core.publisher.Flux; -import java.util.*; -import java.util.regex.Pattern; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; -import static org.zowe.apiml.constants.EurekaMetadataDefinition.*; +import static org.mockito.Mockito.*; +import static org.zowe.apiml.constants.EurekaMetadataDefinition.APIML_ID; class RouteLocatorTest { - static Map metadata = new HashMap<>(); + private static final FilterDefinition[] COMMON_FILTERS = { + new FilterDefinition(), new FilterDefinition() + }; + private static final SchemeHandler[] SCHEME_HANDLER_FILTERS = { + createSchemeHandler(AuthenticationScheme.BYPASS), createSchemeHandler(AuthenticationScheme.ZOWE_JWT) + }; + private static final RouteDefinitionProducer[] PRODUCERS = { + createRouteDefinitionProducer(5, "id5"), + createRouteDefinitionProducer(0, "id0"), + createRouteDefinitionProducer(10, "id10") + }; + + private UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = mock(UrlBasedCorsConfigurationSource.class); + private CorsUtils corsUtils = mock(CorsUtils.class); + private ReactiveDiscoveryClient discoveryClient = mock(ReactiveDiscoveryClient.class); + + private RouteLocator routeLocator; + + @BeforeEach + void init() { + ApplicationContext context = mock(ApplicationContext.class); + doReturn(urlBasedCorsConfigurationSource).when(context).getBean(UrlBasedCorsConfigurationSource.class); + + routeLocator = spy(new RouteLocator( + context, + corsUtils, + discoveryClient, + Arrays.asList(COMMON_FILTERS), + Arrays.asList(SCHEME_HANDLER_FILTERS), + Arrays.asList(PRODUCERS) + )); + } + + private ServiceInstance createServiceInstance(String serviceId, String...routes) { + ServiceInstance serviceInstance = mock(ServiceInstance.class); + doReturn(serviceId).when(serviceInstance).getServiceId(); + + Map metadata = new HashMap<>(); + int i = 1; + for (String route : routes) { + metadata.put("apiml.routes.api-v" + i + ".gatewayUrl", route); + metadata.put("apiml.routes.api-v" + i + ".serviceUrl", route); + i++; + } + doReturn(metadata).when(serviceInstance).getMetadata(); + + return serviceInstance; + } + + private static SchemeHandler createSchemeHandler(AuthenticationScheme type) { + SchemeHandler out = mock(SchemeHandler.class); + doReturn(type).when(out).getAuthenticationScheme(); + return out; + } + + private static RouteDefinitionProducer createRouteDefinitionProducer(int order, String id) { + EurekaMetadataParser metadataParser = new EurekaMetadataParser(); + RouteDefinitionProducer rdp = mock(RouteDefinitionProducer.class); + doReturn(order).when(rdp).getOrder(); + doAnswer(answer -> { + ServiceInstance serviceInstance = answer.getArgument(0); + RoutedService routedService = answer.getArgument(1); + + RouteDefinition routeDefinition = new RouteDefinition(); + Map metadata = new HashMap<>(); + metadata.put("serviceId", serviceInstance.getServiceId()); + metadata.put("gatewayUrl", routedService.getGatewayUrl()); + metadata.put("producerId", id); + routeDefinition.setMetadata(metadata); + + return routeDefinition; + }).when(rdp).get(any(), any()); + return rdp; + } + + @Nested + class CommonMethods { + + @Test + void givenDiscoveryClient_whenGetServiceInstances_thenMapThem() { + when(discoveryClient.getServices()).thenReturn(Flux.fromArray(new String[] {"service1", "service2"})); + ServiceInstance serviceInstance1 = mock(ServiceInstance.class); + ServiceInstance serviceInstance2 = mock(ServiceInstance.class); + doReturn(Flux.just(serviceInstance1)).when(discoveryClient).getInstances("service1"); + doReturn(Flux.just(serviceInstance2)).when(discoveryClient).getInstances("service2"); + + doCallRealMethod().when(routeLocator).getServiceInstances(); + assertArrayEquals( + new Object[] {Collections.singletonList(serviceInstance1), Collections.singletonList(serviceInstance2)}, + routeLocator.getServiceInstances().toStream().toArray() + ); + } + + @Test + void givenNoAuthentication_whenSetAuth_thenDoNothing() { + assertDoesNotThrow(() -> routeLocator.setAuth(null, null)); + } + + @Test + void givenNoAuthenticationScheme_whenSetAuth_thenDoNothing() { + assertDoesNotThrow(() -> routeLocator.setAuth(null, new Authentication())); + } + + @Test + void givenUnknownAuthenticationScheme_whenSetAuth_thenDoNothing() { + assertDoesNotThrow(() -> routeLocator.setAuth(null, new Authentication(AuthenticationScheme.X509, null))); + } + + @Test + void givenExistingAuthenticationScheme_whenSetAuth_thenCallApply() { + RouteDefinition routeDefinition = mock(RouteDefinition.class); + Authentication authentication = new Authentication(AuthenticationScheme.BYPASS, null); + + routeLocator.setAuth(routeDefinition, authentication); + + verify(SCHEME_HANDLER_FILTERS[0]).apply(routeDefinition, authentication); + } + + private TriConsumer getCorsLambda(Consumer> metadataProcessor) { + ServiceInstance serviceInstance = createServiceInstance("myservice", "api/v1"); + metadataProcessor.accept(serviceInstance.getMetadata()); + + routeLocator.setCors(serviceInstance); + ArgumentCaptor> lambdaCaptor = ArgumentCaptor.forClass(TriConsumer.class); + verify(corsUtils).setCorsConfiguration(anyString(), any(), lambdaCaptor.capture()); + + return lambdaCaptor.getValue(); + } + + @Test + void givenApimlId_whenSetCors_thenServiceIdIsReplacedWithApimlId() { + TriConsumer corsLambda = getCorsLambda(md -> md.put(APIML_ID, "apimlid")); + + corsLambda.accept(null, "myservice", null); + + verify(urlBasedCorsConfigurationSource).registerCorsConfiguration("/apimlid/**", null); + } + + @Test + void givenNoApimlId_whenSetCors_thenServiceIdIsUsed() { + TriConsumer corsLambda = getCorsLambda(md -> {}); + + corsLambda.accept(null, "myservice", null); + + verify(urlBasedCorsConfigurationSource).registerCorsConfiguration("/myservice/**", null); + } - static { - metadata.put(ROUTES + ".api-v1." + ROUTES_GATEWAY_URL, "api/v1"); - metadata.put(ROUTES + ".api-v1." + ROUTES_SERVICE_URL, "/"); } - ServiceInstance instance = new DefaultServiceInstance("gateway-10012", "gateway", "gatewayhost", 10012, true, metadata); - ServiceInstance instance2 = new DefaultServiceInstance("gateway-2-10012", "gateway", "gatewayhost-2", 10012, true, metadata); - ReactiveDiscoveryClient dc = mock(ReactiveDiscoveryClient.class); - DiscoveryLocatorProperties properties = new DiscoveryLocatorProperties(); - -// @Nested -// class GivenRouteLocator { -// @Test -// void givenServiceWithDefinedMetadata_thenLocateRoutes() { -// Flux services = Flux.fromIterable(Collections.singleton("gateway")); -// Flux serviceInstances = Flux.fromIterable(Collections.singleton(instance)); -// when(dc.getServices()).thenReturn(services); -// when(dc.getInstances("gateway")).thenReturn(serviceInstances); -// CorsUtils corsUtils = new CorsUtils(false); -// RouteLocator locator = new RouteLocator(dc, properties, Collections.singletonList(new FilterDefinition("name=value")), null, corsUtils); -// Flux definitionFlux = locator.getRouteDefinitions(); -// List definitions = definitionFlux.collectList().block(); -// assertNotNull(definitions); -// assertEquals(1, definitions.size()); -// } -// } -// -// @Nested -// class GivenProxyRouteLocator { -// @Test -// void whenServiceIsMatched_thenCreateRouteWithCorrectPredicate() { -// Flux services = Flux.fromIterable(Collections.singleton("gateway")); -// List instances = Arrays.asList(instance, instance2); -// Flux serviceInstances = Flux.fromIterable(instances); -// when(dc.getServices()).thenReturn(services); -// when(dc.getInstances("gateway")).thenReturn(serviceInstances); -// CorsUtils corsUtils = new CorsUtils(false); -// ProxyRouteLocator locator = new ProxyRouteLocator(dc, properties, Collections.emptyList(), null, corsUtils); -// Flux definitionFlux = locator.getRouteDefinitions(); -// List definitions = definitionFlux.collectList().block(); -// assertNotNull(definitions); -// assertEquals(2, definitions.size()); -// for (int i = 0; i < definitions.size(); i++) { -// RouteDefinition def = definitions.get(i); -// String expression = def.getPredicates().get(0).getArgs().get("regexp"); -// assertTrue(Pattern.matches(expression, instances.get(i).getServiceId() + instances.get(i).getHost())); -// } -// } -// } + @Nested + class Generating { + + @Test + void givenRouteLocator_whenGetRouteDefinitions_thenGenerateAll() { + doReturn(Flux.fromIterable(Arrays.asList( + Arrays.asList( + createServiceInstance("service1", "/", "/a/b"), + createServiceInstance("service2", "/a/b", "/") + ) + ))).when(routeLocator).getServiceInstances(); + RouteDefinition[] rds = routeLocator.getRouteDefinitions().toStream().toArray(RouteDefinition[]::new); + + int index = 0; + for (String serviceId : new String[] {"service1", "service2"}) { + verify(corsUtils).setCorsConfiguration(eq(serviceId), any(), any()); + + for (String gatewayUrl : new String[] {"a/b", ""}) { + for (String producerId : new String[] {"id0", "id5", "id10"}) { + assertEquals(index, rds[index].getOrder()); + assertEquals(serviceId, rds[index].getMetadata().get("serviceId")); + assertEquals(gatewayUrl, rds[index].getMetadata().get("gatewayUrl")); + assertEquals(producerId, rds[index].getMetadata().get("producerId")); + index++; + } + } + } + } + + } } From 77691e8c1d8952d69e3d7086a3912f282ea6b566 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jare=C5=A1?= Date: Mon, 14 Aug 2023 15:50:41 +0200 Subject: [PATCH 10/27] remove test of refactored code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pavel Jareš --- .../config/ConnectionsConfigTest.java | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/config/ConnectionsConfigTest.java b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/config/ConnectionsConfigTest.java index b98a06f948..f91b096c3e 100644 --- a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/config/ConnectionsConfigTest.java +++ b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/config/ConnectionsConfigTest.java @@ -49,16 +49,6 @@ void thenIsNotNull() { } } - @Nested - class WhenCreateRouteLocator { - @Test - void thenIsNotNull() { - ReactiveDiscoveryClient discoveryClient = mock(ReactiveDiscoveryClient.class); - DiscoveryLocatorProperties properties = mock(DiscoveryLocatorProperties.class); - Assertions.assertNotNull(routingConfig.proxyRouteDefLocator(discoveryClient, properties, Collections.singletonList(new FilterDefinition("name=value")), null, null)); - } - } - @Nested class WhenInitializeEurekaClient { @Mock From 1f7ecd532153cbce695a3ca49a4e00c11c6d4ba9 Mon Sep 17 00:00:00 2001 From: Elena Kubantseva Date: Mon, 14 Aug 2023 17:10:07 +0200 Subject: [PATCH 11/27] fix licenses and checkstyle errors Signed-off-by: Elena Kubantseva --- .../filters/HeaderRouteStepFilterFactory.java | 23 ++++----------- .../service/RouteLocator.java | 25 ++++------------- .../service/routing/ByBasePath.java | 24 ++++------------ .../service/routing/ByHeader.java | 23 ++++----------- .../routing/RouteDefinitionProducer.java | 23 ++++----------- .../service/scheme/HttpBasicPassticket.java | 23 ++++----------- .../service/scheme/SchemeHandler.java | 28 ++++++------------- .../service/scheme/X509.java | 23 ++++----------- .../HeaderRouteStepFilterFactoryTest.java | 25 ++++------------- .../service/routing/ByBasePathTest.java | 25 ++++------------- .../service/routing/ByHeaderTest.java | 25 ++++------------- .../routing/RouteDefinitionProducerTest.java | 25 ++++------------- .../scheme/HttpBasicPassticketTest.java | 25 ++++------------- .../service/scheme/X509Test.java | 25 ++++------------- 14 files changed, 81 insertions(+), 261 deletions(-) diff --git a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/filters/HeaderRouteStepFilterFactory.java b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/filters/HeaderRouteStepFilterFactory.java index f17e5d83aa..31d418e6cb 100644 --- a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/filters/HeaderRouteStepFilterFactory.java +++ b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/filters/HeaderRouteStepFilterFactory.java @@ -1,24 +1,11 @@ /* - * Copyright (c) 2022 Broadcom. All Rights Reserved. The term - * "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html * - * This software and all information contained therein is - * confidential and proprietary and shall not be duplicated, - * used, disclosed, or disseminated in any way except as - * authorized by the applicable license agreement, without the - * express written permission of Broadcom. All authorized - * reproductions must be marked with this language. + * SPDX-License-Identifier: EPL-2.0 * - * EXCEPT AS SET FORTH IN THE APPLICABLE LICENSE AGREEMENT, TO - * THE EXTENT PERMITTED BY APPLICABLE LAW, BROADCOM PROVIDES THIS - * SOFTWARE WITHOUT WARRANTY OF ANY KIND, INCLUDING WITHOUT - * LIMITATION, ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR - * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL BROADCOM - * BE LIABLE TO THE END USER OR ANY THIRD PARTY FOR ANY LOSS OR - * DAMAGE, DIRECT OR INDIRECT, FROM THE USE OF THIS SOFTWARE, - * INCLUDING WITHOUT LIMITATION, LOST PROFITS, BUSINESS - * INTERRUPTION, GOODWILL, OR LOST DATA, EVEN IF BROADCOM IS - * EXPRESSLY ADVISED OF SUCH LOSS OR DAMAGE. + * Copyright Contributors to the Zowe Project. */ package org.zowe.apiml.cloudgatewayservice.filters; diff --git a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocator.java b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocator.java index 0b245dfb31..c0314ef935 100644 --- a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocator.java +++ b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocator.java @@ -1,24 +1,11 @@ /* - * Copyright (c) 2022 Broadcom. All Rights Reserved. The term - * "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html * - * This software and all information contained therein is - * confidential and proprietary and shall not be duplicated, - * used, disclosed, or disseminated in any way except as - * authorized by the applicable license agreement, without the - * express written permission of Broadcom. All authorized - * reproductions must be marked with this language. + * SPDX-License-Identifier: EPL-2.0 * - * EXCEPT AS SET FORTH IN THE APPLICABLE LICENSE AGREEMENT, TO - * THE EXTENT PERMITTED BY APPLICABLE LAW, BROADCOM PROVIDES THIS - * SOFTWARE WITHOUT WARRANTY OF ANY KIND, INCLUDING WITHOUT - * LIMITATION, ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR - * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL BROADCOM - * BE LIABLE TO THE END USER OR ANY THIRD PARTY FOR ANY LOSS OR - * DAMAGE, DIRECT OR INDIRECT, FROM THE USE OF THIS SOFTWARE, - * INCLUDING WITHOUT LIMITATION, LOST PROFITS, BUSINESS - * INTERRUPTION, GOODWILL, OR LOST DATA, EVEN IF BROADCOM IS - * EXPRESSLY ADVISED OF SUCH LOSS OR DAMAGE. + * Copyright Contributors to the Zowe Project. */ package org.zowe.apiml.cloudgatewayservice.service; @@ -64,7 +51,7 @@ public class RouteLocator implements RouteDefinitionLocator { private final List routeDefinitionProducers; private final Map schemeHandlers = new HashMap<>(); - @Getter(lazy=true, value = AccessLevel.PRIVATE) + @Getter(lazy = true, value = AccessLevel.PRIVATE) private final UrlBasedCorsConfigurationSource corsConfigurationSource = context.getBean(UrlBasedCorsConfigurationSource.class); public RouteLocator( diff --git a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByBasePath.java b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByBasePath.java index fbb2fe1d4c..390ff59a7c 100644 --- a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByBasePath.java +++ b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByBasePath.java @@ -1,25 +1,13 @@ /* - * Copyright (c) 2022 Broadcom. All Rights Reserved. The term - * "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html * - * This software and all information contained therein is - * confidential and proprietary and shall not be duplicated, - * used, disclosed, or disseminated in any way except as - * authorized by the applicable license agreement, without the - * express written permission of Broadcom. All authorized - * reproductions must be marked with this language. + * SPDX-License-Identifier: EPL-2.0 * - * EXCEPT AS SET FORTH IN THE APPLICABLE LICENSE AGREEMENT, TO - * THE EXTENT PERMITTED BY APPLICABLE LAW, BROADCOM PROVIDES THIS - * SOFTWARE WITHOUT WARRANTY OF ANY KIND, INCLUDING WITHOUT - * LIMITATION, ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR - * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL BROADCOM - * BE LIABLE TO THE END USER OR ANY THIRD PARTY FOR ANY LOSS OR - * DAMAGE, DIRECT OR INDIRECT, FROM THE USE OF THIS SOFTWARE, - * INCLUDING WITHOUT LIMITATION, LOST PROFITS, BUSINESS - * INTERRUPTION, GOODWILL, OR LOST DATA, EVEN IF BROADCOM IS - * EXPRESSLY ADVISED OF SUCH LOSS OR DAMAGE. + * Copyright Contributors to the Zowe Project. */ + package org.zowe.apiml.cloudgatewayservice.service.routing; import org.springframework.cloud.client.ServiceInstance; diff --git a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByHeader.java b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByHeader.java index b9db66fa0e..dbfe6b9cc7 100644 --- a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByHeader.java +++ b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByHeader.java @@ -1,24 +1,11 @@ /* - * Copyright (c) 2022 Broadcom. All Rights Reserved. The term - * "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html * - * This software and all information contained therein is - * confidential and proprietary and shall not be duplicated, - * used, disclosed, or disseminated in any way except as - * authorized by the applicable license agreement, without the - * express written permission of Broadcom. All authorized - * reproductions must be marked with this language. + * SPDX-License-Identifier: EPL-2.0 * - * EXCEPT AS SET FORTH IN THE APPLICABLE LICENSE AGREEMENT, TO - * THE EXTENT PERMITTED BY APPLICABLE LAW, BROADCOM PROVIDES THIS - * SOFTWARE WITHOUT WARRANTY OF ANY KIND, INCLUDING WITHOUT - * LIMITATION, ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR - * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL BROADCOM - * BE LIABLE TO THE END USER OR ANY THIRD PARTY FOR ANY LOSS OR - * DAMAGE, DIRECT OR INDIRECT, FROM THE USE OF THIS SOFTWARE, - * INCLUDING WITHOUT LIMITATION, LOST PROFITS, BUSINESS - * INTERRUPTION, GOODWILL, OR LOST DATA, EVEN IF BROADCOM IS - * EXPRESSLY ADVISED OF SUCH LOSS OR DAMAGE. + * Copyright Contributors to the Zowe Project. */ package org.zowe.apiml.cloudgatewayservice.service.routing; diff --git a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/RouteDefinitionProducer.java b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/RouteDefinitionProducer.java index 0b835c5e37..6d0eaf2878 100644 --- a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/RouteDefinitionProducer.java +++ b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/RouteDefinitionProducer.java @@ -1,24 +1,11 @@ /* - * Copyright (c) 2022 Broadcom. All Rights Reserved. The term - * "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html * - * This software and all information contained therein is - * confidential and proprietary and shall not be duplicated, - * used, disclosed, or disseminated in any way except as - * authorized by the applicable license agreement, without the - * express written permission of Broadcom. All authorized - * reproductions must be marked with this language. + * SPDX-License-Identifier: EPL-2.0 * - * EXCEPT AS SET FORTH IN THE APPLICABLE LICENSE AGREEMENT, TO - * THE EXTENT PERMITTED BY APPLICABLE LAW, BROADCOM PROVIDES THIS - * SOFTWARE WITHOUT WARRANTY OF ANY KIND, INCLUDING WITHOUT - * LIMITATION, ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR - * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL BROADCOM - * BE LIABLE TO THE END USER OR ANY THIRD PARTY FOR ANY LOSS OR - * DAMAGE, DIRECT OR INDIRECT, FROM THE USE OF THIS SOFTWARE, - * INCLUDING WITHOUT LIMITATION, LOST PROFITS, BUSINESS - * INTERRUPTION, GOODWILL, OR LOST DATA, EVEN IF BROADCOM IS - * EXPRESSLY ADVISED OF SUCH LOSS OR DAMAGE. + * Copyright Contributors to the Zowe Project. */ package org.zowe.apiml.cloudgatewayservice.service.routing; diff --git a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/scheme/HttpBasicPassticket.java b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/scheme/HttpBasicPassticket.java index a5f701285a..04843217d3 100644 --- a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/scheme/HttpBasicPassticket.java +++ b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/scheme/HttpBasicPassticket.java @@ -1,24 +1,11 @@ /* - * Copyright (c) 2022 Broadcom. All Rights Reserved. The term - * "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html * - * This software and all information contained therein is - * confidential and proprietary and shall not be duplicated, - * used, disclosed, or disseminated in any way except as - * authorized by the applicable license agreement, without the - * express written permission of Broadcom. All authorized - * reproductions must be marked with this language. + * SPDX-License-Identifier: EPL-2.0 * - * EXCEPT AS SET FORTH IN THE APPLICABLE LICENSE AGREEMENT, TO - * THE EXTENT PERMITTED BY APPLICABLE LAW, BROADCOM PROVIDES THIS - * SOFTWARE WITHOUT WARRANTY OF ANY KIND, INCLUDING WITHOUT - * LIMITATION, ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR - * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL BROADCOM - * BE LIABLE TO THE END USER OR ANY THIRD PARTY FOR ANY LOSS OR - * DAMAGE, DIRECT OR INDIRECT, FROM THE USE OF THIS SOFTWARE, - * INCLUDING WITHOUT LIMITATION, LOST PROFITS, BUSINESS - * INTERRUPTION, GOODWILL, OR LOST DATA, EVEN IF BROADCOM IS - * EXPRESSLY ADVISED OF SUCH LOSS OR DAMAGE. + * Copyright Contributors to the Zowe Project. */ package org.zowe.apiml.cloudgatewayservice.service.scheme; diff --git a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/scheme/SchemeHandler.java b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/scheme/SchemeHandler.java index 9652346d0b..f8b3341118 100644 --- a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/scheme/SchemeHandler.java +++ b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/scheme/SchemeHandler.java @@ -1,25 +1,13 @@ /* - * Copyright (c) 2022 Broadcom. All Rights Reserved. The term - * "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html * - * This software and all information contained therein is - * confidential and proprietary and shall not be duplicated, - * used, disclosed, or disseminated in any way except as - * authorized by the applicable license agreement, without the - * express written permission of Broadcom. All authorized - * reproductions must be marked with this language. + * SPDX-License-Identifier: EPL-2.0 * - * EXCEPT AS SET FORTH IN THE APPLICABLE LICENSE AGREEMENT, TO - * THE EXTENT PERMITTED BY APPLICABLE LAW, BROADCOM PROVIDES THIS - * SOFTWARE WITHOUT WARRANTY OF ANY KIND, INCLUDING WITHOUT - * LIMITATION, ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR - * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL BROADCOM - * BE LIABLE TO THE END USER OR ANY THIRD PARTY FOR ANY LOSS OR - * DAMAGE, DIRECT OR INDIRECT, FROM THE USE OF THIS SOFTWARE, - * INCLUDING WITHOUT LIMITATION, LOST PROFITS, BUSINESS - * INTERRUPTION, GOODWILL, OR LOST DATA, EVEN IF BROADCOM IS - * EXPRESSLY ADVISED OF SUCH LOSS OR DAMAGE. + * Copyright Contributors to the Zowe Project. */ + package org.zowe.apiml.cloudgatewayservice.service.scheme; import org.springframework.cloud.gateway.route.RouteDefinition; @@ -29,7 +17,7 @@ public interface SchemeHandler { AuthenticationScheme getAuthenticationScheme(); - + void apply(RouteDefinition routeDefinition, Authentication auth); - + } diff --git a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/scheme/X509.java b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/scheme/X509.java index 9904dacfc3..0613454ee8 100644 --- a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/scheme/X509.java +++ b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/scheme/X509.java @@ -1,24 +1,11 @@ /* - * Copyright (c) 2022 Broadcom. All Rights Reserved. The term - * "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html * - * This software and all information contained therein is - * confidential and proprietary and shall not be duplicated, - * used, disclosed, or disseminated in any way except as - * authorized by the applicable license agreement, without the - * express written permission of Broadcom. All authorized - * reproductions must be marked with this language. + * SPDX-License-Identifier: EPL-2.0 * - * EXCEPT AS SET FORTH IN THE APPLICABLE LICENSE AGREEMENT, TO - * THE EXTENT PERMITTED BY APPLICABLE LAW, BROADCOM PROVIDES THIS - * SOFTWARE WITHOUT WARRANTY OF ANY KIND, INCLUDING WITHOUT - * LIMITATION, ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR - * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL BROADCOM - * BE LIABLE TO THE END USER OR ANY THIRD PARTY FOR ANY LOSS OR - * DAMAGE, DIRECT OR INDIRECT, FROM THE USE OF THIS SOFTWARE, - * INCLUDING WITHOUT LIMITATION, LOST PROFITS, BUSINESS - * INTERRUPTION, GOODWILL, OR LOST DATA, EVEN IF BROADCOM IS - * EXPRESSLY ADVISED OF SUCH LOSS OR DAMAGE. + * Copyright Contributors to the Zowe Project. */ package org.zowe.apiml.cloudgatewayservice.service.scheme; diff --git a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/filters/HeaderRouteStepFilterFactoryTest.java b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/filters/HeaderRouteStepFilterFactoryTest.java index 7996b4a1ff..cfe90d3a57 100644 --- a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/filters/HeaderRouteStepFilterFactoryTest.java +++ b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/filters/HeaderRouteStepFilterFactoryTest.java @@ -1,24 +1,11 @@ /* - * Copyright (c) 2022 Broadcom. All Rights Reserved. The term - * "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html * - * This software and all information contained therein is - * confidential and proprietary and shall not be duplicated, - * used, disclosed, or disseminated in any way except as - * authorized by the applicable license agreement, without the - * express written permission of Broadcom. All authorized - * reproductions must be marked with this language. + * SPDX-License-Identifier: EPL-2.0 * - * EXCEPT AS SET FORTH IN THE APPLICABLE LICENSE AGREEMENT, TO - * THE EXTENT PERMITTED BY APPLICABLE LAW, BROADCOM PROVIDES THIS - * SOFTWARE WITHOUT WARRANTY OF ANY KIND, INCLUDING WITHOUT - * LIMITATION, ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR - * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL BROADCOM - * BE LIABLE TO THE END USER OR ANY THIRD PARTY FOR ANY LOSS OR - * DAMAGE, DIRECT OR INDIRECT, FROM THE USE OF THIS SOFTWARE, - * INCLUDING WITHOUT LIMITATION, LOST PROFITS, BUSINESS - * INTERRUPTION, GOODWILL, OR LOST DATA, EVEN IF BROADCOM IS - * EXPRESSLY ADVISED OF SUCH LOSS OR DAMAGE. + * Copyright Contributors to the Zowe Project. */ package org.zowe.apiml.cloudgatewayservice.filters; @@ -58,4 +45,4 @@ void test(String headerNameConfig, String headerName, String headerValueOrigin, }); } -} \ No newline at end of file +} diff --git a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByBasePathTest.java b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByBasePathTest.java index 757c068c26..b312c149c3 100644 --- a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByBasePathTest.java +++ b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByBasePathTest.java @@ -1,24 +1,11 @@ /* - * Copyright (c) 2022 Broadcom. All Rights Reserved. The term - * "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html * - * This software and all information contained therein is - * confidential and proprietary and shall not be duplicated, - * used, disclosed, or disseminated in any way except as - * authorized by the applicable license agreement, without the - * express written permission of Broadcom. All authorized - * reproductions must be marked with this language. + * SPDX-License-Identifier: EPL-2.0 * - * EXCEPT AS SET FORTH IN THE APPLICABLE LICENSE AGREEMENT, TO - * THE EXTENT PERMITTED BY APPLICABLE LAW, BROADCOM PROVIDES THIS - * SOFTWARE WITHOUT WARRANTY OF ANY KIND, INCLUDING WITHOUT - * LIMITATION, ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR - * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL BROADCOM - * BE LIABLE TO THE END USER OR ANY THIRD PARTY FOR ANY LOSS OR - * DAMAGE, DIRECT OR INDIRECT, FROM THE USE OF THIS SOFTWARE, - * INCLUDING WITHOUT LIMITATION, LOST PROFITS, BUSINESS - * INTERRUPTION, GOODWILL, OR LOST DATA, EVEN IF BROADCOM IS - * EXPRESSLY ADVISED OF SUCH LOSS OR DAMAGE. + * Copyright Contributors to the Zowe Project. */ package org.zowe.apiml.cloudgatewayservice.service.routing; @@ -110,4 +97,4 @@ void givenInstanceConfig_whenSetFilters_thenConstructRegexFilter( } -} \ No newline at end of file +} diff --git a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByHeaderTest.java b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByHeaderTest.java index ae502bb811..be7687cb13 100644 --- a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByHeaderTest.java +++ b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByHeaderTest.java @@ -1,24 +1,11 @@ /* - * Copyright (c) 2022 Broadcom. All Rights Reserved. The term - * "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html * - * This software and all information contained therein is - * confidential and proprietary and shall not be duplicated, - * used, disclosed, or disseminated in any way except as - * authorized by the applicable license agreement, without the - * express written permission of Broadcom. All authorized - * reproductions must be marked with this language. + * SPDX-License-Identifier: EPL-2.0 * - * EXCEPT AS SET FORTH IN THE APPLICABLE LICENSE AGREEMENT, TO - * THE EXTENT PERMITTED BY APPLICABLE LAW, BROADCOM PROVIDES THIS - * SOFTWARE WITHOUT WARRANTY OF ANY KIND, INCLUDING WITHOUT - * LIMITATION, ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR - * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL BROADCOM - * BE LIABLE TO THE END USER OR ANY THIRD PARTY FOR ANY LOSS OR - * DAMAGE, DIRECT OR INDIRECT, FROM THE USE OF THIS SOFTWARE, - * INCLUDING WITHOUT LIMITATION, LOST PROFITS, BUSINESS - * INTERRUPTION, GOODWILL, OR LOST DATA, EVEN IF BROADCOM IS - * EXPRESSLY ADVISED OF SUCH LOSS OR DAMAGE. + * Copyright Contributors to the Zowe Project. */ package org.zowe.apiml.cloudgatewayservice.service.routing; @@ -81,4 +68,4 @@ void givenInstanceConfig_whenSetFilters_thenConstructHeaderRouteStepFilter() { } -} \ No newline at end of file +} diff --git a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/routing/RouteDefinitionProducerTest.java b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/routing/RouteDefinitionProducerTest.java index ee3dbf5f28..da10e9d47e 100644 --- a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/routing/RouteDefinitionProducerTest.java +++ b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/routing/RouteDefinitionProducerTest.java @@ -1,24 +1,11 @@ /* - * Copyright (c) 2022 Broadcom. All Rights Reserved. The term - * "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html * - * This software and all information contained therein is - * confidential and proprietary and shall not be duplicated, - * used, disclosed, or disseminated in any way except as - * authorized by the applicable license agreement, without the - * express written permission of Broadcom. All authorized - * reproductions must be marked with this language. + * SPDX-License-Identifier: EPL-2.0 * - * EXCEPT AS SET FORTH IN THE APPLICABLE LICENSE AGREEMENT, TO - * THE EXTENT PERMITTED BY APPLICABLE LAW, BROADCOM PROVIDES THIS - * SOFTWARE WITHOUT WARRANTY OF ANY KIND, INCLUDING WITHOUT - * LIMITATION, ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR - * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL BROADCOM - * BE LIABLE TO THE END USER OR ANY THIRD PARTY FOR ANY LOSS OR - * DAMAGE, DIRECT OR INDIRECT, FROM THE USE OF THIS SOFTWARE, - * INCLUDING WITHOUT LIMITATION, LOST PROFITS, BUSINESS - * INTERRUPTION, GOODWILL, OR LOST DATA, EVEN IF BROADCOM IS - * EXPRESSLY ADVISED OF SUCH LOSS OR DAMAGE. + * Copyright Contributors to the Zowe Project. */ package org.zowe.apiml.cloudgatewayservice.service.routing; @@ -119,4 +106,4 @@ void givenInstanceData_whenGet_thenConstructDraftOfRule() { } -} \ No newline at end of file +} diff --git a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/scheme/HttpBasicPassticketTest.java b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/scheme/HttpBasicPassticketTest.java index c608f1917e..0fe4929e28 100644 --- a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/scheme/HttpBasicPassticketTest.java +++ b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/scheme/HttpBasicPassticketTest.java @@ -1,24 +1,11 @@ /* - * Copyright (c) 2022 Broadcom. All Rights Reserved. The term - * "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html * - * This software and all information contained therein is - * confidential and proprietary and shall not be duplicated, - * used, disclosed, or disseminated in any way except as - * authorized by the applicable license agreement, without the - * express written permission of Broadcom. All authorized - * reproductions must be marked with this language. + * SPDX-License-Identifier: EPL-2.0 * - * EXCEPT AS SET FORTH IN THE APPLICABLE LICENSE AGREEMENT, TO - * THE EXTENT PERMITTED BY APPLICABLE LAW, BROADCOM PROVIDES THIS - * SOFTWARE WITHOUT WARRANTY OF ANY KIND, INCLUDING WITHOUT - * LIMITATION, ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR - * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL BROADCOM - * BE LIABLE TO THE END USER OR ANY THIRD PARTY FOR ANY LOSS OR - * DAMAGE, DIRECT OR INDIRECT, FROM THE USE OF THIS SOFTWARE, - * INCLUDING WITHOUT LIMITATION, LOST PROFITS, BUSINESS - * INTERRUPTION, GOODWILL, OR LOST DATA, EVEN IF BROADCOM IS - * EXPRESSLY ADVISED OF SUCH LOSS OR DAMAGE. + * Copyright Contributors to the Zowe Project. */ package org.zowe.apiml.cloudgatewayservice.service.scheme; @@ -52,4 +39,4 @@ void givenInstance_whenApply_thenFulfillFilterFactorArgs() { assertEquals("PassticketFilterFactory", filterDefinition.getName()); } -} \ No newline at end of file +} diff --git a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/scheme/X509Test.java b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/scheme/X509Test.java index cbeac97548..0dfa464bc3 100644 --- a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/scheme/X509Test.java +++ b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/scheme/X509Test.java @@ -1,24 +1,11 @@ /* - * Copyright (c) 2022 Broadcom. All Rights Reserved. The term - * "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html * - * This software and all information contained therein is - * confidential and proprietary and shall not be duplicated, - * used, disclosed, or disseminated in any way except as - * authorized by the applicable license agreement, without the - * express written permission of Broadcom. All authorized - * reproductions must be marked with this language. + * SPDX-License-Identifier: EPL-2.0 * - * EXCEPT AS SET FORTH IN THE APPLICABLE LICENSE AGREEMENT, TO - * THE EXTENT PERMITTED BY APPLICABLE LAW, BROADCOM PROVIDES THIS - * SOFTWARE WITHOUT WARRANTY OF ANY KIND, INCLUDING WITHOUT - * LIMITATION, ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR - * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL BROADCOM - * BE LIABLE TO THE END USER OR ANY THIRD PARTY FOR ANY LOSS OR - * DAMAGE, DIRECT OR INDIRECT, FROM THE USE OF THIS SOFTWARE, - * INCLUDING WITHOUT LIMITATION, LOST PROFITS, BUSINESS - * INTERRUPTION, GOODWILL, OR LOST DATA, EVEN IF BROADCOM IS - * EXPRESSLY ADVISED OF SUCH LOSS OR DAMAGE. + * Copyright Contributors to the Zowe Project. */ package org.zowe.apiml.cloudgatewayservice.service.scheme; @@ -52,4 +39,4 @@ void givenX509_whenApply_thenFulfillFilterFactorArgs() { assertEquals("X509FilterFactory", filterDefinition.getName()); } -} \ No newline at end of file +} From 92988dee78d7af6a260450aeb3b184a86465364b Mon Sep 17 00:00:00 2001 From: Elena Kubantseva Date: Mon, 14 Aug 2023 17:24:18 +0200 Subject: [PATCH 12/27] fix few more checkstyle errors Signed-off-by: Elena Kubantseva --- .../cloudgatewayservice/config/ConnectionsConfigTest.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/config/ConnectionsConfigTest.java b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/config/ConnectionsConfigTest.java index f91b096c3e..317441f8f4 100644 --- a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/config/ConnectionsConfigTest.java +++ b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/config/ConnectionsConfigTest.java @@ -20,16 +20,10 @@ import org.mockito.Mock; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient; -import org.springframework.cloud.gateway.discovery.DiscoveryLocatorProperties; -import org.springframework.cloud.gateway.filter.FilterDefinition; import org.springframework.context.annotation.ComponentScan; import org.springframework.test.util.ReflectionTestUtils; -import java.util.Collections; - import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.mock; @SpringBootTest @ComponentScan(basePackages = "org.zowe.apiml.cloudgatewayservice") From 24de00efc53d14f4145d5c832b8d22f9c5e1ad4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jare=C5=A1?= Date: Tue, 15 Aug 2023 17:36:05 +0200 Subject: [PATCH 13/27] hotfix routing of gateway services - custom routing rules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pavel Jareš --- .../service/RouteLocator.java | 19 ++++++++++++---- .../service/RouteLocatorTest.java | 22 +++++++++++++++---- .../src/main/resources/application.yml | 3 --- 3 files changed, 33 insertions(+), 11 deletions(-) diff --git a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocator.java b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocator.java index c0314ef935..dfb2fdc360 100644 --- a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocator.java +++ b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocator.java @@ -36,12 +36,15 @@ import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; +import java.util.stream.Stream; import static org.zowe.apiml.constants.EurekaMetadataDefinition.APIML_ID; @Service public class RouteLocator implements RouteDefinitionLocator { + private static final EurekaMetadataParser metadataParser = new EurekaMetadataParser(); + private final ApplicationContext context; private final CorsUtils corsUtils; @@ -98,9 +101,19 @@ void setCors(ServiceInstance serviceInstance) { }); } + Stream getRoutedService(ServiceInstance serviceInstance) { + // FIXME: this is till the SCGW and GW uses the same DS. The rouing rules should be different for each application + if (org.apache.commons.lang.StringUtils.equalsIgnoreCase("GATEWAY", serviceInstance.getServiceId())) { + return Stream.of(new RoutedService("zuul", "", "/")); + } + + return metadataParser.parseToListRoute(serviceInstance.getMetadata()).stream() + // sorting avoid a conflict with the more general pattern + .sorted(Comparator.comparingInt(x -> StringUtils.removeFirstAndLastOccurrence(x.getGatewayUrl(), "/").length()).reversed()); + } + @Override public Flux getRouteDefinitions() { - EurekaMetadataParser metadataParser = new EurekaMetadataParser(); AtomicInteger order = new AtomicInteger(); /** * It generates each rule for each combination of instance x routing x generator ({@link RouteDefinitionProducer}) @@ -113,9 +126,7 @@ public Flux getRouteDefinitions() { return getServiceInstances().flatMap(Flux::fromIterable).map(serviceInstance -> { Authentication auth = metadataParser.parseAuthentication(serviceInstance.getMetadata()); setCors(serviceInstance); - return metadataParser.parseToListRoute(serviceInstance.getMetadata()).stream() - // sorting avoid a conflict with the more general pattern - .sorted(Comparator.comparingInt(x -> StringUtils.removeFirstAndLastOccurrence(x.getGatewayUrl(), "/").length()).reversed()) + return getRoutedService(serviceInstance) .map(routedService -> routeDefinitionProducers.stream() .sorted(Comparator.comparingInt(x -> x.getOrder())) diff --git a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocatorTest.java b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocatorTest.java index 00e28253de..eaeed546c6 100644 --- a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocatorTest.java +++ b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocatorTest.java @@ -31,11 +31,9 @@ import org.zowe.apiml.util.CorsUtils; import reactor.core.publisher.Flux; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; +import java.util.*; import java.util.function.Consumer; +import java.util.stream.Collectors; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; @@ -190,6 +188,22 @@ void givenNoApimlId_whenSetCors_thenServiceIdIsUsed() { verify(urlBasedCorsConfigurationSource).registerCorsConfiguration("/myservice/**", null); } + @Test + void givenGateway_whenGetRoutedService_thenReturnDefaultRouting() { + ServiceInstance gw = createServiceInstance("gateway", "api/v1"); + List rs = routeLocator.getRoutedService(gw).collect(Collectors.toList()); + assertEquals(1, rs.size()); + assertEquals("", rs.get(0).getGatewayUrl()); + assertEquals("/", rs.get(0).getServiceUrl()); + } + + @Test + void givenNonGatewayService_whenGetRoutedService_thenReturnRoutingFromMetadata() { + ServiceInstance s = createServiceInstance("myservice", "api/v1", "ui/v1"); + List rs = routeLocator.getRoutedService(s).collect(Collectors.toList()); + assertEquals(2, rs.size()); + } + } @Nested diff --git a/gateway-service/src/main/resources/application.yml b/gateway-service/src/main/resources/application.yml index c089c117ff..4b60506a47 100644 --- a/gateway-service/src/main/resources/application.yml +++ b/gateway-service/src/main/resources/application.yml @@ -244,9 +244,6 @@ eureka: api_v1: gatewayUrl: /api/v1 serviceUrl: /gateway - zuul: - gatewayUrl: / - serviceUrl: / apiInfo: - apiId: zowe.apiml.gateway version: 1.0.0 From fb8d76515675f5562b342ecf361d96feff666db7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jare=C5=A1?= Date: Tue, 15 Aug 2023 17:36:47 +0200 Subject: [PATCH 14/27] fix support empty gatewayUrl in routing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pavel Jareš --- .../gateway/routing/ApimlRouteLocator.java | 2 ++ .../gateway/routing/NewApimlRouteLocator.java | 18 ++++++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/gateway-service/src/main/java/org/zowe/apiml/gateway/routing/ApimlRouteLocator.java b/gateway-service/src/main/java/org/zowe/apiml/gateway/routing/ApimlRouteLocator.java index 0697b146f4..f4c454d79c 100644 --- a/gateway-service/src/main/java/org/zowe/apiml/gateway/routing/ApimlRouteLocator.java +++ b/gateway-service/src/main/java/org/zowe/apiml/gateway/routing/ApimlRouteLocator.java @@ -20,6 +20,7 @@ import org.zowe.apiml.eurekaservice.client.util.EurekaMetadataParser; import org.zowe.apiml.message.log.ApimlLogger; import org.zowe.apiml.product.logging.annotations.InjectApimlLogger; +import org.zowe.apiml.product.routing.RoutedService; import org.zowe.apiml.product.routing.RoutedServices; import org.zowe.apiml.product.routing.RoutedServicesUser; @@ -149,6 +150,7 @@ private List createRouteKeys(List serviceInstance, .flatMap( metadata -> eurekaMetadataParser.parseToListRoute(metadata).stream() ) + .sorted(Comparator.comparingInt(x -> x.getGatewayUrl().length()).reversed()) .forEach(routedService -> { keys.add("/" + mapRouteToService(serviceId) + "/" + routedService.getGatewayUrl() + "/**"); routes.addRoutedService(routedService); diff --git a/gateway-service/src/main/java/org/zowe/apiml/gateway/routing/NewApimlRouteLocator.java b/gateway-service/src/main/java/org/zowe/apiml/gateway/routing/NewApimlRouteLocator.java index 97e3607928..25e5e21fbe 100644 --- a/gateway-service/src/main/java/org/zowe/apiml/gateway/routing/NewApimlRouteLocator.java +++ b/gateway-service/src/main/java/org/zowe/apiml/gateway/routing/NewApimlRouteLocator.java @@ -12,6 +12,7 @@ import com.google.common.annotations.VisibleForTesting; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.cloud.netflix.zuul.filters.ZuulProperties; @@ -20,6 +21,7 @@ import org.zowe.apiml.product.routing.RoutedService; import org.zowe.apiml.product.routing.RoutedServices; +import java.util.Comparator; import java.util.LinkedHashMap; import java.util.Map; @@ -60,9 +62,11 @@ private Map createServiceRoutes(String service .map(ServiceInstance::getMetadata) .flatMap( metadata -> metadataParser.parseToListRoute(metadata).stream() - ).forEach( routedService -> { - routesMap.putAll(buildRoute(serviceId, routedService)); - routedServices.addRoutedService(routedService); + ) + .sorted(Comparator.comparingInt(x -> x.getGatewayUrl().length()).reversed()) + .forEach( routedService -> { + routesMap.putAll(buildRoute(serviceId, routedService)); + routedServices.addRoutedService(routedService); }); routedServicesNotifier.addRoutedServices(serviceId, routedServices); @@ -72,7 +76,13 @@ private Map createServiceRoutes(String service private Map buildRoute(String serviceId, RoutedService routedService) { LinkedHashMap routesMap = new LinkedHashMap<>(); - String routeKey = "/" + serviceId + "/" + routedService.getGatewayUrl() + "/**"; + StringBuilder sb = new StringBuilder(); + sb.append('/').append(serviceId).append('/'); + if (!StringUtils.isEmpty(routedService.getGatewayUrl())) { + sb.append(routedService.getGatewayUrl()).append('/'); + } + sb.append("**"); + String routeKey = sb.toString(); ZuulProperties.ZuulRoute routeFormat = new ZuulProperties.ZuulRoute(routeKey, serviceId); routesMap.put(routeKey, routeFormat); From 97bc80641a24cdb775b7e6ae1f80fadef43b5d5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jare=C5=A1?= Date: Tue, 15 Aug 2023 18:56:24 +0200 Subject: [PATCH 15/27] fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pavel Jareš --- .../acceptance/RequestInstanceTest.java | 4 ++-- .../acceptance/RetryPerServiceTest.java | 17 ++++++++++------- .../acceptance/common/MetadataBuilder.java | 2 +- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/RequestInstanceTest.java b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/RequestInstanceTest.java index a8969779ab..4a6e72e7bd 100644 --- a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/RequestInstanceTest.java +++ b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/RequestInstanceTest.java @@ -40,7 +40,7 @@ class WhenValidInstanceId { @Test void routeToCorrectService() { given() - .header("X-Request-Id", "serviceid1localhost") + .header("X-Request-Id", "serviceid1") .when() .get(basePath + serviceWithCustomConfiguration.getPath()) .then().statusCode(Matchers.is(SC_OK)); @@ -65,7 +65,7 @@ void routeToServiceWithCorsDisabled() { given() .header("Origin", "https://localhost:3000") - .header("X-Request-Id", "serviceid1localhost") + .header("X-Request-Id", "serviceid1") .when() .get(basePath + serviceWithCustomConfiguration.getPath()) .then().statusCode(Matchers.is(SC_FORBIDDEN)); diff --git a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/RetryPerServiceTest.java b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/RetryPerServiceTest.java index e270788b06..7f414ba533 100644 --- a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/RetryPerServiceTest.java +++ b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/RetryPerServiceTest.java @@ -27,6 +27,7 @@ @AcceptanceTest class RetryPerServiceTest extends AcceptanceTestWithTwoServices { + Consumer dummyConsumer = (headers -> { }); @@ -36,7 +37,7 @@ class GivenRetryOnAllOperationsIsDisabled { void whenGetReturnsUnavailable_thenRetry() throws Exception { AtomicInteger counter = mockServerWithSpecificHttpResponse(503, "/serviceid2/test", 0, dummyConsumer, "".getBytes()); given() - .header("X-Request-Id", "serviceid2localhost") + .header("X-Request-Id", "serviceid2") .when() .get(basePath + serviceWithDefaultConfiguration.getPath()) .then().statusCode(is(SC_SERVICE_UNAVAILABLE)); @@ -47,31 +48,31 @@ void whenGetReturnsUnavailable_thenRetry() throws Exception { void whenRequestReturnsUnauthorized_thenDontRetry() throws Exception { AtomicInteger counter = mockServerWithSpecificHttpResponse(401, "/serviceid2/test", 0, dummyConsumer, "".getBytes()); given() - .header("X-Request-Id", "serviceid2localhost") + .header("X-Request-Id", "serviceid2") .when() .get(basePath + serviceWithDefaultConfiguration.getPath()) .then().statusCode(is(SC_UNAUTHORIZED)); assertEquals(1, counter.get()); given() - .header("X-Request-Id", "serviceid2localhost") + .header("X-Request-Id", "serviceid2") .when() .post(basePath + serviceWithDefaultConfiguration.getPath()) .then().statusCode(is(SC_UNAUTHORIZED)); assertEquals(2, counter.get()); given() - .header("X-Request-Id", "serviceid2localhost") + .header("X-Request-Id", "serviceid2") .when() .put(basePath + serviceWithDefaultConfiguration.getPath()) .then().statusCode(is(SC_UNAUTHORIZED)); assertEquals(3, counter.get()); given() - .header("X-Request-Id", "serviceid2localhost") + .header("X-Request-Id", "serviceid2") .when() .delete(basePath + serviceWithDefaultConfiguration.getPath()) .then().statusCode(is(SC_UNAUTHORIZED)); assertEquals(4, counter.get()); given() - .header("X-Request-Id", "serviceid2localhost") + .header("X-Request-Id", "serviceid2") .when() .patch(basePath + serviceWithDefaultConfiguration.getPath()) .then().statusCode(is(SC_UNAUTHORIZED)); @@ -82,11 +83,13 @@ void whenRequestReturnsUnauthorized_thenDontRetry() throws Exception { void whenPostReturnsUnavailable_thenDontRetry() throws Exception { AtomicInteger counter = mockServerWithSpecificHttpResponse(503, "/serviceid2/test", 0, dummyConsumer, "".getBytes()); given() - .header("X-Request-Id", "serviceid2localhost") + .header("X-Request-Id", "serviceid2") .when() .post(basePath + serviceWithDefaultConfiguration.getPath()) .then().statusCode(is(SC_SERVICE_UNAVAILABLE)); assertEquals(1, counter.get()); } + } + } diff --git a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/common/MetadataBuilder.java b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/common/MetadataBuilder.java index 4a5b49f738..e5025599d1 100644 --- a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/common/MetadataBuilder.java +++ b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/common/MetadataBuilder.java @@ -31,7 +31,7 @@ public Map build() { public static MetadataBuilder defaultInstance() { MetadataBuilder builder = new MetadataBuilder(); builder.metadata.put("apiml.corsEnabled", "true"); - builder.metadata.put("apiml.authentication.scheme","httpBasicPassTicket"); + builder.metadata.put("apiml.authentication.scheme", "httpBasicPassTicket"); return builder; } From c24ba5806e287d71cd719f3b4e41cd801288e33d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jare=C5=A1?= Date: Wed, 16 Aug 2023 12:58:06 +0200 Subject: [PATCH 16/27] fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pavel Jareš --- .../config/DiscoveryClientTestConfig.java | 6 ------ .../netflix/ApimlDiscoveryClientStub.java | 13 +++++++++++-- .../netflix/ApimlDiscoveryClientStub.java | 13 +++++++++++-- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/config/DiscoveryClientTestConfig.java b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/config/DiscoveryClientTestConfig.java index 1712a2acc2..de4152f8eb 100644 --- a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/config/DiscoveryClientTestConfig.java +++ b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/config/DiscoveryClientTestConfig.java @@ -35,7 +35,6 @@ import org.zowe.apiml.cloudgatewayservice.acceptance.common.Service; import org.zowe.apiml.cloudgatewayservice.acceptance.netflix.ApimlDiscoveryClientStub; import org.zowe.apiml.cloudgatewayservice.acceptance.netflix.ApplicationRegistry; -import org.zowe.apiml.cloudgatewayservice.service.InstanceInfoService; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; @@ -114,9 +113,4 @@ private EurekaJerseyClient eurekaJerseyClient(ApplicationRegistry registry, Stri return jerseyClient; } - - @Bean - InstanceInfoService instanceInfoService() { - return mock(InstanceInfoService.class); - } } diff --git a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/netflix/ApimlDiscoveryClientStub.java b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/netflix/ApimlDiscoveryClientStub.java index 9973d49827..ed77b6d519 100644 --- a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/netflix/ApimlDiscoveryClientStub.java +++ b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/netflix/ApimlDiscoveryClientStub.java @@ -15,10 +15,12 @@ import com.netflix.discovery.AbstractDiscoveryClientOptionalArgs; import com.netflix.discovery.EurekaClientConfig; import com.netflix.discovery.shared.Applications; +import org.apache.commons.lang3.StringUtils; import org.springframework.cloud.netflix.eureka.CloudEurekaClient; import org.springframework.context.ApplicationEventPublisher; import java.util.List; +import java.util.stream.Collectors; public class ApimlDiscoveryClientStub extends CloudEurekaClient { private ApplicationRegistry applicationRegistry; @@ -40,11 +42,18 @@ public Applications getApplications() { @Override public List getInstancesByVipAddress(String vipAddress, boolean secure) { - return applicationRegistry.getInstances(); + return applicationRegistry.getInstances().stream() + .filter(x -> StringUtils.equalsAnyIgnoreCase(vipAddress, x.getVIPAddress())) + .filter(x -> !secure || x.getSecurePort() != 0) + .collect(Collectors.toList()); } @Override public List getInstancesByVipAddress(String vipAddress, boolean secure, String region) { - return applicationRegistry.getInstances(); + return applicationRegistry.getInstances().stream() + .filter(x -> StringUtils.equalsAnyIgnoreCase(vipAddress, x.getVIPAddress())) + .filter(x -> !secure || x.getSecurePort() != 0) + .collect(Collectors.toList()); } + } diff --git a/gateway-service/src/test/java/org/zowe/apiml/acceptance/netflix/ApimlDiscoveryClientStub.java b/gateway-service/src/test/java/org/zowe/apiml/acceptance/netflix/ApimlDiscoveryClientStub.java index 4d9a9497dd..3d28f63b84 100644 --- a/gateway-service/src/test/java/org/zowe/apiml/acceptance/netflix/ApimlDiscoveryClientStub.java +++ b/gateway-service/src/test/java/org/zowe/apiml/acceptance/netflix/ApimlDiscoveryClientStub.java @@ -15,10 +15,12 @@ import com.netflix.discovery.AbstractDiscoveryClientOptionalArgs; import com.netflix.discovery.EurekaClientConfig; import com.netflix.discovery.shared.Applications; +import org.apache.commons.lang3.StringUtils; import org.springframework.context.ApplicationEventPublisher; import org.zowe.apiml.gateway.discovery.ApimlDiscoveryClient; import java.util.List; +import java.util.stream.Collectors; public class ApimlDiscoveryClientStub extends ApimlDiscoveryClient { private ApplicationRegistry applicationRegistry; @@ -44,11 +46,18 @@ public Applications getApplications() { @Override public List getInstancesByVipAddress(String vipAddress, boolean secure) { - return applicationRegistry.getInstances(); + return applicationRegistry.getInstances().stream() + .filter(x -> StringUtils.equalsAnyIgnoreCase(vipAddress, x.getVIPAddress())) + .filter(x -> !secure || x.getSecurePort() != 0) + .collect(Collectors.toList()); } @Override public List getInstancesByVipAddress(String vipAddress, boolean secure, String region) { - return applicationRegistry.getInstances(); + return applicationRegistry.getInstances().stream() + .filter(x -> StringUtils.equalsAnyIgnoreCase(vipAddress, x.getVIPAddress())) + .filter(x -> !secure || x.getSecurePort() != 0) + .collect(Collectors.toList()); } + } From 55d33b3cf5855ed8e335423d39f5eae891d61638 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jare=C5=A1?= Date: Wed, 16 Aug 2023 13:15:37 +0200 Subject: [PATCH 17/27] fix CORS unit test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pavel Jareš --- .../cloudgatewayservice/acceptance/RequestInstanceTest.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/RequestInstanceTest.java b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/RequestInstanceTest.java index 4a6e72e7bd..916fd208ca 100644 --- a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/RequestInstanceTest.java +++ b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/RequestInstanceTest.java @@ -15,6 +15,7 @@ import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.TestPropertySource; import org.zowe.apiml.cloudgatewayservice.acceptance.common.AcceptanceTest; import org.zowe.apiml.cloudgatewayservice.acceptance.common.AcceptanceTestWithTwoServices; @@ -26,6 +27,9 @@ @AcceptanceTest @ActiveProfiles("test") +@TestPropertySource(properties = { + "apiml.service.corsEnabled=false" +}) class RequestInstanceTest extends AcceptanceTestWithTwoServices { @BeforeEach From 5c035c3e28ccf70fa7db104c25900ce1b26238d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jare=C5=A1?= Date: Wed, 16 Aug 2023 14:13:02 +0200 Subject: [PATCH 18/27] fix cloud spring gateway tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pavel Jareš --- .../zowe/apiml/cloudgatewayservice/service/RouteLocator.java | 2 +- .../apiml/cloudgatewayservice/acceptance/PassticketTest.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocator.java b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocator.java index dfb2fdc360..8ad57bd733 100644 --- a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocator.java +++ b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocator.java @@ -96,7 +96,7 @@ void setCors(ServiceInstance serviceInstance) { serviceInstance.getServiceId().toLowerCase(), serviceInstance.getMetadata(), (prefix, serviceId, config) -> { - serviceId = serviceInstance.getMetadata().getOrDefault(APIML_ID, serviceInstance.getServiceId()); + serviceId = serviceInstance.getMetadata().getOrDefault(APIML_ID, serviceInstance.getServiceId().toLowerCase()); getCorsConfigurationSource().registerCorsConfiguration("/" + serviceId + "/**", config); }); } diff --git a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/PassticketTest.java b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/PassticketTest.java index 6a56600d6e..38c0bd458c 100644 --- a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/PassticketTest.java +++ b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/PassticketTest.java @@ -19,7 +19,7 @@ import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.mockito.Mockito; -import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.netflix.eureka.EurekaServiceInstance; import org.springframework.test.context.DynamicPropertyRegistry; @@ -41,7 +41,7 @@ @AcceptanceTest public class PassticketTest extends AcceptanceTestWithTwoServices { - @Autowired + @MockBean InstanceInfoService instanceInfoService; @DynamicPropertySource From d416d4bdb1058a61d9aed9feb4e1b849773a29c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jare=C5=A1?= Date: Wed, 16 Aug 2023 14:28:20 +0200 Subject: [PATCH 19/27] fix API gateway tests by removing fix in testing class - some tests depend on the bug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pavel Jareš --- .../netflix/ApimlDiscoveryClientStub.java | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/gateway-service/src/test/java/org/zowe/apiml/acceptance/netflix/ApimlDiscoveryClientStub.java b/gateway-service/src/test/java/org/zowe/apiml/acceptance/netflix/ApimlDiscoveryClientStub.java index 3d28f63b84..4d9a9497dd 100644 --- a/gateway-service/src/test/java/org/zowe/apiml/acceptance/netflix/ApimlDiscoveryClientStub.java +++ b/gateway-service/src/test/java/org/zowe/apiml/acceptance/netflix/ApimlDiscoveryClientStub.java @@ -15,12 +15,10 @@ import com.netflix.discovery.AbstractDiscoveryClientOptionalArgs; import com.netflix.discovery.EurekaClientConfig; import com.netflix.discovery.shared.Applications; -import org.apache.commons.lang3.StringUtils; import org.springframework.context.ApplicationEventPublisher; import org.zowe.apiml.gateway.discovery.ApimlDiscoveryClient; import java.util.List; -import java.util.stream.Collectors; public class ApimlDiscoveryClientStub extends ApimlDiscoveryClient { private ApplicationRegistry applicationRegistry; @@ -46,18 +44,11 @@ public Applications getApplications() { @Override public List getInstancesByVipAddress(String vipAddress, boolean secure) { - return applicationRegistry.getInstances().stream() - .filter(x -> StringUtils.equalsAnyIgnoreCase(vipAddress, x.getVIPAddress())) - .filter(x -> !secure || x.getSecurePort() != 0) - .collect(Collectors.toList()); + return applicationRegistry.getInstances(); } @Override public List getInstancesByVipAddress(String vipAddress, boolean secure, String region) { - return applicationRegistry.getInstances().stream() - .filter(x -> StringUtils.equalsAnyIgnoreCase(vipAddress, x.getVIPAddress())) - .filter(x -> !secure || x.getSecurePort() != 0) - .collect(Collectors.toList()); + return applicationRegistry.getInstances(); } - } From 2da55869e8578f6b0f942ade54b7aea8f5294803 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jare=C5=A1?= Date: Wed, 16 Aug 2023 14:31:46 +0200 Subject: [PATCH 20/27] fix CorsPerServiceTest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pavel Jareš --- .../cloudgatewayservice/acceptance/CorsPerServiceTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/CorsPerServiceTest.java b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/CorsPerServiceTest.java index 2fddd180b5..748aad50a0 100644 --- a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/CorsPerServiceTest.java +++ b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/CorsPerServiceTest.java @@ -32,7 +32,7 @@ void routeToServiceWithCorsEnabled() throws IOException { ); given() .header("Origin", "https://localhost:3000") - .header("X-Request-Id", "serviceid2localhost") + .header("X-Request-Id", "serviceid2") .when() .get(basePath + serviceWithDefaultConfiguration.getPath()) .then().statusCode(Matchers.is(SC_OK)); From eee41b11310b02150c59419ca905718183e2e0fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jare=C5=A1?= Date: Wed, 16 Aug 2023 15:44:51 +0200 Subject: [PATCH 21/27] fix tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pavel Jareš --- config/docker/gateway-service.yml | 1 + .../integration/proxy/CloudGatewayProxyTest.java | 13 ++++++------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/config/docker/gateway-service.yml b/config/docker/gateway-service.yml index 3b1fb57358..3de27bd441 100644 --- a/config/docker/gateway-service.yml +++ b/config/docker/gateway-service.yml @@ -1,5 +1,6 @@ apiml: service: + apimlId: apiml1 hostname: gateway-service discoveryServiceUrls: https://discovery-service:10011/eureka/ security: diff --git a/integration-tests/src/test/java/org/zowe/apiml/integration/proxy/CloudGatewayProxyTest.java b/integration-tests/src/test/java/org/zowe/apiml/integration/proxy/CloudGatewayProxyTest.java index 62f70db56d..bd415546a3 100644 --- a/integration-tests/src/test/java/org/zowe/apiml/integration/proxy/CloudGatewayProxyTest.java +++ b/integration-tests/src/test/java/org/zowe/apiml/integration/proxy/CloudGatewayProxyTest.java @@ -37,9 +37,9 @@ void givenRequestHeader_thenRouteToProvidedHost() throws URISyntaxException { RestAssured.useRelaxedHTTPSValidation(); String scgUrl = String.format("%s://%s:%s/%s", conf.getScheme(), conf.getHost(), conf.getPort(), "gateway/version"); - given().header("X-Request-Id", "gatewaygateway-service") + given().header("X-Request-Id", "apiml1") .get(new URI(scgUrl)).then().statusCode(200); - given().header("X-Request-Id", "gatewaygateway-service-2") + given().header("X-Request-Id", "apiml1") .get(new URI(scgUrl)).then().statusCode(200); } @@ -49,11 +49,10 @@ void givenRequestTimeoutIsReached_thenDropConnection() { String scgUrl = String.format("%s://%s:%s%s?%s=%d", conf.getScheme(), conf.getHost(), conf.getPort(), DISCOVERABLE_GREET, "delayMs", DEFAULT_TIMEOUT + SECOND); assertTimeout(Duration.ofMillis(DEFAULT_TIMEOUT * 3), () -> { given() - .header("X-Request-Id", "discoverableclientdiscoverable-client") - .when() - .get(scgUrl - ) - .then() + .header("X-Request-Id", "discoverableclient") + .when() + .get(scgUrl) + .then() .statusCode(HttpStatus.SC_GATEWAY_TIMEOUT); }); } From ff63e281fb2c98ae14fd18c73088cd8f8348b874 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jare=C5=A1?= Date: Thu, 17 Aug 2023 09:36:00 +0200 Subject: [PATCH 22/27] fix Sonar issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pavel Jareš --- .../zowe/apiml/cloudgatewayservice/service/RouteLocator.java | 4 ++-- .../service/routing/RouteDefinitionProducer.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocator.java b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocator.java index 8ad57bd733..27ef20c0a4 100644 --- a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocator.java +++ b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocator.java @@ -31,7 +31,7 @@ import reactor.core.publisher.Flux; import java.util.Comparator; -import java.util.HashMap; +import java.util.EnumMap; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; @@ -52,7 +52,7 @@ public class RouteLocator implements RouteDefinitionLocator { private final List commonFilters; private final List routeDefinitionProducers; - private final Map schemeHandlers = new HashMap<>(); + private final Map schemeHandlers = new EnumMap<>(AuthenticationScheme.class); @Getter(lazy = true, value = AccessLevel.PRIVATE) private final UrlBasedCorsConfigurationSource corsConfigurationSource = context.getBean(UrlBasedCorsConfigurationSource.class); diff --git a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/RouteDefinitionProducer.java b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/RouteDefinitionProducer.java index 6d0eaf2878..91d5bc94da 100644 --- a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/RouteDefinitionProducer.java +++ b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/RouteDefinitionProducer.java @@ -33,7 +33,7 @@ public abstract class RouteDefinitionProducer { protected final SimpleEvaluationContext evalCtxt = SimpleEvaluationContext.forReadOnlyDataBinding().withInstanceMethods().build(); protected final Expression urlExpr; - public RouteDefinitionProducer(DiscoveryLocatorProperties properties) { + protected RouteDefinitionProducer(DiscoveryLocatorProperties properties) { SpelExpressionParser parser = new SpelExpressionParser(); urlExpr = parser.parseExpression(properties.getUrlExpression()); } From 212c613c0e2a47dacfbd100f3c4d5e8937ab9760 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jare=C5=A1?= Date: Thu, 17 Aug 2023 10:07:07 +0200 Subject: [PATCH 23/27] JavaDoc update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pavel Jareš --- .../filters/HeaderRouteStepFilterFactory.java | 10 +++++ .../service/RouteLocator.java | 20 ++++++---- .../service/routing/ByBasePath.java | 7 ++++ .../service/routing/ByHeader.java | 6 +++ .../routing/RouteDefinitionProducer.java | 40 ++++++++++++++++++- .../service/scheme/SchemeHandler.java | 11 +++++ 6 files changed, 85 insertions(+), 9 deletions(-) diff --git a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/filters/HeaderRouteStepFilterFactory.java b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/filters/HeaderRouteStepFilterFactory.java index 31d418e6cb..067a3513da 100644 --- a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/filters/HeaderRouteStepFilterFactory.java +++ b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/filters/HeaderRouteStepFilterFactory.java @@ -15,6 +15,16 @@ import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; import org.springframework.stereotype.Service; +/** + * This filter is responsible to update request header during the routing. The header contain a locator to which server + * route. It could contain also multiple steps separated by /. + * + * In the case header contain multiple steps the filter remove just the first part, otherwise it remove header at all. + * + * Examples: + * "step1/step2/step3" > "step2/step3" + * "node" > null (removed) + */ @Service public class HeaderRouteStepFilterFactory extends AbstractGatewayFilterFactory { diff --git a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocator.java b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocator.java index 27ef20c0a4..bf04c35894 100644 --- a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocator.java +++ b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocator.java @@ -112,25 +112,29 @@ Stream getRoutedService(ServiceInstance serviceInstance) { .sorted(Comparator.comparingInt(x -> StringUtils.removeFirstAndLastOccurrence(x.getGatewayUrl(), "/").length()).reversed()); } + /** + * It generates each rule for each combination of instance x routing x generator ({@link RouteDefinitionProducer}) + * The routes are sorted by serviceUrl to avoid clashing between multiple levels of paths, ie. / vs. /a. + * Sorting routes and generators by order allows to redefine order of each rule. There is no possible to have + * multiple valid rules for the same case at one moment. + * + * @return routing rules + */ @Override public Flux getRouteDefinitions() { AtomicInteger order = new AtomicInteger(); - /** - * It generates each rule for each combination of instance x routing x generator ({@link RouteDefinitionProducer}) - * - * The routes are sorted by serviceUrl to avoid clashing between multiple levels of paths, ie. / vs. /a. - * - * Sorting routes and generators by order allows to redefine order of each rule. There is no possible to have - * multiple valid rules for the same case at one moment. - */ + // iterate over services return getServiceInstances().flatMap(Flux::fromIterable).map(serviceInstance -> { Authentication auth = metadataParser.parseAuthentication(serviceInstance.getMetadata()); + // configure CORS for the service (if necessary) setCors(serviceInstance); + // iterate over routing definition (ordered from the longest one to match with the most specific) return getRoutedService(serviceInstance) .map(routedService -> routeDefinitionProducers.stream() .sorted(Comparator.comparingInt(x -> x.getOrder())) .map(rdp -> { + // generate a new routing rule by a specific produces RouteDefinition routeDefinition = rdp.get(serviceInstance, routedService); routeDefinition.setOrder(order.getAndIncrement()); routeDefinition.getFilters().addAll(commonFilters); diff --git a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByBasePath.java b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByBasePath.java index 390ff59a7c..fd6735c368 100644 --- a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByBasePath.java +++ b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByBasePath.java @@ -19,6 +19,13 @@ import org.zowe.apiml.product.routing.RoutedService; import org.zowe.apiml.util.StringUtils; +/** + * Routing rule by path modify the path of the request. It makes this replacement: + * + * from: /// + * to: // + * + */ @Component public class ByBasePath extends RouteDefinitionProducer { diff --git a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByHeader.java b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByHeader.java index dbfe6b9cc7..2ae0b108b8 100644 --- a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByHeader.java +++ b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByHeader.java @@ -18,6 +18,12 @@ import org.springframework.stereotype.Component; import org.zowe.apiml.product.routing.RoutedService; +/** + * The routing rule by header. It uses a header with name {@link ByHeader#TARGET_HEADER_NAME} and make rule routing by + * it. It looks for a first part of routing steps and redirect by it (it could contain multiple steps separated by /). + * + * The rule modify the header for next hop (remove first part of remove the whole one if there is just one part). + */ @Component public class ByHeader extends RouteDefinitionProducer { diff --git a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/RouteDefinitionProducer.java b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/RouteDefinitionProducer.java index 91d5bc94da..f0ce78eff3 100644 --- a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/RouteDefinitionProducer.java +++ b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/RouteDefinitionProducer.java @@ -28,6 +28,11 @@ import static org.zowe.apiml.constants.EurekaMetadataDefinition.APIML_ID; import static org.zowe.apiml.constants.EurekaMetadataDefinition.SERVICE_EXTERNAL_URL; +/** + * The class that extends this abstract class is responsible to generate routing rule for a specific service and routing. + * + * The producers define an order ({@link #getOrder()}). It allows to create multiple rules with a prioritization. + */ public abstract class RouteDefinitionProducer { protected final SimpleEvaluationContext evalCtxt = SimpleEvaluationContext.forReadOnlyDataBinding().withInstanceMethods().build(); @@ -42,6 +47,12 @@ protected String evalHostname(ServiceInstance serviceInstance) { return urlExpr.getValue(this.evalCtxt, serviceInstance, String.class); } + /** + * The method returns URL to route. In the case the service define URL by attribute apiml.service.externalUrl it is + * reused. Otherwise, it generates default URL such as lb:// + * @param serviceInstance instance of service to process + * @return URL for loadbalancer (without path) + */ protected String getHostname(ServiceInstance serviceInstance) { String output = null; Map metadata = serviceInstance.getMetadata(); @@ -79,12 +90,35 @@ protected RouteDefinition buildRouteDefinition(ServiceInstance serviceInstance, return routeDefinition; } + /** + * Define priority of the producer. A producer with lower number is processed before other one. + * @return order defines the priority of producer + */ public abstract int getOrder(); + /** + * Generates the condition which define if the rule could be used. Usually its checking if request is matching + * to the routing rule (by path, header, etc.) + * @param routeDefinition the rule created by this bean + * @param serviceInstance instance of service + * @param routedService routing definition + */ protected abstract void setCondition(RouteDefinition routeDefinition, ServiceInstance serviceInstance, RoutedService routedService); + /** + * Generates the action during routing for the service. It could be for example modification of path, header, etc. + * @param routeDefinition the rule created by this bean + * @param serviceInstance instance of service + * @param routedService routing definition + */ protected abstract void setFilters(RouteDefinition routeDefinition, ServiceInstance serviceInstance, RoutedService routedService); + /** + * It creates a rule for specific input. + * @param serviceInstance instance of service + * @param routedService routing definition + * @return definition of routing rule + */ public RouteDefinition get(ServiceInstance serviceInstance, RoutedService routedService) { serviceInstance = getEvalServiceInstance(serviceInstance); RouteDefinition routeDefinition = buildRouteDefinition(serviceInstance, routedService.getSubServiceId()); @@ -95,6 +129,10 @@ public RouteDefinition get(ServiceInstance serviceInstance, RoutedService routed return routeDefinition; } + /** + * This class is responsible to update ServiceInstance without changing data in the origin. It can change + * the serviceId. It is helpful if we want to use a different identifier instead of serviceId, ie. ID of APIML. + */ @RequiredArgsConstructor static class ServiceInstanceEval implements ServiceInstance { @@ -107,7 +145,7 @@ public String getServiceId() { return evalServiceId; } - private static interface Overridden { + private interface Overridden { String getServiceId(); } diff --git a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/scheme/SchemeHandler.java b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/scheme/SchemeHandler.java index f8b3341118..32cac7f6de 100644 --- a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/scheme/SchemeHandler.java +++ b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/scheme/SchemeHandler.java @@ -14,10 +14,21 @@ import org.zowe.apiml.auth.Authentication; import org.zowe.apiml.auth.AuthenticationScheme; +/** + * This interface is used by handler which add filter to transform credentials in the request. + */ public interface SchemeHandler { + /** + * @return type of supported authentication scheme + */ AuthenticationScheme getAuthenticationScheme(); + /** + * Set filter in the routing rule. + * @param routeDefinition rule to be updated + * @param auth definition of authentication scheme from the service instance + */ void apply(RouteDefinition routeDefinition, Authentication auth); } From cff6248c3c81e5bd51207229e5e197d5ac9c76a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jare=C5=A1?= Date: Thu, 17 Aug 2023 10:17:38 +0200 Subject: [PATCH 24/27] rename header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pavel Jareš --- .../service/routing/ByHeader.java | 2 +- .../acceptance/CorsPerServiceTest.java | 4 +++- .../acceptance/RequestInstanceTest.java | 8 +++++--- .../acceptance/RetryPerServiceTest.java | 17 ++++++++++------- .../service/routing/ByHeaderTest.java | 2 +- .../proxy/CloudGatewayProxyTest.java | 8 +++++--- 6 files changed, 25 insertions(+), 16 deletions(-) diff --git a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByHeader.java b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByHeader.java index 2ae0b108b8..f5c3d59a85 100644 --- a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByHeader.java +++ b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByHeader.java @@ -27,7 +27,7 @@ @Component public class ByHeader extends RouteDefinitionProducer { - private static final String TARGET_HEADER_NAME = "X-Request-Id"; + private static final String TARGET_HEADER_NAME = "X-Forward-To"; public ByHeader(DiscoveryLocatorProperties properties) { super(properties); diff --git a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/CorsPerServiceTest.java b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/CorsPerServiceTest.java index 748aad50a0..18067aec1b 100644 --- a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/CorsPerServiceTest.java +++ b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/CorsPerServiceTest.java @@ -24,6 +24,8 @@ @AcceptanceTest class CorsPerServiceTest extends AcceptanceTestWithTwoServices { + private static final String HEADER_X_FORWARD_TO = "X-Forward-To"; + @Test void routeToServiceWithCorsEnabled() throws IOException { mockServerWithSpecificHttpResponse(200, "/serviceid2/test", 0, (headers) -> @@ -32,7 +34,7 @@ void routeToServiceWithCorsEnabled() throws IOException { ); given() .header("Origin", "https://localhost:3000") - .header("X-Request-Id", "serviceid2") + .header(HEADER_X_FORWARD_TO, "serviceid2") .when() .get(basePath + serviceWithDefaultConfiguration.getPath()) .then().statusCode(Matchers.is(SC_OK)); diff --git a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/RequestInstanceTest.java b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/RequestInstanceTest.java index 916fd208ca..9d208c4845 100644 --- a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/RequestInstanceTest.java +++ b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/RequestInstanceTest.java @@ -32,6 +32,8 @@ }) class RequestInstanceTest extends AcceptanceTestWithTwoServices { + private static final String HEADER_X_FORWARD_TO = "X-Forward-To"; + @BeforeEach void setUp() throws IOException { mockServerWithSpecificHttpResponse(200, "/serviceid1/test", 0, (headers) -> { @@ -44,7 +46,7 @@ class WhenValidInstanceId { @Test void routeToCorrectService() { given() - .header("X-Request-Id", "serviceid1") + .header(HEADER_X_FORWARD_TO, "serviceid1") .when() .get(basePath + serviceWithCustomConfiguration.getPath()) .then().statusCode(Matchers.is(SC_OK)); @@ -56,7 +58,7 @@ class WhenNonExistingInstanceId { @Test void cantRouteToServer() { given() - .header("X-Request-Id", "non-existing"). + .header(HEADER_X_FORWARD_TO, "non-existing"). when() .get(basePath + serviceWithCustomConfiguration.getPath()) .then() @@ -69,7 +71,7 @@ void routeToServiceWithCorsDisabled() { given() .header("Origin", "https://localhost:3000") - .header("X-Request-Id", "serviceid1") + .header(HEADER_X_FORWARD_TO, "serviceid1") .when() .get(basePath + serviceWithCustomConfiguration.getPath()) .then().statusCode(Matchers.is(SC_FORBIDDEN)); diff --git a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/RetryPerServiceTest.java b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/RetryPerServiceTest.java index 7f414ba533..85756c64ef 100644 --- a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/RetryPerServiceTest.java +++ b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/RetryPerServiceTest.java @@ -16,6 +16,7 @@ import org.zowe.apiml.cloudgatewayservice.acceptance.common.AcceptanceTest; import org.zowe.apiml.cloudgatewayservice.acceptance.common.AcceptanceTestWithTwoServices; +import javax.ws.rs.HEAD; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; @@ -28,6 +29,8 @@ @AcceptanceTest class RetryPerServiceTest extends AcceptanceTestWithTwoServices { + private static final String HEADER_X_FORWARD_TO = "X-Forward-To"; + Consumer dummyConsumer = (headers -> { }); @@ -37,7 +40,7 @@ class GivenRetryOnAllOperationsIsDisabled { void whenGetReturnsUnavailable_thenRetry() throws Exception { AtomicInteger counter = mockServerWithSpecificHttpResponse(503, "/serviceid2/test", 0, dummyConsumer, "".getBytes()); given() - .header("X-Request-Id", "serviceid2") + .header(HEADER_X_FORWARD_TO, "serviceid2") .when() .get(basePath + serviceWithDefaultConfiguration.getPath()) .then().statusCode(is(SC_SERVICE_UNAVAILABLE)); @@ -48,31 +51,31 @@ void whenGetReturnsUnavailable_thenRetry() throws Exception { void whenRequestReturnsUnauthorized_thenDontRetry() throws Exception { AtomicInteger counter = mockServerWithSpecificHttpResponse(401, "/serviceid2/test", 0, dummyConsumer, "".getBytes()); given() - .header("X-Request-Id", "serviceid2") + .header(HEADER_X_FORWARD_TO, "serviceid2") .when() .get(basePath + serviceWithDefaultConfiguration.getPath()) .then().statusCode(is(SC_UNAUTHORIZED)); assertEquals(1, counter.get()); given() - .header("X-Request-Id", "serviceid2") + .header(HEADER_X_FORWARD_TO, "serviceid2") .when() .post(basePath + serviceWithDefaultConfiguration.getPath()) .then().statusCode(is(SC_UNAUTHORIZED)); assertEquals(2, counter.get()); given() - .header("X-Request-Id", "serviceid2") + .header(HEADER_X_FORWARD_TO, "serviceid2") .when() .put(basePath + serviceWithDefaultConfiguration.getPath()) .then().statusCode(is(SC_UNAUTHORIZED)); assertEquals(3, counter.get()); given() - .header("X-Request-Id", "serviceid2") + .header(HEADER_X_FORWARD_TO, "serviceid2") .when() .delete(basePath + serviceWithDefaultConfiguration.getPath()) .then().statusCode(is(SC_UNAUTHORIZED)); assertEquals(4, counter.get()); given() - .header("X-Request-Id", "serviceid2") + .header(HEADER_X_FORWARD_TO, "serviceid2") .when() .patch(basePath + serviceWithDefaultConfiguration.getPath()) .then().statusCode(is(SC_UNAUTHORIZED)); @@ -83,7 +86,7 @@ void whenRequestReturnsUnauthorized_thenDontRetry() throws Exception { void whenPostReturnsUnavailable_thenDontRetry() throws Exception { AtomicInteger counter = mockServerWithSpecificHttpResponse(503, "/serviceid2/test", 0, dummyConsumer, "".getBytes()); given() - .header("X-Request-Id", "serviceid2") + .header(HEADER_X_FORWARD_TO, "serviceid2") .when() .post(basePath + serviceWithDefaultConfiguration.getPath()) .then().statusCode(is(SC_SERVICE_UNAVAILABLE)); diff --git a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByHeaderTest.java b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByHeaderTest.java index be7687cb13..47f3237541 100644 --- a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByHeaderTest.java +++ b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/routing/ByHeaderTest.java @@ -37,7 +37,7 @@ void givenInstance_whenOrder_thenReturnsTheDefaultOrder() { @Nested class RuleConstruction { - private static final String TARGET_HEADER_NAME = "X-Request-Id"; + private static final String TARGET_HEADER_NAME = "X-Forward-To"; @Test void givenInstanceConfig_whenSetCondition_thenConstructRegexCondition() { diff --git a/integration-tests/src/test/java/org/zowe/apiml/integration/proxy/CloudGatewayProxyTest.java b/integration-tests/src/test/java/org/zowe/apiml/integration/proxy/CloudGatewayProxyTest.java index bd415546a3..f7cf69ba8f 100644 --- a/integration-tests/src/test/java/org/zowe/apiml/integration/proxy/CloudGatewayProxyTest.java +++ b/integration-tests/src/test/java/org/zowe/apiml/integration/proxy/CloudGatewayProxyTest.java @@ -30,6 +30,8 @@ class CloudGatewayProxyTest { private static final int SECOND = 1000; private static final int DEFAULT_TIMEOUT = 2 * SECOND; + private static final String HEADER_X_FORWARD_TO = "X-Forward-To"; + static CloudGatewayConfiguration conf = ConfigReader.environmentConfiguration().getCloudGatewayConfiguration(); @Test @@ -37,9 +39,9 @@ void givenRequestHeader_thenRouteToProvidedHost() throws URISyntaxException { RestAssured.useRelaxedHTTPSValidation(); String scgUrl = String.format("%s://%s:%s/%s", conf.getScheme(), conf.getHost(), conf.getPort(), "gateway/version"); - given().header("X-Request-Id", "apiml1") + given().header(HEADER_X_FORWARD_TO, "apiml1") .get(new URI(scgUrl)).then().statusCode(200); - given().header("X-Request-Id", "apiml1") + given().header(HEADER_X_FORWARD_TO, "apiml1") .get(new URI(scgUrl)).then().statusCode(200); } @@ -49,7 +51,7 @@ void givenRequestTimeoutIsReached_thenDropConnection() { String scgUrl = String.format("%s://%s:%s%s?%s=%d", conf.getScheme(), conf.getHost(), conf.getPort(), DISCOVERABLE_GREET, "delayMs", DEFAULT_TIMEOUT + SECOND); assertTimeout(Duration.ofMillis(DEFAULT_TIMEOUT * 3), () -> { given() - .header("X-Request-Id", "discoverableclient") + .header(HEADER_X_FORWARD_TO, "discoverableclient") .when() .get(scgUrl) .then() From c2c5d16f4b2ee5b28fb0da16b64a6fa9c41c507a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jare=C5=A1?= Date: Thu, 17 Aug 2023 10:37:44 +0200 Subject: [PATCH 25/27] fix constant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pavel Jareš --- .../java/org/zowe/apiml/constants/EurekaMetadataDefinition.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common-service-core/src/main/java/org/zowe/apiml/constants/EurekaMetadataDefinition.java b/common-service-core/src/main/java/org/zowe/apiml/constants/EurekaMetadataDefinition.java index 0456d290a5..22c456b3c5 100644 --- a/common-service-core/src/main/java/org/zowe/apiml/constants/EurekaMetadataDefinition.java +++ b/common-service-core/src/main/java/org/zowe/apiml/constants/EurekaMetadataDefinition.java @@ -32,7 +32,7 @@ private EurekaMetadataDefinition() { public static final String SERVICE_TITLE = "apiml.service.title"; public static final String SERVICE_DESCRIPTION = "apiml.service.description"; public static final String SERVICE_EXTERNAL_URL = "apiml.service.externalUrl"; - public static final String APIML_ID = "apiml.service.id"; + public static final String APIML_ID = "apiml.service.apimlId"; public static final String API_INFO = "apiml.apiInfo"; public static final String API_INFO_API_ID = "apiId"; From 0511b4bc8c6191e4d2a8c89d53aca649b51ccac9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jare=C5=A1?= Date: Thu, 17 Aug 2023 12:56:06 +0200 Subject: [PATCH 26/27] remove unused import MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pavel Jareš --- .../cloudgatewayservice/acceptance/RetryPerServiceTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/RetryPerServiceTest.java b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/RetryPerServiceTest.java index 85756c64ef..5a86e531ab 100644 --- a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/RetryPerServiceTest.java +++ b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/RetryPerServiceTest.java @@ -16,7 +16,6 @@ import org.zowe.apiml.cloudgatewayservice.acceptance.common.AcceptanceTest; import org.zowe.apiml.cloudgatewayservice.acceptance.common.AcceptanceTestWithTwoServices; -import javax.ws.rs.HEAD; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; From cb3e02e6f2aebb76e774b0ca9344afd7b7b493c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jare=C5=A1?= Date: Tue, 22 Aug 2023 14:34:58 +0200 Subject: [PATCH 27/27] code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pavel Jareš --- .../zowe/apiml/cloudgatewayservice/service/RouteLocator.java | 2 +- .../apiml/cloudgatewayservice/service/RouteLocatorTest.java | 4 ++-- .../service/scheme/HttpBasicPassticketTest.java | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocator.java b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocator.java index bf04c35894..2ff178df0a 100644 --- a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocator.java +++ b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocator.java @@ -102,7 +102,7 @@ void setCors(ServiceInstance serviceInstance) { } Stream getRoutedService(ServiceInstance serviceInstance) { - // FIXME: this is till the SCGW and GW uses the same DS. The rouing rules should be different for each application + // TODO: this is till the SCGW and GW uses the same DS. The routing rules should be different for each application if (org.apache.commons.lang.StringUtils.equalsIgnoreCase("GATEWAY", serviceInstance.getServiceId())) { return Stream.of(new RoutedService("zuul", "", "/")); } diff --git a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocatorTest.java b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocatorTest.java index eaeed546c6..f7c2735c0e 100644 --- a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocatorTest.java +++ b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/RouteLocatorTest.java @@ -120,7 +120,7 @@ private static RouteDefinitionProducer createRouteDefinitionProducer(int order, class CommonMethods { @Test - void givenDiscoveryClient_whenGetServiceInstances_thenMapThem() { + void givenDiscoveryClient_whenGetServiceInstances_thenReturnAllServiceInstances() { when(discoveryClient.getServices()).thenReturn(Flux.fromArray(new String[] {"service1", "service2"})); ServiceInstance serviceInstance1 = mock(ServiceInstance.class); ServiceInstance serviceInstance2 = mock(ServiceInstance.class); @@ -145,7 +145,7 @@ void givenNoAuthenticationScheme_whenSetAuth_thenDoNothing() { } @Test - void givenUnknownAuthenticationScheme_whenSetAuth_thenDoNothing() { + void givenAuthenticationSchemeWithoutFilter_whenSetAuth_thenDoNothing() { assertDoesNotThrow(() -> routeLocator.setAuth(null, new Authentication(AuthenticationScheme.X509, null))); } diff --git a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/scheme/HttpBasicPassticketTest.java b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/scheme/HttpBasicPassticketTest.java index 0fe4929e28..23808694a4 100644 --- a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/scheme/HttpBasicPassticketTest.java +++ b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/service/scheme/HttpBasicPassticketTest.java @@ -21,12 +21,12 @@ class HttpBasicPassticketTest { @Test - void givenInstance_whenGetAuthenticationScheme_thenReturnProperType() { + void givenHttpBasicPassticketInstance_whenGetAuthenticationScheme_thenReturnProperType() { assertEquals(AuthenticationScheme.HTTP_BASIC_PASSTICKET, new HttpBasicPassticket().getAuthenticationScheme()); } @Test - void givenInstance_whenApply_thenFulfillFilterFactorArgs() { + void givenRouteDefinition_whenApply_thenFulfillFilterFactorArgs() { RouteDefinition routeDefinition = new RouteDefinition(); Authentication authentication = new Authentication(); authentication.setApplid("applid");