Skip to content

Commit

Permalink
feat: override external URL for additional registration (#3935)
Browse files Browse the repository at this point in the history
Signed-off-by: ac892247 <[email protected]>
Signed-off-by: Pavel Jareš <[email protected]>
Signed-off-by: Pablo Carle <[email protected]>
Co-authored-by: Pavel Jareš <[email protected]>
Co-authored-by: Pavel Jareš <[email protected]>
Co-authored-by: Pablo Carle <[email protected]>
Co-authored-by: Pablo Carle <[email protected]>
  • Loading branch information
5 people authored Jan 14, 2025
1 parent 8704622 commit d5dd912
Show file tree
Hide file tree
Showing 6 changed files with 282 additions and 14 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/integration-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,7 @@ jobs:
env:
APIML_SERVICE_APIMLID: central-apiml
APIML_SERVICE_HOSTNAME: gateway-service
APIML_SERVICE_EXTERNALURL: https://gateway-service:10010
APIML_GATEWAY_REGISTRY_ENABLED: true
APIML_SECURITY_X509_REGISTRY_ALLOWEDUSERS: USER,UNKNOWNUSER
mock-services:
Expand All @@ -313,6 +314,7 @@ jobs:
env:
APIML_SERVICE_APIMLID: domain-apiml
APIML_SERVICE_HOSTNAME: gateway-service-2
APIML_SERVICE_EXTERNALURL: https://gateway-service-2:10010
APIML_GATEWAY_REGISTRY_ENABLED: false
APIML_SECURITY_X509_REGISTRY_ALLOWEDUSERS: USER,UNKNOWNUSER
APIML_SERVICE_DISCOVERYSERVICEURLS: https://discovery-service-2:10031/eureka/
Expand Down
1 change: 1 addition & 0 deletions containers/gateway-service/prepare.sh
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ buildPackage $gateway_package "packageApiGateway"
buildApimlCommonPackage

preparePackage $gateway_package
preparePackage $apiml_common_package "apiml-common-lib"
prepareBasicFiles

copyToBuildContext $linux_distro $cpu_arch
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,7 @@

package org.zowe.apiml.gateway.config;

import com.netflix.appinfo.ApplicationInfoManager;
import com.netflix.appinfo.EurekaInstanceConfig;
import com.netflix.appinfo.HealthCheckHandler;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.appinfo.*;
import com.netflix.discovery.EurekaClient;
import com.netflix.discovery.EurekaClientConfig;
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
Expand All @@ -22,6 +19,8 @@
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.resolver.DefaultAddressResolverGroup;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.experimental.Delegate;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.aop.support.AopUtils;
Expand Down Expand Up @@ -56,6 +55,7 @@
import org.springframework.util.CollectionUtils;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.util.UriComponentsBuilder;
import org.springframework.web.util.pattern.PathPatternParser;
import org.zowe.apiml.config.AdditionalRegistration;
import org.zowe.apiml.config.AdditionalRegistrationCondition;
Expand All @@ -74,6 +74,8 @@

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManagerFactory;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.KeyStore;
import java.time.Duration;
import java.util.ArrayList;
Expand Down Expand Up @@ -147,6 +149,9 @@ public ConnectionsConfig(ApplicationContext context) {
this.context = context;
}

@Value("${apiml.service.externalUrl:}")
private String externalUrl;

@PostConstruct
public void updateConfigParameters() {
ServerProperties serverProperties = context.getBean(ServerProperties.class);
Expand Down Expand Up @@ -328,13 +333,13 @@ private CloudEurekaClient registerInTheApimlInstance(EurekaClientConfig config,
configBean.setServiceUrl(urls);

EurekaInstanceConfig eurekaInstanceConfig = appManager.getEurekaInstanceConfig();
InstanceInfo newInfo = eurekaFactory.createInstanceInfo(eurekaInstanceConfig);
InstanceInfo newInfo = create(eurekaInstanceConfig);

updateMetadata(newInfo, apimlRegistration);

RestTemplateDiscoveryClientOptionalArgs args1 = defaultArgs(getDefaultEurekaClientHttpRequestFactorySupplier());
RestTemplateTransportClientFactories factories = new RestTemplateTransportClientFactories(args1);
return eurekaFactory.createCloudEurekaClient(eurekaInstanceConfig, newInfo, configBean, context, factories, args1);
return eurekaFactory.createCloudEurekaClient(new AdditionalEurekaConfiguration(eurekaInstanceConfig, newInfo), newInfo, configBean, context, factories, args1);
}

private boolean isRouteKey(String key) {
Expand Down Expand Up @@ -416,4 +421,126 @@ public CorsUtils corsUtils() {
return new CorsUtils(corsEnabled, null);
}

public InstanceInfo create(EurekaInstanceConfig config) {
LeaseInfo.Builder leaseInfoBuilder = LeaseInfo.Builder.newBuilder()
.setRenewalIntervalInSecs(config.getLeaseRenewalIntervalInSeconds())
.setDurationInSecs(config.getLeaseExpirationDurationInSeconds());

// Builder the instance information to be registered with eureka
// server
InstanceInfo.Builder builder = InstanceInfo.Builder.newBuilder();

String namespace = config.getNamespace();
if (!namespace.endsWith(".")) {
namespace = namespace + ".";
}
URL url;
try {
url = new URL(externalUrl);
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}

builder
.setNamespace(namespace)
.setAppName(config.getAppname())
.setInstanceId(config.getInstanceId())
.setAppGroupName(config.getAppGroupName())
.setDataCenterInfo(config.getDataCenterInfo())
.setIPAddr(config.getIpAddress())
.setHostName(url.getHost())
.setPort(url.getPort())
.enablePort(InstanceInfo.PortType.UNSECURE, config.isNonSecurePortEnabled())
.setSecurePort(url.getPort())
.enablePort(InstanceInfo.PortType.SECURE, config.getSecurePortEnabled())
.setVIPAddress(config.getVirtualHostName())
.setSecureVIPAddress(config.getSecureVirtualHostName())
.setHomePageUrl(null, UriComponentsBuilder.fromUriString(externalUrl).path(config.getHomePageUrlPath()).toUriString())
.setStatusPageUrl(null, UriComponentsBuilder.fromUriString(externalUrl).path(config.getStatusPageUrlPath()).toUriString())
.setHealthCheckUrls(config.getHealthCheckUrlPath(), null,null)
.setASGName(config.getASGName());

// Start off with the STARTING state to avoid traffic
if (!config.isInstanceEnabledOnit()) {
InstanceInfo.InstanceStatus initialStatus = InstanceInfo.InstanceStatus.STARTING;
if (log.isInfoEnabled()) {
log.info("Setting initial instance status as: " + initialStatus);
}
builder.setStatus(initialStatus);
}
else {
if (log.isInfoEnabled()) {
log.info("Setting initial instance status as: " + InstanceInfo.InstanceStatus.UP
+ ". This may be too early for the instance to advertise itself as available. "
+ "You would instead want to control this via a healthcheck handler.");
}
}

// Add any user-specific metadata information
var fromUrl = UriComponentsBuilder.fromUriString(config.getHomePageUrl()).path("/").toUriString();
var toUrl = UriComponentsBuilder.fromUriString(externalUrl).path("/").toUriString();
for (Map.Entry<String, String> mapEntry : config.getMetadataMap().entrySet()) {
String key = mapEntry.getKey();
String value = mapEntry.getValue();
// only add the metadata if the value is present
if (value != null && !value.isEmpty()) {
value = value.replace(fromUrl, toUrl);
builder.add(key, value);
}
}

InstanceInfo instanceInfo = builder.build();
instanceInfo.setLeaseInfo(leaseInfoBuilder.build());
return instanceInfo;
}

@RequiredArgsConstructor
static class AdditionalEurekaConfiguration implements EurekaInstanceConfig {

@Delegate(excludes = NonDelegated.class)
private final EurekaInstanceConfig eurekaInstanceConfig;

private final InstanceInfo instanceInfo;

@Override
public String getHostName(boolean refresh) {
eurekaInstanceConfig.getHostName(refresh);
return instanceInfo.getHostName();
}

@Override
public String getHealthCheckUrl() {
if (instanceInfo.isPortEnabled(InstanceInfo.PortType.UNSECURE)) {
return instanceInfo.getHealthCheckUrl();
}
return instanceInfo.getSecureHealthCheckUrl();
}

@Override
public String getSecureHealthCheckUrl() {
return instanceInfo.getSecureHealthCheckUrl();
}

@Override
public String getHomePageUrl() {
return instanceInfo.getHomePageUrl();
}

@Override
public String getStatusPageUrl() {
return instanceInfo.getStatusPageUrl();
}

interface NonDelegated {

String getHostName(boolean refresh);
String getHealthCheckUrl();
String getSecureHealthCheckUrl();
String getHomePageUrl();
String getStatusPageUrl();

}

}

}
4 changes: 4 additions & 0 deletions gateway-service/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@ eureka:
#ports are computed in code
homePageUrl: ${apiml.service.scheme}://${apiml.service.hostname}:${apiml.service.port}/
healthCheckUrl: ${apiml.service.scheme}://${apiml.service.hostname}:${apiml.service.port}/application/health
healthCheckUrlPath: /application/health
port: ${apiml.service.port}
securePort: ${apiml.service.port}
nonSecurePortEnabled: ${apiml.service.nonSecurePortEnabled}
securePortEnabled: ${apiml.service.securePortEnabled}
statusPageUrl: ${apiml.service.scheme}://${apiml.service.hostname}:${apiml.service.port}/application/info
statusPageUrlPath: /application/info
metadata-map:
apiml:
registrationType: primary
Expand Down Expand Up @@ -103,6 +106,7 @@ apiml:
preferIpAddress: false
nonSecurePortEnabled: false
securePortEnabled: true
externalUrl: ${apiml.service.scheme}://${apiml.service.hostname}:${apiml.service.port}
security:
headersToBeCleared: X-Certificate-Public,X-Certificate-DistinguishedName,X-Certificate-CommonName
ssl:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ public void setUp() throws Exception {
metadata.put("apiml.routes.0.gatewayUrl", "/api/v1");
metadata.put("apiml.routes.0.serviceUrl", "/service/api/v1");
instanceInfo = InstanceInfo.Builder.newBuilder().setAppName("service1").setMetadata(metadata).build();
lenient().when(eurekaFactory.createInstanceInfo(any())).thenReturn(instanceInfo);
lenient().doReturn(instanceInfo).when(configSpy).create(any());
}

@Test
Expand Down
Loading

0 comments on commit d5dd912

Please sign in to comment.