From 66dd285d815337d93f17d3e2f33b1fe633bd28b5 Mon Sep 17 00:00:00 2001 From: Evgenii Stetsenko Date: Wed, 18 Mar 2026 14:57:12 +0300 Subject: [PATCH] feat: do not post routes if mesh type is istio --- core-quarkus-extensions/build-parent/pom.xml | 2 +- .../routes-registrator/deployment/pom.xml | 4 + .../routes-registrator/runtime/pom.xml | 4 + .../route/RouteRegistrationConfig.java | 11 ++- ...uteRegistrationProcessorConfiguration.java | 22 ++++- .../route-registration-common/pom.xml | 5 + .../common/gateway/route/ServiceMeshType.java | 6 ++ .../common/gateway/route/Utils.java | 2 +- .../common/service/TopologyConfigService.java | 78 +++++++++++++++ .../service/TopologyConfigServiceTest.java | 94 +++++++++++++++++++ 10 files changed, 220 insertions(+), 8 deletions(-) create mode 100644 core-rest-libraries/route-registration/route-registration-common/src/main/java/com/netcracker/cloud/routesregistration/common/gateway/route/ServiceMeshType.java create mode 100644 core-rest-libraries/route-registration/route-registration-common/src/main/java/com/netcracker/cloud/routesregistration/common/service/TopologyConfigService.java create mode 100644 core-rest-libraries/route-registration/route-registration-common/src/test/java/com/netcracker/cloud/routesregistration/common/service/TopologyConfigServiceTest.java diff --git a/core-quarkus-extensions/build-parent/pom.xml b/core-quarkus-extensions/build-parent/pom.xml index 92f2b3355..2687544f4 100644 --- a/core-quarkus-extensions/build-parent/pom.xml +++ b/core-quarkus-extensions/build-parent/pom.xml @@ -35,7 +35,7 @@ 3.6.2 0.8.7 3.5.3 - 7.0.0 + 7.0.1-SNAPSHOT 11.4.1 3.4.1 diff --git a/core-quarkus-extensions/routes-registrator/deployment/pom.xml b/core-quarkus-extensions/routes-registrator/deployment/pom.xml index e39dfe16b..c1d18e40f 100644 --- a/core-quarkus-extensions/routes-registrator/deployment/pom.xml +++ b/core-quarkus-extensions/routes-registrator/deployment/pom.xml @@ -28,6 +28,10 @@ com.fasterxml.jackson.core jackson-databind + + io.quarkus + quarkus-jackson-deployment + com.netcracker.cloud.quarkus diff --git a/core-quarkus-extensions/routes-registrator/runtime/pom.xml b/core-quarkus-extensions/routes-registrator/runtime/pom.xml index 5f8349f65..73b2acefa 100644 --- a/core-quarkus-extensions/routes-registrator/runtime/pom.xml +++ b/core-quarkus-extensions/routes-registrator/runtime/pom.xml @@ -22,6 +22,10 @@ com.squareup.okhttp3 okhttp + + io.quarkus + quarkus-jackson + com.netcracker.cloud.quarkus diff --git a/core-quarkus-extensions/routes-registrator/runtime/src/main/java/com/netcracker/cloud/quarkus/routesregistration/runtime/gateway/route/RouteRegistrationConfig.java b/core-quarkus-extensions/routes-registrator/runtime/src/main/java/com/netcracker/cloud/quarkus/routesregistration/runtime/gateway/route/RouteRegistrationConfig.java index fd1bd9ea1..ce7e8b20a 100644 --- a/core-quarkus-extensions/routes-registrator/runtime/src/main/java/com/netcracker/cloud/quarkus/routesregistration/runtime/gateway/route/RouteRegistrationConfig.java +++ b/core-quarkus-extensions/routes-registrator/runtime/src/main/java/com/netcracker/cloud/quarkus/routesregistration/runtime/gateway/route/RouteRegistrationConfig.java @@ -1,9 +1,11 @@ package com.netcracker.cloud.quarkus.routesregistration.runtime.gateway.route; +import com.fasterxml.jackson.databind.ObjectMapper; import com.netcracker.cloud.quarkus.security.auth.M2MManager; import com.netcracker.cloud.routesregistration.common.gateway.route.*; import com.netcracker.cloud.routesregistration.common.gateway.route.rest.RegistrationRequestFactory; import com.netcracker.cloud.routesregistration.common.gateway.route.transformation.RouteTransformer; +import com.netcracker.cloud.routesregistration.common.service.TopologyConfigService; import com.netcracker.cloud.security.core.auth.Token; import io.quarkus.arc.Unremovable; import io.reactivex.Scheduler; @@ -125,7 +127,9 @@ RegistrationRequestFactory registrationRequestFactory() { RoutesRestRegistrationProcessor routesRestRegistrationProcessor(ControlPlaneClient controlPlaneClient, RouteRetryManager retryManager, RouteTransformer routeTransformer, - RegistrationRequestFactory registrationRequestFactory) { + RegistrationRequestFactory registrationRequestFactory, + TopologyConfigService topologyConfigService) { + return new RoutesRestRegistrationProcessor( controlPlaneClient, retryManager, @@ -138,6 +142,11 @@ cloudServiceName, microserviceName, getPort(), "/", postRoutesAppnameDisabled) ); } + @Produces + TopologyConfigService topologyConfigService(ObjectMapper objectMapper) { + return new TopologyConfigService(objectMapper); + } + private String getPort() { return microservicePort; } diff --git a/core-rest-libraries/route-registration/route-registration-common-spring/src/main/java/com/netcracker/cloud/routesregistration/common/config/RouteRegistrationProcessorConfiguration.java b/core-rest-libraries/route-registration/route-registration-common-spring/src/main/java/com/netcracker/cloud/routesregistration/common/config/RouteRegistrationProcessorConfiguration.java index 095d0e38d..1d3eb1cf3 100644 --- a/core-rest-libraries/route-registration/route-registration-common-spring/src/main/java/com/netcracker/cloud/routesregistration/common/config/RouteRegistrationProcessorConfiguration.java +++ b/core-rest-libraries/route-registration/route-registration-common-spring/src/main/java/com/netcracker/cloud/routesregistration/common/config/RouteRegistrationProcessorConfiguration.java @@ -1,15 +1,17 @@ package com.netcracker.cloud.routesregistration.common.config; +import com.fasterxml.jackson.databind.ObjectMapper; import com.netcracker.cloud.restclient.MicroserviceRestClient; -import io.reactivex.Scheduler; -import io.reactivex.schedulers.Schedulers; import com.netcracker.cloud.routesregistration.common.annotation.processing.RouteHostMapping; import com.netcracker.cloud.routesregistration.common.gateway.route.*; import com.netcracker.cloud.routesregistration.common.gateway.route.rest.RegistrationRequestFactory; import com.netcracker.cloud.routesregistration.common.gateway.route.transformation.RouteTransformer; +import com.netcracker.cloud.routesregistration.common.service.TopologyConfigService; import com.netcracker.cloud.routesregistration.common.spring.gateway.route.RouteAnnotationProcessor; import com.netcracker.cloud.routesregistration.common.spring.gateway.route.RouteFormatter; import com.netcracker.cloud.routesregistration.common.spring.gateway.route.SpringControlPlaneClient; +import io.reactivex.Scheduler; +import io.reactivex.schedulers.Schedulers; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.ApplicationContext; @@ -20,6 +22,8 @@ import java.util.List; import java.util.Optional; +import static com.netcracker.cloud.routesregistration.common.gateway.route.ServiceMeshType.ISTIO; + @Configuration public class RouteRegistrationProcessorConfiguration { private final String microserviceName; @@ -47,7 +51,7 @@ public RouteRegistrationProcessorConfiguration(@Value("${cloud.microservice.name this.postRoutesEnabled = postRoutesEnabled; this.cloudServiceName = microserviceName; - if(deploymentVersion != null && !deploymentVersion.isEmpty()){ + if (deploymentVersion != null && !deploymentVersion.isEmpty()) { this.cloudServiceName += "-" + deploymentVersion; } } @@ -98,7 +102,9 @@ RouteTransformer routeTransformer() { RoutesRestRegistrationProcessor routesRestRegistrationProcessor(ControlPlaneClient controlPlaneClient, RouteRetryManager routeRetryManager, RouteTransformer routeTransformer, - RegistrationRequestFactory registrationRequestFactory) { + RegistrationRequestFactory registrationRequestFactory, + TopologyConfigService topologyConfigService) { + String microserviceInternalURL = Utils.formatMicroserviceInternalURL( cloudServiceName, microserviceName, @@ -111,7 +117,8 @@ RoutesRestRegistrationProcessor routesRestRegistrationProcessor(ControlPlaneClie routeRetryManager, routeTransformer, registrationRequestFactory, - postRoutesEnabled, + postRoutesEnabled + && topologyConfigService.getServiceMeshType() != ISTIO, microserviceName, microserviceInternalURL); } @@ -130,4 +137,9 @@ RouteFormatter routeFormatter() { RoutesRegistrationDelayProvider routesRegistrationDelayProvider() { return new RoutesRegistrationDelayProvider(); } + + @Bean + TopologyConfigService topologyConfigService(ObjectMapper objectMapper) { + return new TopologyConfigService(objectMapper); + } } diff --git a/core-rest-libraries/route-registration/route-registration-common/pom.xml b/core-rest-libraries/route-registration/route-registration-common/pom.xml index 521d64cf9..67e54d8f5 100644 --- a/core-rest-libraries/route-registration/route-registration-common/pom.xml +++ b/core-rest-libraries/route-registration/route-registration-common/pom.xml @@ -31,6 +31,11 @@ org.slf4j slf4j-api + + com.fasterxml.jackson.core + jackson-databind + 2.20.2 + org.junit.jupiter diff --git a/core-rest-libraries/route-registration/route-registration-common/src/main/java/com/netcracker/cloud/routesregistration/common/gateway/route/ServiceMeshType.java b/core-rest-libraries/route-registration/route-registration-common/src/main/java/com/netcracker/cloud/routesregistration/common/gateway/route/ServiceMeshType.java new file mode 100644 index 000000000..d366f18b2 --- /dev/null +++ b/core-rest-libraries/route-registration/route-registration-common/src/main/java/com/netcracker/cloud/routesregistration/common/gateway/route/ServiceMeshType.java @@ -0,0 +1,6 @@ +package com.netcracker.cloud.routesregistration.common.gateway.route; + +public enum ServiceMeshType { + CORE, + ISTIO +} diff --git a/core-rest-libraries/route-registration/route-registration-common/src/main/java/com/netcracker/cloud/routesregistration/common/gateway/route/Utils.java b/core-rest-libraries/route-registration/route-registration-common/src/main/java/com/netcracker/cloud/routesregistration/common/gateway/route/Utils.java index 292c8376f..0f909a4c1 100644 --- a/core-rest-libraries/route-registration/route-registration-common/src/main/java/com/netcracker/cloud/routesregistration/common/gateway/route/Utils.java +++ b/core-rest-libraries/route-registration/route-registration-common/src/main/java/com/netcracker/cloud/routesregistration/common/gateway/route/Utils.java @@ -12,7 +12,7 @@ public static String formatMicroserviceInternalURL(String cloudServiceName, String contextPath, boolean postRoutesAppnameDisabled) { String host = microserviceName; - if (cloudServiceName != null &&! cloudServiceName.isEmpty()) { + if (cloudServiceName != null && !cloudServiceName.isEmpty()) { host = cloudServiceName; } diff --git a/core-rest-libraries/route-registration/route-registration-common/src/main/java/com/netcracker/cloud/routesregistration/common/service/TopologyConfigService.java b/core-rest-libraries/route-registration/route-registration-common/src/main/java/com/netcracker/cloud/routesregistration/common/service/TopologyConfigService.java new file mode 100644 index 000000000..2568d4a90 --- /dev/null +++ b/core-rest-libraries/route-registration/route-registration-common/src/main/java/com/netcracker/cloud/routesregistration/common/service/TopologyConfigService.java @@ -0,0 +1,78 @@ +package com.netcracker.cloud.routesregistration.common.service; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.netcracker.cloud.routesregistration.common.gateway.route.ServiceMeshType; +import lombok.extern.slf4j.Slf4j; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Optional; + +@Slf4j +public class TopologyConfigService { + private static final String TOPOLOGY_CONFIG_PATH_DEFAULT = "/etc/topology.json"; + private static final String SERVICE_MESH_TYPE_PATH = "/featureFlags/core/serviceMeshType"; + + private final ObjectMapper objectMapper; + private final String topologyConfigPath; + + private volatile ServiceMeshType serviceMeshType; + + public TopologyConfigService(ObjectMapper objectMapper) { + this.objectMapper = objectMapper; + this.topologyConfigPath = Optional.ofNullable(System.getenv("TOPOLOGY_CONFIG_PATH")) + .filter(s -> !s.isBlank()) + .orElse(TOPOLOGY_CONFIG_PATH_DEFAULT); + } + + // visible for testing + TopologyConfigService(ObjectMapper objectMapper, String topologyConfigPath) { + this.objectMapper = objectMapper; + this.topologyConfigPath = topologyConfigPath; + } + + public ServiceMeshType getServiceMeshType() { + if (serviceMeshType != null) { + return serviceMeshType; + } + synchronized (this) { + if (serviceMeshType != null) { + return serviceMeshType; + } + serviceMeshType = loadServiceMeshType(); + } + return serviceMeshType; + } + + private ServiceMeshType loadServiceMeshType() { + try (InputStream inputStream = Files.newInputStream(Paths.get(topologyConfigPath))) { + JsonNode root = objectMapper.readTree(inputStream); + JsonNode node = root.at(SERVICE_MESH_TYPE_PATH); + + if (node.isMissingNode() || node.isNull()) { + log.warn("'{}' not found in topology config, defaulting to {}", SERVICE_MESH_TYPE_PATH, ServiceMeshType.CORE); + return ServiceMeshType.CORE; + } + + return parseServiceMeshType(node.asText()); + + } catch (IOException e) { + log.warn("Failed to read topology config from '{}', defaulting to {}", topologyConfigPath, ServiceMeshType.CORE, e); + return ServiceMeshType.CORE; + } + } + + private ServiceMeshType parseServiceMeshType(String value) { + return Arrays.stream(ServiceMeshType.values()) + .filter(type -> type.name().equalsIgnoreCase(value)) + .findFirst() + .orElseGet(() -> { + log.warn("Unknown serviceMeshType '{}', defaulting to {}", value, ServiceMeshType.CORE); + return ServiceMeshType.CORE; + }); + } +} diff --git a/core-rest-libraries/route-registration/route-registration-common/src/test/java/com/netcracker/cloud/routesregistration/common/service/TopologyConfigServiceTest.java b/core-rest-libraries/route-registration/route-registration-common/src/test/java/com/netcracker/cloud/routesregistration/common/service/TopologyConfigServiceTest.java new file mode 100644 index 000000000..65f871e4a --- /dev/null +++ b/core-rest-libraries/route-registration/route-registration-common/src/test/java/com/netcracker/cloud/routesregistration/common/service/TopologyConfigServiceTest.java @@ -0,0 +1,94 @@ +package com.netcracker.cloud.routesregistration.common.service; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.netcracker.cloud.routesregistration.common.gateway.route.ServiceMeshType; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.junit.jupiter.api.Assertions.*; + +class TopologyConfigServiceTest { + + private static final ObjectMapper objectMapper = new ObjectMapper(); + + private TopologyConfigService service(String content) throws IOException { + Path file = Files.createTempFile("topology", ".json"); + Files.writeString(file, content); + file.toFile().deleteOnExit(); + return new TopologyConfigService(objectMapper, file.toString()); + } + + private TopologyConfigService serviceWithPath(String path) { + return new TopologyConfigService(objectMapper, path); + } + + @Test + void defaultsToCoreWhenFileNotFound() { + TopologyConfigService service = serviceWithPath("/nonexistent/topology.json"); + assertEquals(ServiceMeshType.CORE, service.getServiceMeshType()); + } + + @Test + void defaultsToCoreWhenFileIsMalformed() throws IOException { + TopologyConfigService service = service("not valid json"); + assertEquals(ServiceMeshType.CORE, service.getServiceMeshType()); + } + + @Test + void defaultsToCoreWhenNodeIsMissing() throws IOException { + TopologyConfigService service = service("{}"); + assertEquals(ServiceMeshType.CORE, service.getServiceMeshType()); + } + + @Test + void defaultsToCoreWhenServiceMeshTypeIsNull() throws IOException { + TopologyConfigService service = service(""" + {"featureFlags":{"core":{"serviceMeshType":null}}} + """); + assertEquals(ServiceMeshType.CORE, service.getServiceMeshType()); + } + + @Test + void defaultsToCoreWhenServiceMeshTypeIsUnknown() throws IOException { + TopologyConfigService service = service(""" + {"featureFlags":{"core":{"serviceMeshType":"UNKNOWN"}}} + """); + assertEquals(ServiceMeshType.CORE, service.getServiceMeshType()); + } + + @Test + void returnsCoreWhenServiceMeshTypeIsCore() throws IOException { + TopologyConfigService service = service(""" + {"featureFlags":{"core":{"serviceMeshType":"CORE"}}} + """); + assertEquals(ServiceMeshType.CORE, service.getServiceMeshType()); + } + + @Test + void returnsIstioWhenServiceMeshTypeIsIstio() throws IOException { + TopologyConfigService service = service(""" + {"featureFlags":{"core":{"serviceMeshType":"ISTIO"}}} + """); + assertEquals(ServiceMeshType.ISTIO, service.getServiceMeshType()); + } + + @Test + void isCaseInsensitive() throws IOException { + TopologyConfigService service = service(""" + {"featureFlags":{"core":{"serviceMeshType":"istio"}}} + """); + assertEquals(ServiceMeshType.ISTIO, service.getServiceMeshType()); + } + + @Test + void cachesResultOnSubsequentCalls() throws IOException { + TopologyConfigService service = service(""" + {"featureFlags":{"core":{"serviceMeshType":"ISTIO"}}} + """); + assertEquals(ServiceMeshType.ISTIO, service.getServiceMeshType()); + assertEquals(ServiceMeshType.ISTIO, service.getServiceMeshType()); + } +}