From 7a6c17f8239724571f48732ebf9a841ef9e589ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jare=C5=A1?= Date: Thu, 31 Jul 2025 15:55:36 +0200 Subject: [PATCH 01/11] first version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pavel Jareš --- .../impl/ApiMediationLayerStartupChecker.java | 103 +++++++++--------- 1 file changed, 52 insertions(+), 51 deletions(-) diff --git a/integration-tests/src/test/java/org/zowe/apiml/startup/impl/ApiMediationLayerStartupChecker.java b/integration-tests/src/test/java/org/zowe/apiml/startup/impl/ApiMediationLayerStartupChecker.java index 6ba06ec4dc..8def2e21de 100644 --- a/integration-tests/src/test/java/org/zowe/apiml/startup/impl/ApiMediationLayerStartupChecker.java +++ b/integration-tests/src/test/java/org/zowe/apiml/startup/impl/ApiMediationLayerStartupChecker.java @@ -15,6 +15,7 @@ import com.jayway.jsonpath.PathNotFoundException; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; +import net.minidev.json.JSONArray; import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; @@ -22,19 +23,12 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; -import org.zowe.apiml.util.config.ConfigReader; -import org.zowe.apiml.util.config.Credentials; -import org.zowe.apiml.util.config.DiscoverableClientConfiguration; -import org.zowe.apiml.util.config.DiscoveryServiceConfiguration; -import org.zowe.apiml.util.config.GatewayServiceConfiguration; -import org.zowe.apiml.util.config.SslContext; +import org.zowe.apiml.util.config.*; import org.zowe.apiml.util.http.HttpClientUtils; import org.zowe.apiml.util.http.HttpRequestUtils; import java.io.IOException; -import java.util.ArrayList; -import java.util.Base64; -import java.util.List; +import java.util.*; import static java.util.concurrent.TimeUnit.MINUTES; import static java.util.concurrent.TimeUnit.SECONDS; @@ -51,14 +45,15 @@ public class ApiMediationLayerStartupChecker { private final GatewayServiceConfiguration gatewayConfiguration; private final DiscoverableClientConfiguration discoverableClientConfiguration; private final DiscoveryServiceConfiguration discoveryServiceConfiguration; - private final Credentials credentials; + private final String authorizationHeader; private final List servicesToCheck = new ArrayList<>(); private final String healthEndpoint = "/application/health"; public ApiMediationLayerStartupChecker() { gatewayConfiguration = ConfigReader.environmentConfiguration().getGatewayServiceConfiguration(); - credentials = ConfigReader.environmentConfiguration().getCredentials(); + var credentials = ConfigReader.environmentConfiguration().getCredentials(); + authorizationHeader = "Basic " + Base64.getEncoder().encodeToString(String.format("%s:%s", credentials.getUser(), credentials.getPassword()).getBytes()); discoverableClientConfiguration = ConfigReader.environmentConfiguration().getDiscoverableClientConfiguration(); discoveryServiceConfiguration = ConfigReader.environmentConfiguration().getDiscoveryServiceConfiguration(); @@ -100,49 +95,47 @@ private DocumentContext getDocumentAsContext(HttpGet request) { } private boolean areAllServicesUp() { + return Arrays.stream(gatewayConfiguration.getHost().split(",")) + .allMatch(this::areAllServicesUp); + } + + private boolean areAllServicesUp(String gatewayHost) { try { - var gatewayHosts = gatewayConfiguration.getHost().split(","); - var requestToGateway1 = HttpRequestUtils.getRequest(gatewayHosts[0], healthEndpoint); - // If second one does not exist, redundant call and check to same gateway - var requestToGateway2 = HttpRequestUtils.getRequest(gatewayHosts.length > 1 ? gatewayHosts[1] : gatewayHosts[0], healthEndpoint); + var requestToGateway = HttpRequestUtils.getRequest(gatewayHost, healthEndpoint); - requestToGateway1.addHeader("Authorization", "Basic " + Base64.getEncoder().encodeToString(String.format("%s:%s", credentials.getUser(), credentials.getPassword()).getBytes())); - requestToGateway2.addHeader("Authorization", "Basic " + Base64.getEncoder().encodeToString(String.format("%s:%s", credentials.getUser(), credentials.getPassword()).getBytes())); - DocumentContext context1 = getDocumentAsContext(requestToGateway1); - DocumentContext context2 = getDocumentAsContext(requestToGateway2); + requestToGateway.addHeader("Authorization", authorizationHeader); + DocumentContext context = getDocumentAsContext(requestToGateway); - if (context1 == null || context2 == null) { + if (context == null) { return false; } boolean areAllServicesUp = true; for (Service toCheck : servicesToCheck) { - boolean isUp = isServiceUp(context1, toCheck.path); + boolean isUp = isServiceUp(context, toCheck.path); logDebug(toCheck.name + " is {}", isUp); - - if (!isUp) { - areAllServicesUp = false; - } + areAllServicesUp &= false; } if (!IS_MODULITH_ENABLED && !isAuthUp()) { areAllServicesUp = false; } - String allComponents = context1.read("$.components.discoveryComposite.components.discoveryClient.details.services").toString(); - boolean isTestApplicationUp = allComponents.toLowerCase().contains("discoverableclient"); + JSONArray servicesJsonArray = context.read("$.components.discoveryComposite.components.discoveryClient.details.services"); + List services = servicesJsonArray.stream().map(Objects::toString).map(String::toLowerCase).toList(); + + boolean isTestApplicationUp = services.contains("discoverableclient"); boolean needsTestApplication = discoverableClientConfiguration.getInstances() > 0; log.debug("Discoverable Client is {}", isTestApplicationUp); log.debug("Needs Discoverable Client: {}", needsTestApplication); isTestApplicationUp = !needsTestApplication || isTestApplicationUp; - Integer amountOfActiveGateways1 = context1.read("$.components.gateway.details.gatewayCount"); - Integer amountOfActiveGateways2 = context2.read("$.components.gateway.details.gatewayCount"); + Integer amountOfActiveGateways = context.read("$.components.gateway.details.gatewayCount"); var expectedGatewayCount = Integer.getInteger("environment.gwCount", gatewayConfiguration.getInstances()); - boolean isValidAmountOfGatewaysUp = amountOfActiveGateways1 != null && amountOfActiveGateways2 != null && - amountOfActiveGateways1 >= expectedGatewayCount && amountOfActiveGateways2 >= expectedGatewayCount; - log.debug("There are {} gateways in GW1 and {} in GW2", amountOfActiveGateways1, amountOfActiveGateways2); + boolean isValidAmountOfGatewaysUp = amountOfActiveGateways != null && + amountOfActiveGateways >= expectedGatewayCount; + log.debug("There are {} gateways in GW on {}", amountOfActiveGateways, gatewayHost); if (!isValidAmountOfGatewaysUp) { log.debug("Expecting at least {} gateways", gatewayConfiguration.getInstances()); @@ -151,24 +144,7 @@ private boolean areAllServicesUp() { } // Consider properly the case with multiple gateway services running on different ports. - if (gatewayConfiguration.getInternalPorts() != null && !gatewayConfiguration.getInternalPorts().isEmpty()) { - String[] internalPorts = gatewayConfiguration.getInternalPorts().split(","); - String[] hosts = gatewayConfiguration.getHost().split(","); - - for (int i = 0; i < Math.min(internalPorts.length, hosts.length); i++) { - log.debug("Trying to access the Gateway at port {}", internalPorts[i]); - requestToGateway1 = HttpRequestUtils.getRequest(healthEndpoint); - requestToGateway1.addHeader("Authorization", "Basic " + Base64.getEncoder().encodeToString(String.format("%s:%s", credentials.getUser(), credentials.getPassword()).getBytes())); - var response = HttpClientUtils.client().execute(requestToGateway1); - - if (response.getStatusLine().getStatusCode() != 200) { - log.debug("Response from gateway at {} was: {}", requestToGateway1.getURI(), response.getEntity() != null ? EntityUtils.toString(response.getEntity()) : "undefined"); - throw new IOException(); - } - - } - - } + callInternalPorts(gatewayHost); var result = areAllServicesUp && isTestApplicationUp; if (!result) { @@ -182,6 +158,31 @@ private boolean areAllServicesUp() { } } + private void callInternalPorts(String gatewayHost) throws IOException { + if (StringUtils.isBlank(gatewayConfiguration.getInternalPorts())) { + log.debug("No internal ports are defined"); + return; + } + + String[] internalPorts = gatewayConfiguration.getInternalPorts().split(","); + String[] hosts = gatewayConfiguration.getHost().split(","); + + for (int i = 0; i < Math.min(internalPorts.length, hosts.length); i++) { + if (!StringUtils.equals(hosts[i], gatewayHost)) continue; + + log.debug("Trying to access the Gateway at port {}", internalPorts[i]); + var requestToGateway = HttpRequestUtils.getRequest(healthEndpoint); + requestToGateway.addHeader("Authorization", authorizationHeader); + var response = HttpClientUtils.client().execute(requestToGateway); + + if (response.getStatusLine().getStatusCode() != 200) { + log.debug("Response from gateway at {} was: {}", requestToGateway.getURI(), response.getEntity() != null ? EntityUtils.toString(response.getEntity()) : "undefined"); + throw new IOException(); + } + + } + } + private void callEurekaApps() { HttpGet requestToEurekaApps = new HttpGet(HttpRequestUtils.getUriFromService(discoveryServiceConfiguration, "/eureka/apps")); CloseableHttpClient client = HttpClients.custom().setSSLContext(SslContext.sslClientCertValid).build(); @@ -205,7 +206,7 @@ private boolean isAuthUp() { } else { requestToZaas = new HttpGet(HttpRequestUtils.getUriFromGateway(healthEndpoint)); } - requestToZaas.addHeader("Authorization", "Basic " + Base64.getEncoder().encodeToString(String.format("%s:%s", credentials.getUser(), credentials.getPassword()).getBytes())); + requestToZaas.addHeader("Authorization", authorizationHeader); DocumentContext zaasContext = getDocumentAsContext(requestToZaas); if (zaasContext == null) { return false; From 52f64c3c2a9bccf71b33b1d749fc51f6eeae0ea2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jare=C5=A1?= Date: Thu, 31 Jul 2025 17:20:58 +0200 Subject: [PATCH 02/11] fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pavel Jareš --- .../apiml/startup/impl/ApiMediationLayerStartupChecker.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration-tests/src/test/java/org/zowe/apiml/startup/impl/ApiMediationLayerStartupChecker.java b/integration-tests/src/test/java/org/zowe/apiml/startup/impl/ApiMediationLayerStartupChecker.java index 8def2e21de..604a1005a7 100644 --- a/integration-tests/src/test/java/org/zowe/apiml/startup/impl/ApiMediationLayerStartupChecker.java +++ b/integration-tests/src/test/java/org/zowe/apiml/startup/impl/ApiMediationLayerStartupChecker.java @@ -114,7 +114,7 @@ private boolean areAllServicesUp(String gatewayHost) { for (Service toCheck : servicesToCheck) { boolean isUp = isServiceUp(context, toCheck.path); logDebug(toCheck.name + " is {}", isUp); - areAllServicesUp &= false; + areAllServicesUp &= isUp; } if (!IS_MODULITH_ENABLED && !isAuthUp()) { areAllServicesUp = false; From 6323dd04417d3db0a63642ffd6a833852dc63cf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jare=C5=A1?= Date: Thu, 31 Jul 2025 17:43:04 +0200 Subject: [PATCH 03/11] fix - check amount of ZAAS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pavel Jareš --- .../impl/ApiMediationLayerStartupChecker.java | 47 ++++++++++++------- .../ApiCatalogServiceConfiguration.java | 8 ++++ .../config/CachingServiceConfiguration.java | 6 +++ .../CentralGatewayServiceConfiguration.java | 6 +++ .../DiscoverableClientConfiguration.java | 8 ++++ .../config/DiscoveryServiceConfiguration.java | 8 ++++ .../config/GatewayServiceConfiguration.java | 8 ++++ .../util/config/ServiceConfiguration.java | 12 +++++ .../apiml/util/config/ZaasConfiguration.java | 8 ++++ .../config/ZosmfServiceConfiguration.java | 2 + 10 files changed, 96 insertions(+), 17 deletions(-) diff --git a/integration-tests/src/test/java/org/zowe/apiml/startup/impl/ApiMediationLayerStartupChecker.java b/integration-tests/src/test/java/org/zowe/apiml/startup/impl/ApiMediationLayerStartupChecker.java index 604a1005a7..5d0c06cbca 100644 --- a/integration-tests/src/test/java/org/zowe/apiml/startup/impl/ApiMediationLayerStartupChecker.java +++ b/integration-tests/src/test/java/org/zowe/apiml/startup/impl/ApiMediationLayerStartupChecker.java @@ -57,12 +57,12 @@ public ApiMediationLayerStartupChecker() { discoverableClientConfiguration = ConfigReader.environmentConfiguration().getDiscoverableClientConfiguration(); discoveryServiceConfiguration = ConfigReader.environmentConfiguration().getDiscoveryServiceConfiguration(); - servicesToCheck.add(new Service("Gateway", "$.status")); + servicesToCheck.add(new Service("Gateway", "$.status", ConfigReader.environmentConfiguration().getGatewayServiceConfiguration())); if (!IS_MODULITH_ENABLED) { - servicesToCheck.add(new Service("ZAAS", "$.components.gateway.details.zaas")); + servicesToCheck.add(new Service("ZAAS", "$.components.gateway.details.zaas", ConfigReader.environmentConfiguration().getZaasConfiguration())); } - servicesToCheck.add(new Service("Api Catalog", "$.components.gateway.details.apicatalog")); - servicesToCheck.add(new Service("Discovery Service", "$.components.gateway.details.discovery")); + servicesToCheck.add(new Service("Api Catalog", "$.components.gateway.details.apicatalog", ConfigReader.environmentConfiguration().getApiCatalogServiceConfiguration())); + servicesToCheck.add(new Service("Discovery Service", "$.components.gateway.details.discovery", ConfigReader.environmentConfiguration().getDiscoveryServiceConfiguration())); } public void waitUntilReady() { @@ -115,6 +115,8 @@ private boolean areAllServicesUp(String gatewayHost) { boolean isUp = isServiceUp(context, toCheck.path); logDebug(toCheck.name + " is {}", isUp); areAllServicesUp &= isUp; + + areAllServicesUp &= checkServicesCount(context, gatewayConfiguration, gatewayHost); } if (!IS_MODULITH_ENABLED && !isAuthUp()) { areAllServicesUp = false; @@ -130,19 +132,6 @@ private boolean areAllServicesUp(String gatewayHost) { log.debug("Needs Discoverable Client: {}", needsTestApplication); isTestApplicationUp = !needsTestApplication || isTestApplicationUp; - Integer amountOfActiveGateways = context.read("$.components.gateway.details.gatewayCount"); - var expectedGatewayCount = Integer.getInteger("environment.gwCount", gatewayConfiguration.getInstances()); - - boolean isValidAmountOfGatewaysUp = amountOfActiveGateways != null && - amountOfActiveGateways >= expectedGatewayCount; - log.debug("There are {} gateways in GW on {}", amountOfActiveGateways, gatewayHost); - - if (!isValidAmountOfGatewaysUp) { - log.debug("Expecting at least {} gateways", gatewayConfiguration.getInstances()); - callEurekaApps(); - return false; - } - // Consider properly the case with multiple gateway services running on different ports. callInternalPorts(gatewayHost); @@ -158,6 +147,26 @@ private boolean areAllServicesUp(String gatewayHost) { } } + private boolean checkServicesCount(DocumentContext context, ServiceConfiguration serviceConfiguration, String gatewayHost) { + Integer amountOfActiveService = context.read("$.components.discoveryComposite.components.eureka.details.applications." + serviceConfiguration.getServiceId().toUpperCase()); + var expectedCount = serviceConfiguration.getInstances(); + if (serviceConfiguration instanceof GatewayServiceConfiguration) { + expectedCount = Integer.getInteger("environment.gwCount", expectedCount); + } + + boolean isValidAmountOfServicesUp = amountOfActiveService != null && + amountOfActiveService >= expectedCount; + log.debug("There are {} {} in GW on {}", amountOfActiveService, serviceConfiguration.getServiceId(), gatewayHost); + + if (!isValidAmountOfServicesUp) { + log.debug("Expecting at least {} services ({})", expectedCount, serviceConfiguration.getServiceId()); + callEurekaApps(); + return false; + } + + return true; + } + private void callInternalPorts(String gatewayHost) throws IOException { if (StringUtils.isBlank(gatewayConfiguration.getInternalPorts())) { log.debug("No internal ports are defined"); @@ -231,7 +240,11 @@ private void logDebug(String logMessage, boolean state) { @AllArgsConstructor private class Service { + String name; String path; + ServiceConfiguration configuration; + } + } diff --git a/integration-tests/src/test/java/org/zowe/apiml/util/config/ApiCatalogServiceConfiguration.java b/integration-tests/src/test/java/org/zowe/apiml/util/config/ApiCatalogServiceConfiguration.java index 7e30cbb3f2..1f1a482b63 100644 --- a/integration-tests/src/test/java/org/zowe/apiml/util/config/ApiCatalogServiceConfiguration.java +++ b/integration-tests/src/test/java/org/zowe/apiml/util/config/ApiCatalogServiceConfiguration.java @@ -13,14 +13,22 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; +import org.zowe.apiml.product.constants.CoreService; @Data @AllArgsConstructor @NoArgsConstructor public class ApiCatalogServiceConfiguration implements ServiceConfiguration { + private String scheme; private String url; private String host; private int port; private int instances; + + @Override + public String getServiceId() { + return CoreService.API_CATALOG.getServiceId(); + } + } diff --git a/integration-tests/src/test/java/org/zowe/apiml/util/config/CachingServiceConfiguration.java b/integration-tests/src/test/java/org/zowe/apiml/util/config/CachingServiceConfiguration.java index eca6eeba76..4dbebed7f6 100644 --- a/integration-tests/src/test/java/org/zowe/apiml/util/config/CachingServiceConfiguration.java +++ b/integration-tests/src/test/java/org/zowe/apiml/util/config/CachingServiceConfiguration.java @@ -13,6 +13,7 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; +import org.zowe.apiml.product.constants.CoreService; @Data @AllArgsConstructor @@ -36,4 +37,9 @@ public int getPort() { throw new IllegalStateException("Method is not implemented"); } + @Override + public String getServiceId() { + return CoreService.CACHING.getServiceId(); + } + } diff --git a/integration-tests/src/test/java/org/zowe/apiml/util/config/CentralGatewayServiceConfiguration.java b/integration-tests/src/test/java/org/zowe/apiml/util/config/CentralGatewayServiceConfiguration.java index 322b967735..868b53f499 100644 --- a/integration-tests/src/test/java/org/zowe/apiml/util/config/CentralGatewayServiceConfiguration.java +++ b/integration-tests/src/test/java/org/zowe/apiml/util/config/CentralGatewayServiceConfiguration.java @@ -13,6 +13,7 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; +import org.zowe.apiml.product.constants.CoreService; @Data @AllArgsConstructor @@ -23,4 +24,9 @@ public class CentralGatewayServiceConfiguration implements ServiceConfiguration private String host; private int port; + @Override + public String getServiceId() { + return "centralGateway"; + } + } diff --git a/integration-tests/src/test/java/org/zowe/apiml/util/config/DiscoverableClientConfiguration.java b/integration-tests/src/test/java/org/zowe/apiml/util/config/DiscoverableClientConfiguration.java index cb2d706cfa..beccb25992 100644 --- a/integration-tests/src/test/java/org/zowe/apiml/util/config/DiscoverableClientConfiguration.java +++ b/integration-tests/src/test/java/org/zowe/apiml/util/config/DiscoverableClientConfiguration.java @@ -13,6 +13,7 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; +import org.zowe.apiml.product.constants.CoreService; /** * Configuration parameters for DiscoverableClient @@ -21,9 +22,16 @@ @NoArgsConstructor @AllArgsConstructor public class DiscoverableClientConfiguration implements ServiceConfiguration { + private String scheme; private String applId; private String host; private int port; private int instances; + + @Override + public String getServiceId() { + return "discoverableclient"; + } + } diff --git a/integration-tests/src/test/java/org/zowe/apiml/util/config/DiscoveryServiceConfiguration.java b/integration-tests/src/test/java/org/zowe/apiml/util/config/DiscoveryServiceConfiguration.java index 43843846eb..1d582469dd 100644 --- a/integration-tests/src/test/java/org/zowe/apiml/util/config/DiscoveryServiceConfiguration.java +++ b/integration-tests/src/test/java/org/zowe/apiml/util/config/DiscoveryServiceConfiguration.java @@ -13,11 +13,13 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; +import org.zowe.apiml.product.constants.CoreService; @Data @AllArgsConstructor @NoArgsConstructor public class DiscoveryServiceConfiguration implements ServiceConfiguration { + private String scheme; private String user; private String password; @@ -26,5 +28,11 @@ public class DiscoveryServiceConfiguration implements ServiceConfiguration { private int port; private int additionalPort; private int instances; + + @Override + public String getServiceId() { + return CoreService.DISCOVERY.getServiceId(); + } + } diff --git a/integration-tests/src/test/java/org/zowe/apiml/util/config/GatewayServiceConfiguration.java b/integration-tests/src/test/java/org/zowe/apiml/util/config/GatewayServiceConfiguration.java index 8978dd2879..0e8aa3b020 100644 --- a/integration-tests/src/test/java/org/zowe/apiml/util/config/GatewayServiceConfiguration.java +++ b/integration-tests/src/test/java/org/zowe/apiml/util/config/GatewayServiceConfiguration.java @@ -13,11 +13,13 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; +import org.zowe.apiml.product.constants.CoreService; @Data @AllArgsConstructor @NoArgsConstructor public class GatewayServiceConfiguration implements ServiceConfiguration { + private String scheme; private String host; private String dvipaHost; @@ -27,4 +29,10 @@ public class GatewayServiceConfiguration implements ServiceConfiguration { private String internalPorts; private String servicesEndpoint; private int bucketCapacity; + + @Override + public String getServiceId() { + return CoreService.GATEWAY.getServiceId(); + } + } diff --git a/integration-tests/src/test/java/org/zowe/apiml/util/config/ServiceConfiguration.java b/integration-tests/src/test/java/org/zowe/apiml/util/config/ServiceConfiguration.java index 58687487ae..0b1f598eab 100644 --- a/integration-tests/src/test/java/org/zowe/apiml/util/config/ServiceConfiguration.java +++ b/integration-tests/src/test/java/org/zowe/apiml/util/config/ServiceConfiguration.java @@ -10,10 +10,22 @@ package org.zowe.apiml.util.config; +import org.apache.commons.lang3.StringUtils; + public interface ServiceConfiguration { String getScheme(); String getHost(); int getPort(); + default int getInstances() { + var host = getHost(); + if (StringUtils.isBlank(host)) { + return 0; + } + return getHost().split(",").length; + } + + String getServiceId(); + } diff --git a/integration-tests/src/test/java/org/zowe/apiml/util/config/ZaasConfiguration.java b/integration-tests/src/test/java/org/zowe/apiml/util/config/ZaasConfiguration.java index 2933c6785f..c27708494c 100644 --- a/integration-tests/src/test/java/org/zowe/apiml/util/config/ZaasConfiguration.java +++ b/integration-tests/src/test/java/org/zowe/apiml/util/config/ZaasConfiguration.java @@ -13,13 +13,21 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; +import org.zowe.apiml.product.constants.CoreService; @Data @AllArgsConstructor @NoArgsConstructor public class ZaasConfiguration implements ServiceConfiguration { + private String scheme; private String host; private int port; private int instances; + + @Override + public String getServiceId() { + return CoreService.ZAAS.getServiceId(); + } + } diff --git a/integration-tests/src/test/java/org/zowe/apiml/util/config/ZosmfServiceConfiguration.java b/integration-tests/src/test/java/org/zowe/apiml/util/config/ZosmfServiceConfiguration.java index 306a268d6a..5521017850 100644 --- a/integration-tests/src/test/java/org/zowe/apiml/util/config/ZosmfServiceConfiguration.java +++ b/integration-tests/src/test/java/org/zowe/apiml/util/config/ZosmfServiceConfiguration.java @@ -18,9 +18,11 @@ @AllArgsConstructor @NoArgsConstructor public class ZosmfServiceConfiguration implements ServiceConfiguration { + private String scheme; private String host; private int port; private String serviceId; private String contextRoot; + } From 6b85422316caad5697dade9e2f80e76d7e1dc101 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jare=C5=A1?= Date: Thu, 31 Jul 2025 18:04:22 +0200 Subject: [PATCH 04/11] different way on how to read amount of services MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pavel Jareš --- .../impl/ApiMediationLayerStartupChecker.java | 35 ++++++++----------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/integration-tests/src/test/java/org/zowe/apiml/startup/impl/ApiMediationLayerStartupChecker.java b/integration-tests/src/test/java/org/zowe/apiml/startup/impl/ApiMediationLayerStartupChecker.java index 5d0c06cbca..664287c178 100644 --- a/integration-tests/src/test/java/org/zowe/apiml/startup/impl/ApiMediationLayerStartupChecker.java +++ b/integration-tests/src/test/java/org/zowe/apiml/startup/impl/ApiMediationLayerStartupChecker.java @@ -23,7 +23,10 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; -import org.zowe.apiml.util.config.*; +import org.zowe.apiml.util.config.ConfigReader; +import org.zowe.apiml.util.config.GatewayServiceConfiguration; +import org.zowe.apiml.util.config.ServiceConfiguration; +import org.zowe.apiml.util.config.SslContext; import org.zowe.apiml.util.http.HttpClientUtils; import org.zowe.apiml.util.http.HttpRequestUtils; @@ -42,20 +45,13 @@ public class ApiMediationLayerStartupChecker { private static final boolean IS_MODULITH_ENABLED = Boolean.parseBoolean(System.getProperty("environment.modulith")); - private final GatewayServiceConfiguration gatewayConfiguration; - private final DiscoverableClientConfiguration discoverableClientConfiguration; - private final DiscoveryServiceConfiguration discoveryServiceConfiguration; private final String authorizationHeader; private final List servicesToCheck = new ArrayList<>(); private final String healthEndpoint = "/application/health"; - public ApiMediationLayerStartupChecker() { - gatewayConfiguration = ConfigReader.environmentConfiguration().getGatewayServiceConfiguration(); var credentials = ConfigReader.environmentConfiguration().getCredentials(); authorizationHeader = "Basic " + Base64.getEncoder().encodeToString(String.format("%s:%s", credentials.getUser(), credentials.getPassword()).getBytes()); - discoverableClientConfiguration = ConfigReader.environmentConfiguration().getDiscoverableClientConfiguration(); - discoveryServiceConfiguration = ConfigReader.environmentConfiguration().getDiscoveryServiceConfiguration(); servicesToCheck.add(new Service("Gateway", "$.status", ConfigReader.environmentConfiguration().getGatewayServiceConfiguration())); if (!IS_MODULITH_ENABLED) { @@ -95,7 +91,7 @@ private DocumentContext getDocumentAsContext(HttpGet request) { } private boolean areAllServicesUp() { - return Arrays.stream(gatewayConfiguration.getHost().split(",")) + return Arrays.stream(ConfigReader.environmentConfiguration().getGatewayServiceConfiguration().getHost().split(",")) .allMatch(this::areAllServicesUp); } @@ -116,7 +112,7 @@ private boolean areAllServicesUp(String gatewayHost) { logDebug(toCheck.name + " is {}", isUp); areAllServicesUp &= isUp; - areAllServicesUp &= checkServicesCount(context, gatewayConfiguration, gatewayHost); + areAllServicesUp &= checkServicesCount(context, toCheck.configuration, gatewayHost); } if (!IS_MODULITH_ENABLED && !isAuthUp()) { areAllServicesUp = false; @@ -125,22 +121,14 @@ private boolean areAllServicesUp(String gatewayHost) { JSONArray servicesJsonArray = context.read("$.components.discoveryComposite.components.discoveryClient.details.services"); List services = servicesJsonArray.stream().map(Objects::toString).map(String::toLowerCase).toList(); - boolean isTestApplicationUp = services.contains("discoverableclient"); - boolean needsTestApplication = discoverableClientConfiguration.getInstances() > 0; - - log.debug("Discoverable Client is {}", isTestApplicationUp); - log.debug("Needs Discoverable Client: {}", needsTestApplication); - isTestApplicationUp = !needsTestApplication || isTestApplicationUp; - // Consider properly the case with multiple gateway services running on different ports. callInternalPorts(gatewayHost); - var result = areAllServicesUp && isTestApplicationUp; - if (!result) { + if (!areAllServicesUp) { log.debug("API ML is not ready, check which services are missing in the above messages"); } - return result; + return areAllServicesUp; } catch (PathNotFoundException | IOException e) { log.warn("Check failed on retrieving the information from document: {}", e.getMessage()); return false; @@ -148,6 +136,10 @@ private boolean areAllServicesUp(String gatewayHost) { } private boolean checkServicesCount(DocumentContext context, ServiceConfiguration serviceConfiguration, String gatewayHost) { + if (serviceConfiguration.getInstances() == 0) { + return true; + } + Integer amountOfActiveService = context.read("$.components.discoveryComposite.components.eureka.details.applications." + serviceConfiguration.getServiceId().toUpperCase()); var expectedCount = serviceConfiguration.getInstances(); if (serviceConfiguration instanceof GatewayServiceConfiguration) { @@ -168,6 +160,8 @@ private boolean checkServicesCount(DocumentContext context, ServiceConfiguration } private void callInternalPorts(String gatewayHost) throws IOException { + var gatewayConfiguration = ConfigReader.environmentConfiguration().getGatewayServiceConfiguration(); + if (StringUtils.isBlank(gatewayConfiguration.getInternalPorts())) { log.debug("No internal ports are defined"); return; @@ -193,6 +187,7 @@ private void callInternalPorts(String gatewayHost) throws IOException { } private void callEurekaApps() { + var discoveryServiceConfiguration = ConfigReader.environmentConfiguration().getDiscoveryServiceConfiguration(); HttpGet requestToEurekaApps = new HttpGet(HttpRequestUtils.getUriFromService(discoveryServiceConfiguration, "/eureka/apps")); CloseableHttpClient client = HttpClients.custom().setSSLContext(SslContext.sslClientCertValid).build(); try (client) { From def669f2f68956cf7419333677e36ac3ee0c8948 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jare=C5=A1?= Date: Fri, 1 Aug 2025 09:35:12 +0200 Subject: [PATCH 05/11] add eureka health - to back-compatibility and checking amount of all services MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pavel Jareš --- .../EurekaHealthIndicatorApiml.java | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 apiml-common/src/main/java/org/zowe/apiml/product/compatibility/EurekaHealthIndicatorApiml.java diff --git a/apiml-common/src/main/java/org/zowe/apiml/product/compatibility/EurekaHealthIndicatorApiml.java b/apiml-common/src/main/java/org/zowe/apiml/product/compatibility/EurekaHealthIndicatorApiml.java new file mode 100644 index 0000000000..f2d67a5af9 --- /dev/null +++ b/apiml-common/src/main/java/org/zowe/apiml/product/compatibility/EurekaHealthIndicatorApiml.java @@ -0,0 +1,63 @@ +/* + * 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.product.compatibility; + +import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.actuate.health.Status; +import org.springframework.cloud.client.discovery.DiscoveryClient; +import org.springframework.cloud.netflix.eureka.EurekaHealthIndicator; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Component; + +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * This class is replacement of org.springframework.cloud.netflix.eureka.EurekaHealthIndicator, because it is using + * a different Eureka client + */ +@Primary +@Component("eurekaHealthIndicator") +public class EurekaHealthIndicatorApiml extends EurekaHealthIndicator { + + private DiscoveryClient discoveryClient; + + public EurekaHealthIndicatorApiml(DiscoveryClient discoveryClient) { + super(null, null, null); + this.discoveryClient = discoveryClient; + } + + @Override + public String getName() { + return "eureka"; + } + + @Override + public Health health() { + Health.Builder builder = Health.unknown(); + Status status = getStatus(builder); + return builder.status(status).withDetail("applications", getApplications()).build(); + } + + private Status getStatus(Health.Builder builder) { + return new Status("UP", "Eureka discovery client has not yet successfully connected to a Eureka server"); + } + + private Map getApplications() { + return discoveryClient.getServices().stream() + .collect(Collectors.toMap( + Function.identity(), + serviceId -> discoveryClient.getInstances(serviceId).size()) + ); + } + +} From bf3b6edbd7a4ccb9ab78194fe2896ef902546872 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jare=C5=A1?= Date: Fri, 1 Aug 2025 09:57:34 +0200 Subject: [PATCH 06/11] fix expected amount of apicatalog (+others modulith services) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pavel Jareš --- .../startup/impl/ApiMediationLayerStartupChecker.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/integration-tests/src/test/java/org/zowe/apiml/startup/impl/ApiMediationLayerStartupChecker.java b/integration-tests/src/test/java/org/zowe/apiml/startup/impl/ApiMediationLayerStartupChecker.java index 664287c178..c9bdd6997a 100644 --- a/integration-tests/src/test/java/org/zowe/apiml/startup/impl/ApiMediationLayerStartupChecker.java +++ b/integration-tests/src/test/java/org/zowe/apiml/startup/impl/ApiMediationLayerStartupChecker.java @@ -23,6 +23,7 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; +import org.zowe.apiml.product.constants.CoreService; import org.zowe.apiml.util.config.ConfigReader; import org.zowe.apiml.util.config.GatewayServiceConfiguration; import org.zowe.apiml.util.config.ServiceConfiguration; @@ -146,6 +147,12 @@ private boolean checkServicesCount(DocumentContext context, ServiceConfiguration expectedCount = Integer.getInteger("environment.gwCount", expectedCount); } + if (IS_MODULITH_ENABLED && StringUtils.equalsAnyIgnoreCase(serviceConfiguration.getServiceId(), + CoreService.API_CATALOG.getServiceId(), CoreService.CACHING.getServiceId(), CoreService.ZAAS.getServiceId() + )) { + amountOfActiveService = Math.min(amountOfActiveService, 1); + } + boolean isValidAmountOfServicesUp = amountOfActiveService != null && amountOfActiveService >= expectedCount; log.debug("There are {} {} in GW on {}", amountOfActiveService, serviceConfiguration.getServiceId(), gatewayHost); From b45d91c07f9aff7f4dd779bee0313056df56ef11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jare=C5=A1?= Date: Fri, 1 Aug 2025 13:59:44 +0200 Subject: [PATCH 07/11] wip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pavel Jareš --- .../EurekaHealthIndicatorApiml.java | 2 +- .../impl/ApiMediationLayerStartupChecker.java | 150 +++++++++++++----- .../environment-configuration-ha.yml | 2 +- 3 files changed, 112 insertions(+), 42 deletions(-) diff --git a/apiml-common/src/main/java/org/zowe/apiml/product/compatibility/EurekaHealthIndicatorApiml.java b/apiml-common/src/main/java/org/zowe/apiml/product/compatibility/EurekaHealthIndicatorApiml.java index f2d67a5af9..c46afdfb9c 100644 --- a/apiml-common/src/main/java/org/zowe/apiml/product/compatibility/EurekaHealthIndicatorApiml.java +++ b/apiml-common/src/main/java/org/zowe/apiml/product/compatibility/EurekaHealthIndicatorApiml.java @@ -55,7 +55,7 @@ private Status getStatus(Health.Builder builder) { private Map getApplications() { return discoveryClient.getServices().stream() .collect(Collectors.toMap( - Function.identity(), + String::toLowerCase, serviceId -> discoveryClient.getInstances(serviceId).size()) ); } diff --git a/integration-tests/src/test/java/org/zowe/apiml/startup/impl/ApiMediationLayerStartupChecker.java b/integration-tests/src/test/java/org/zowe/apiml/startup/impl/ApiMediationLayerStartupChecker.java index c9bdd6997a..93904a692e 100644 --- a/integration-tests/src/test/java/org/zowe/apiml/startup/impl/ApiMediationLayerStartupChecker.java +++ b/integration-tests/src/test/java/org/zowe/apiml/startup/impl/ApiMediationLayerStartupChecker.java @@ -20,19 +20,18 @@ import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.client.methods.HttpGet; +import org.apache.http.conn.ssl.SSLSocketFactory; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import org.zowe.apiml.product.constants.CoreService; -import org.zowe.apiml.util.config.ConfigReader; -import org.zowe.apiml.util.config.GatewayServiceConfiguration; -import org.zowe.apiml.util.config.ServiceConfiguration; -import org.zowe.apiml.util.config.SslContext; +import org.zowe.apiml.util.config.*; import org.zowe.apiml.util.http.HttpClientUtils; import org.zowe.apiml.util.http.HttpRequestUtils; import java.io.IOException; import java.util.*; +import java.util.stream.Stream; import static java.util.concurrent.TimeUnit.MINUTES; import static java.util.concurrent.TimeUnit.SECONDS; @@ -68,10 +67,10 @@ public void waitUntilReady() { .atMost(10, MINUTES) .pollDelay(0, SECONDS) .pollInterval(poolInterval, SECONDS) - .until(this::areAllServicesUp); + .until(this::isApimlReady); } - private DocumentContext getDocumentAsContext(HttpGet request) { + private static DocumentContext getDocumentAsContext(HttpGet request) { try { final HttpResponse response = HttpClientUtils.client().execute(request); if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { @@ -91,65 +90,131 @@ private DocumentContext getDocumentAsContext(HttpGet request) { } } - private boolean areAllServicesUp() { - return Arrays.stream(ConfigReader.environmentConfiguration().getGatewayServiceConfiguration().getHost().split(",")) - .allMatch(this::areAllServicesUp); + private boolean isApimlReady() { + try { + return Stream.of( + checkHealthEndpointWithEureka(ConfigReader.environmentConfiguration().getZaasConfiguration()), + checkHealthEndpointWithEureka(ConfigReader.environmentConfiguration().getGatewayServiceConfiguration()), + // Consider properly the case with multiple gateway services running on different ports. + callInternalPorts(), + allServicesAreUp() + ).allMatch(x -> x); + } catch (Exception e) { + log.error("Error during checking if APIML is up", e); + return false; + } } - private boolean areAllServicesUp(String gatewayHost) { - try { - var requestToGateway = HttpRequestUtils.getRequest(gatewayHost, healthEndpoint); + private String getHealthEndpoint(ServiceConfiguration serviceConfiguration, String host) { + String path = healthEndpoint; + if (serviceConfiguration instanceof ApiCatalogServiceConfiguration) { + path = "/apicatalog" + path; + } + return String.format("%s://%s:%d%s", serviceConfiguration.getScheme(), host, serviceConfiguration.getPort(), path); + } - requestToGateway.addHeader("Authorization", authorizationHeader); - DocumentContext context = getDocumentAsContext(requestToGateway); + private boolean allServicesAreUp() { + return servicesToCheck.stream() + .map(service -> service.configuration) + .filter(service -> !IS_MODULITH_ENABLED || !isModulithComponent(service.getServiceId())) + .flatMap(service -> Arrays.stream(service.getHost().split(",")) + .map(host -> getHealthEndpoint(service, host)) + .map(HttpGet::new) + .map(request -> { + request.addHeader("Authorization", authorizationHeader); + DocumentContext context = getDocumentAsContext(request); + return (context != null) && "UP".equals(context.read("$.status")); + }) + ).allMatch(x -> x); + } + private boolean checkHealthEndpointWithEureka(ServiceConfiguration serviceConfiguration) { + return Arrays.stream(serviceConfiguration.getHost().split(",")) + .map(host -> getHealthEndpoint(serviceConfiguration, host)) + .map(HttpGet::new) + .allMatch(request -> { + request.addHeader("Authorization", authorizationHeader); + DocumentContext context = getDocumentAsContext(request); + + return isApimlReadyByFullHealthEndpoint(context, request.getURI().getHost()); + }); + } + + private boolean isApimlReadyByFullHealthEndpoint(DocumentContext context, String host) { + try { if (context == null) { return false; } + JSONArray servicesJsonArray = context.read("$.components.discoveryComposite.components.discoveryClient.details.services"); + List services = servicesJsonArray.stream().map(Objects::toString).map(String::toLowerCase).toList(); + boolean areAllServicesUp = true; for (Service toCheck : servicesToCheck) { - boolean isUp = isServiceUp(context, toCheck.path); - logDebug(toCheck.name + " is {}", isUp); - areAllServicesUp &= isUp; - - areAllServicesUp &= checkServicesCount(context, toCheck.configuration, gatewayHost); + if (toCheck.configuration instanceof GatewayServiceConfiguration) { + boolean isUp = isServiceUp(context, toCheck.path); + logDebug(toCheck.name + " is {}", isUp); + areAllServicesUp &= isUp; + } + areAllServicesUp &= services.contains(toCheck.configuration.getServiceId().toLowerCase()); + areAllServicesUp &= checkServicesCount(context, toCheck.configuration, host); } if (!IS_MODULITH_ENABLED && !isAuthUp()) { areAllServicesUp = false; } - JSONArray servicesJsonArray = context.read("$.components.discoveryComposite.components.discoveryClient.details.services"); - List services = servicesJsonArray.stream().map(Objects::toString).map(String::toLowerCase).toList(); - - // Consider properly the case with multiple gateway services running on different ports. - callInternalPorts(gatewayHost); if (!areAllServicesUp) { log.debug("API ML is not ready, check which services are missing in the above messages"); } return areAllServicesUp; - } catch (PathNotFoundException | IOException e) { + } catch (PathNotFoundException e) { log.warn("Check failed on retrieving the information from document: {}", e.getMessage()); return false; } } + private boolean isModulithComponent(String serviceId) { + return StringUtils.equalsAnyIgnoreCase(serviceId, + CoreService.API_CATALOG.getServiceId(), + CoreService.CACHING.getServiceId(), + CoreService.ZAAS.getServiceId(), + CoreService.DISCOVERY.getServiceId() + ); + } + + private V getService(Map map, String key) { + V out = map.get(key.toLowerCase()); + if (out == null) { + out = map.get(key.toUpperCase()); + } + if (out == null) { + out = map.entrySet().stream() + .filter(e -> key.equalsIgnoreCase(e.getKey())) + .map(Map.Entry::getValue) + .findFirst() + .orElseThrow(() -> new NullPointerException("Cannot find record for " + key)); + } + return out; + } + private boolean checkServicesCount(DocumentContext context, ServiceConfiguration serviceConfiguration, String gatewayHost) { if (serviceConfiguration.getInstances() == 0) { return true; } - Integer amountOfActiveService = context.read("$.components.discoveryComposite.components.eureka.details.applications." + serviceConfiguration.getServiceId().toUpperCase()); + Map services = context.read("$.components.discoveryComposite.components.eureka.details.applications"); + Integer amountOfActiveService = getService(services, serviceConfiguration.getServiceId()); + if (amountOfActiveService == null) { + amountOfActiveService = services.get(serviceConfiguration.getServiceId().toUpperCase()); + } var expectedCount = serviceConfiguration.getInstances(); if (serviceConfiguration instanceof GatewayServiceConfiguration) { expectedCount = Integer.getInteger("environment.gwCount", expectedCount); } - if (IS_MODULITH_ENABLED && StringUtils.equalsAnyIgnoreCase(serviceConfiguration.getServiceId(), - CoreService.API_CATALOG.getServiceId(), CoreService.CACHING.getServiceId(), CoreService.ZAAS.getServiceId() - )) { + if (IS_MODULITH_ENABLED && isModulithComponent(serviceConfiguration.getServiceId())) { amountOfActiveService = Math.min(amountOfActiveService, 1); } @@ -166,37 +231,42 @@ private boolean checkServicesCount(DocumentContext context, ServiceConfiguration return true; } - private void callInternalPorts(String gatewayHost) throws IOException { + private boolean callInternalPorts() { var gatewayConfiguration = ConfigReader.environmentConfiguration().getGatewayServiceConfiguration(); if (StringUtils.isBlank(gatewayConfiguration.getInternalPorts())) { log.debug("No internal ports are defined"); - return; + return true; } String[] internalPorts = gatewayConfiguration.getInternalPorts().split(","); String[] hosts = gatewayConfiguration.getHost().split(","); for (int i = 0; i < Math.min(internalPorts.length, hosts.length); i++) { - if (!StringUtils.equals(hosts[i], gatewayHost)) continue; - log.debug("Trying to access the Gateway at port {}", internalPorts[i]); var requestToGateway = HttpRequestUtils.getRequest(healthEndpoint); requestToGateway.addHeader("Authorization", authorizationHeader); - var response = HttpClientUtils.client().execute(requestToGateway); - - if (response.getStatusLine().getStatusCode() != 200) { - log.debug("Response from gateway at {} was: {}", requestToGateway.getURI(), response.getEntity() != null ? EntityUtils.toString(response.getEntity()) : "undefined"); - throw new IOException(); + try { + var response = HttpClientUtils.client().execute(requestToGateway); + + if (response.getStatusLine().getStatusCode() != 200) { + log.debug("Response from gateway at {} was: {}", requestToGateway.getURI(), response.getEntity() != null ? EntityUtils.toString(response.getEntity()) : "undefined"); + return false; + } + } catch (IOException ioException) { + return false; } - } + return true; } private void callEurekaApps() { var discoveryServiceConfiguration = ConfigReader.environmentConfiguration().getDiscoveryServiceConfiguration(); HttpGet requestToEurekaApps = new HttpGet(HttpRequestUtils.getUriFromService(discoveryServiceConfiguration, "/eureka/apps")); - CloseableHttpClient client = HttpClients.custom().setSSLContext(SslContext.sslClientCertValid).build(); + CloseableHttpClient client = HttpClients.custom() + .setSSLContext(SslContext.sslClientCertValid) + .setSSLHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER) + .build(); try (client) { var response = client.execute(requestToEurekaApps); var entity = response.getEntity(); diff --git a/integration-tests/src/test/resources/environment-configuration-ha.yml b/integration-tests/src/test/resources/environment-configuration-ha.yml index 5d8b25a433..1dbb84cf3d 100644 --- a/integration-tests/src/test/resources/environment-configuration-ha.yml +++ b/integration-tests/src/test/resources/environment-configuration-ha.yml @@ -13,7 +13,7 @@ gatewayServiceConfiguration: bucketCapacity: 20 zaasConfiguration: scheme: https - host: zaas-service + host: zaas-service,zaas-service-2 port: 10023 discoveryServiceConfiguration: scheme: https From 293e558f86a2a9b70ac342943626c70add777711 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jare=C5=A1?= Date: Fri, 1 Aug 2025 14:15:35 +0200 Subject: [PATCH 08/11] NPE fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pavel Jareš --- .../apiml/startup/impl/ApiMediationLayerStartupChecker.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/integration-tests/src/test/java/org/zowe/apiml/startup/impl/ApiMediationLayerStartupChecker.java b/integration-tests/src/test/java/org/zowe/apiml/startup/impl/ApiMediationLayerStartupChecker.java index 93904a692e..6b4e562328 100644 --- a/integration-tests/src/test/java/org/zowe/apiml/startup/impl/ApiMediationLayerStartupChecker.java +++ b/integration-tests/src/test/java/org/zowe/apiml/startup/impl/ApiMediationLayerStartupChecker.java @@ -129,6 +129,9 @@ private boolean allServicesAreUp() { } private boolean checkHealthEndpointWithEureka(ServiceConfiguration serviceConfiguration) { + if (serviceConfiguration == null) { + return true; + } return Arrays.stream(serviceConfiguration.getHost().split(",")) .map(host -> getHealthEndpoint(serviceConfiguration, host)) .map(HttpGet::new) From 88ad35a99e047985f49e5c76d5b675922a9f58bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jare=C5=A1?= Date: Fri, 1 Aug 2025 14:41:57 +0200 Subject: [PATCH 09/11] wip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pavel Jareš --- .../apiml/startup/impl/ApiMediationLayerStartupChecker.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration-tests/src/test/java/org/zowe/apiml/startup/impl/ApiMediationLayerStartupChecker.java b/integration-tests/src/test/java/org/zowe/apiml/startup/impl/ApiMediationLayerStartupChecker.java index 6b4e562328..7ca493be12 100644 --- a/integration-tests/src/test/java/org/zowe/apiml/startup/impl/ApiMediationLayerStartupChecker.java +++ b/integration-tests/src/test/java/org/zowe/apiml/startup/impl/ApiMediationLayerStartupChecker.java @@ -218,7 +218,7 @@ private boolean checkServicesCount(DocumentContext context, ServiceConfiguration } if (IS_MODULITH_ENABLED && isModulithComponent(serviceConfiguration.getServiceId())) { - amountOfActiveService = Math.min(amountOfActiveService, 1); + expectedCount = Math.min(expectedCount, 1); } boolean isValidAmountOfServicesUp = amountOfActiveService != null && From 6d3d06dd24b135774efaac3e0ba26a67c264a310 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jare=C5=A1?= Date: Fri, 1 Aug 2025 15:02:08 +0200 Subject: [PATCH 10/11] fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pavel Jareš --- .github/workflows/integration-tests.yml | 7 +++++++ .../java/org/zowe/apiml}/EurekaHealthIndicatorApiml.java | 3 +-- .../startup/impl/ApiMediationLayerStartupChecker.java | 1 - 3 files changed, 8 insertions(+), 3 deletions(-) rename {apiml-common/src/main/java/org/zowe/apiml/product/compatibility => apiml/src/main/java/org/zowe/apiml}/EurekaHealthIndicatorApiml.java (96%) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 62fb547316..da05ac9ec3 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -1089,6 +1089,13 @@ jobs: image: ghcr.io/balhar-jakub/api-catalog-services:${{ github.run_id }}-${{ github.run_number }} volumes: - /api-defs:/api-defs + api-catalog-services-2: + image: ghcr.io/balhar-jakub/api-catalog-services:${{ github.run_id }}-${{ github.run_number }} + volumes: + - /api-defs:/api-defs + env: + APIML_SERVICE_HOSTNAME: api-catalog-services-2 + APIML_HEALTH_PROTECTED: false caching-service: image: ghcr.io/balhar-jakub/caching-service:${{ github.run_id }}-${{ github.run_number }} discoverable-client: diff --git a/apiml-common/src/main/java/org/zowe/apiml/product/compatibility/EurekaHealthIndicatorApiml.java b/apiml/src/main/java/org/zowe/apiml/EurekaHealthIndicatorApiml.java similarity index 96% rename from apiml-common/src/main/java/org/zowe/apiml/product/compatibility/EurekaHealthIndicatorApiml.java rename to apiml/src/main/java/org/zowe/apiml/EurekaHealthIndicatorApiml.java index c46afdfb9c..a4c7e4d007 100644 --- a/apiml-common/src/main/java/org/zowe/apiml/product/compatibility/EurekaHealthIndicatorApiml.java +++ b/apiml/src/main/java/org/zowe/apiml/EurekaHealthIndicatorApiml.java @@ -8,7 +8,7 @@ * Copyright Contributors to the Zowe Project. */ -package org.zowe.apiml.product.compatibility; +package org.zowe.apiml; import org.springframework.boot.actuate.health.Health; import org.springframework.boot.actuate.health.Status; @@ -18,7 +18,6 @@ import org.springframework.stereotype.Component; import java.util.Map; -import java.util.function.Function; import java.util.stream.Collectors; /** diff --git a/integration-tests/src/test/java/org/zowe/apiml/startup/impl/ApiMediationLayerStartupChecker.java b/integration-tests/src/test/java/org/zowe/apiml/startup/impl/ApiMediationLayerStartupChecker.java index 7ca493be12..b625e9d188 100644 --- a/integration-tests/src/test/java/org/zowe/apiml/startup/impl/ApiMediationLayerStartupChecker.java +++ b/integration-tests/src/test/java/org/zowe/apiml/startup/impl/ApiMediationLayerStartupChecker.java @@ -166,7 +166,6 @@ private boolean isApimlReadyByFullHealthEndpoint(DocumentContext context, String areAllServicesUp = false; } - if (!areAllServicesUp) { log.debug("API ML is not ready, check which services are missing in the above messages"); } From 38e6febb5aa75d2eaa715455d78c1e671794febc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jare=C5=A1?= Date: Fri, 1 Aug 2025 15:48:42 +0200 Subject: [PATCH 11/11] add log message MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pavel Jareš --- .../apiml/startup/impl/ApiMediationLayerStartupChecker.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/integration-tests/src/test/java/org/zowe/apiml/startup/impl/ApiMediationLayerStartupChecker.java b/integration-tests/src/test/java/org/zowe/apiml/startup/impl/ApiMediationLayerStartupChecker.java index b625e9d188..e1772b9355 100644 --- a/integration-tests/src/test/java/org/zowe/apiml/startup/impl/ApiMediationLayerStartupChecker.java +++ b/integration-tests/src/test/java/org/zowe/apiml/startup/impl/ApiMediationLayerStartupChecker.java @@ -123,7 +123,11 @@ private boolean allServicesAreUp() { .map(request -> { request.addHeader("Authorization", authorizationHeader); DocumentContext context = getDocumentAsContext(request); - return (context != null) && "UP".equals(context.read("$.status")); + boolean response = (context != null) && "UP".equals(context.read("$.status")); + if (!response) { + log.debug("Service {} is not ready at {} : {}", service.getServiceId(), request.getURI(), context); + } + return response; }) ).allMatch(x -> x); }