Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: register gateway to additional discovery service #3068

Merged
merged 60 commits into from
Sep 13, 2023
Merged
Show file tree
Hide file tree
Changes from 48 commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
0e909a9
chore: move babel to dev, modify webpack config, spring security
achmelo Aug 25, 2023
0817f17
fix: Integration tests for new Cloud Gateway routing (#3044)
arxioly Aug 28, 2023
60b9717
chore: update dependencies (#3048)
achmelo Aug 29, 2023
6317cbf
remove dependency from spring enabler (#3052)
pablocarle Aug 29, 2023
fa5e37d
[skip ci] Update version
zowe-robot Aug 29, 2023
406e71c
[Gradle Release plugin] [skip ci] Before tag commit 'v2.10.15'.
zowe-robot Aug 29, 2023
aaa0ef4
[Gradle Release plugin] Create new version: 'v2.11.0-SNAPSHOT'.
zowe-robot Aug 29, 2023
8889a4a
[skip ci] Update version
zowe-robot Aug 29, 2023
c2ef946
[Gradle Release plugin] [skip ci] Before tag commit 'v2.11.0'.
zowe-robot Aug 29, 2023
f90770b
[Gradle Release plugin] Create new version: 'v2.11.1-SNAPSHOT'.
zowe-robot Aug 29, 2023
eabedec
chore: Update UI per suggestions from UX + add keyboard shortcuts - M…
taban03 Aug 29, 2023
16348b5
GH2883 Move ServicesInfoService to the apiml-common (#3056)
cumarav Aug 30, 2023
7827a1e
feat: verify service SSO support from API ML (#3054)
matejpopda Aug 30, 2023
13b83a1
register GW into 2 DS with one being primary for auth, service info,..
achmelo Aug 31, 2023
329fe29
wrapper for discovery clients
achmelo Sep 1, 2023
97a409c
feat: Forward client certificate from central gateway to domain gatew…
weinfurt Aug 31, 2023
eaef561
chore: add media icon and change link color (#3057)
taban03 Sep 1, 2023
a2b37be
fix unregistering
achmelo Sep 4, 2023
b172b2f
cleanup
achmelo Sep 4, 2023
e48e661
IT for multiple registration
achmelo Sep 5, 2023
d81d465
revert reading of DC list to original version
achmelo Sep 5, 2023
b3e8dd9
revert checkstyle
achmelo Sep 5, 2023
be7365f
REMOVE! hardcode DS url for test
achmelo Sep 5, 2023
13cc61b
run new job only
achmelo Sep 5, 2023
5577e8b
skip tests
achmelo Sep 5, 2023
1af5b16
styles
achmelo Sep 5, 2023
f58d63b
align with original CITests
achmelo Sep 5, 2023
3394c03
add metrics service test
achmelo Sep 5, 2023
97d1cf9
Revert "run new job only"
achmelo Sep 5, 2023
09e054e
fix: Fixes for API Catalog standalone mode (#3050)
pablocarle Sep 1, 2023
3477c03
fix: header in api portal (#3059)
pablocarle Sep 4, 2023
18e7f2a
verify registration with second DS
achmelo Sep 6, 2023
4b24053
hardcode discovery host
achmelo Sep 6, 2023
f8b5bc1
revert local auth provider
achmelo Sep 6, 2023
04dc4fa
getting the list of discovery services from configuration
Shobhajayanna Sep 7, 2023
c6df4a8
create bean conditionally
achmelo Sep 7, 2023
bc06bb4
changed the name from discoveryServiceUrlsList to centralRegistryUrls
Shobhajayanna Sep 7, 2023
a1d6c8c
Changed name of a variable to url
Shobhajayanna Sep 7, 2023
2348210
enable second registration in CITests
achmelo Sep 7, 2023
1cb1897
Merge remote-tracking branch 'origin/v2.x.x' into reboot/gh2425/gw_mu…
achmelo Sep 7, 2023
c47cf50
default value must be empty
achmelo Sep 7, 2023
8f024c9
revert logging level
achmelo Sep 7, 2023
1a7b12a
fix styles
achmelo Sep 7, 2023
67f877c
code review, store unit test reports
achmelo Sep 7, 2023
c43694b
increase test coverage
achmelo Sep 8, 2023
0b99e2f
add licenses
achmelo Sep 8, 2023
a83ab73
AOP check is done also inside getTargetObject
achmelo Sep 8, 2023
9591b5e
remove empty test
achmelo Sep 8, 2023
5cad7ed
code review
achmelo Sep 11, 2023
846ad85
Merge branch 'v2.x.x' into reboot/gh2425/gw_multiple_ds
achmelo Sep 11, 2023
956dbd6
Revert "code review"
achmelo Sep 12, 2023
4968c91
shutdown discoveryclient in test
achmelo Sep 12, 2023
fcb2f63
Revert "Revert "code review""
achmelo Sep 12, 2023
3859beb
Merge remote-tracking branch 'origin/v2.x.x' into reboot/gh2425/gw_mu…
achmelo Sep 12, 2023
cfbdb58
skip test
achmelo Sep 12, 2023
499add9
debug system exit
achmelo Sep 12, 2023
ea2dcb8
return test
achmelo Sep 12, 2023
9730ef1
remove debugging
achmelo Sep 13, 2023
b0bbc3a
remove system exit
achmelo Sep 13, 2023
ac0c00b
separate startup check from build
achmelo Sep 13, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/ci-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,11 @@ jobs:
name: CITests-${{ env.JOB_ID }}
path: |
integration-tests/build/reports/**
gateway-service/build/reports/**
api-catalog-services/build/reports/**
caching-service/build/reports/**
cloud-gateway-service/build/reports/**
discovery-service/build/reports/**
metrics-service/build/reports/**

- uses: ./.github/actions/teardown
7 changes: 7 additions & 0 deletions .github/workflows/containers.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,19 @@ jobs:
image: ghcr.io/balhar-jakub/discovery-service:${{ github.run_id }}-${{ github.run_number }}
volumes:
- /api-defs:/api-defs
discovery-service-2:
image: ghcr.io/balhar-jakub/discovery-service:${{ github.run_id }}-${{ github.run_number }}
volumes:
- /api-defs:/api-defs
env:
APIML_SERVICE_HOSTNAME: discovery-service-2
gateway-service:
image: ghcr.io/balhar-jakub/gateway-service:${{ github.run_id }}-${{ github.run_number }}
env:
APIML_SECURITY_AUTH_JWT_CUSTOMAUTHHEADER: customJwtHeader
APIML_SECURITY_AUTH_PASSTICKET_CUSTOMUSERHEADER: customUserHeader
APIML_SECURITY_AUTH_PASSTICKET_CUSTOMAUTHHEADER: customPassticketHeader
APIML_SERVICE_CENTRALREGISTRYURLS: https://discovery-service-2:10011/eureka
mock-services:
image: ghcr.io/balhar-jakub/mock-services:${{ github.run_id }}-${{ github.run_number }}
metrics-service:
Expand Down
1 change: 0 additions & 1 deletion .run/GatewayApplication.run.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
</param>
</additionalParameters>
<option name="ALTERNATIVE_JRE_PATH" value="1.8 (2)" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
<module name="api-layer.gateway-service.main" />
<option name="SHORTEN_COMMAND_LINE" value="MANIFEST" />
<option name="SPRING_BOOT_MAIN_CLASS" value="org.zowe.apiml.gateway.GatewayApplication" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -299,5 +299,9 @@ public EurekaJerseyClient eurekaJerseyClient() {
return eurekaJerseyClientBuilder.build();
}

@Bean
public EurekaJerseyClientBuilder eurekaJerseyClientBuilder() {
return eurekaJerseyClientBuilder;
}

}
2 changes: 2 additions & 0 deletions config/local/gateway-service.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ apiml:
hostname: localhost
ipAddress: 127.0.0.1
port: 10010
centralRegistryUrls: https://localhost:10021/eureka/,https://localhost:10031/eureka/
achmelo marked this conversation as resolved.
Show resolved Hide resolved
discoveryServiceUrls: https://localhost:10011/eureka/

security:
allowTokenRefresh: true
webfinger:
Expand Down
1 change: 1 addition & 0 deletions gateway-package/src/main/resources/bin/start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ _BPX_JOBNAME=${ZWE_zowe_job_prefix}${GATEWAY_CODE} java \
-Dapiml.service.hostname=${ZWE_haInstance_hostname:-localhost} \
-Dapiml.service.port=${ZWE_configs_port:-7554} \
-Dapiml.service.discoveryServiceUrls=${ZWE_DISCOVERY_SERVICES_LIST:-"https://${ZWE_haInstance_hostname:-localhost}:${ZWE_components_discovery_port:-7553}/eureka/"} \
-Dapiml.service.centralRegistryUrls=${ZWE_configs_apiml_service_centralRegistryUrls:-} \
-Dapiml.service.allowEncodedSlashes=${ZWE_configs_apiml_service_allowEncodedSlashes:-true} \
-Dapiml.service.corsEnabled=${ZWE_configs_apiml_service_corsEnabled:-false} \
-Dapiml.service.externalUrl="${httpProtocol}://${ZWE_zowe_externalDomains_0}:${ZWE_zowe_externalPort}" \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,46 +14,86 @@
import com.netflix.appinfo.HealthCheckHandler;
import com.netflix.discovery.AbstractDiscoveryClientOptionalArgs;
import com.netflix.discovery.EurekaClientConfig;
import com.netflix.discovery.shared.transport.jersey.EurekaJerseyClientImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.netflix.eureka.EurekaClientConfigBean;
import org.springframework.cloud.netflix.eureka.MutableDiscoveryClientOptionalArgs;
import org.springframework.cloud.util.ProxyUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.zowe.apiml.gateway.discovery.ApimlDiscoveryClient;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* This configuration override bean EurekaClient with custom ApimlDiscoveryClient. This bean offer additional method
* fetchRegistry. User can call this method to asynchronously fetch new data from discovery service. There is no time
* to fetching.
* <p>
* Configuration also add listeners to call other beans waiting for fetch new registry. It speed up distribution of
* Configuration also add listeners to call other beans waiting for fetch new registry. It speeds up distribution of
* changes in whole gateway.
*/
@Configuration
@RequiredArgsConstructor
public class DiscoveryClientConfig {
private final ApplicationContext context;
private final AbstractDiscoveryClientOptionalArgs<?> optionalArgs;
private final EurekaJerseyClientImpl.EurekaJerseyClientBuilder eurekaJerseyClientBuilder;

@Value("${apiml.service.centralRegistryUrls:-}")
private String[] centralRegistryUrls;

@Bean(destroyMethod = "shutdown")
@RefreshScope
public ApimlDiscoveryClient eurekaClient(ApplicationInfoManager manager,
EurekaClientConfig config,
@Autowired(required = false) HealthCheckHandler healthCheckHandler
public ApimlDiscoveryClient primaryApimlEurekaClient(ApplicationInfoManager manager,
EurekaClientConfig config,
@Autowired(required = false) HealthCheckHandler healthCheckHandler
) {
ApplicationInfoManager appManager;
if (AopUtils.isAopProxy(manager)) {
appManager = ProxyUtils.getTargetObject(manager);
} else {
appManager = manager;
}
ApplicationInfoManager appManager = ProxyUtils.getTargetObject(manager);

final ApimlDiscoveryClient discoveryClientClient = new ApimlDiscoveryClient(appManager, config, this.optionalArgs, this.context);
discoveryClientClient.registerHealthCheck(healthCheckHandler);

return discoveryClientClient;
}

@Bean(destroyMethod = "shutdown")
@ConditionalOnProperty(name = "apiml.service.centralRegistryUrls")
@RefreshScope
public DiscoveryClientWrapper additionalDiscoverClientWrapper(ApplicationInfoManager manager,
achmelo marked this conversation as resolved.
Show resolved Hide resolved
EurekaClientConfig config,
@Autowired(required = false) HealthCheckHandler healthCheckHandler
) {
ApplicationInfoManager appManager = ProxyUtils.getTargetObject(manager);
List<ApimlDiscoveryClient> listOfDiscoveryClients = new ArrayList<>();
achmelo marked this conversation as resolved.
Show resolved Hide resolved

for (String url : centralRegistryUrls) {
achmelo marked this conversation as resolved.
Show resolved Hide resolved

EurekaClientConfigBean configBean = new EurekaClientConfigBean();
BeanUtils.copyProperties(config, configBean);

Map<String, String> urls = new HashMap<>();
urls.put("defaultZone", url);

configBean.setServiceUrl(urls);

MutableDiscoveryClientOptionalArgs args = new MutableDiscoveryClientOptionalArgs();
args.setEurekaJerseyClient(eurekaJerseyClientBuilder.build());

final ApimlDiscoveryClient discoveryClientClient = new ApimlDiscoveryClient(appManager, configBean, args, this.context);
discoveryClientClient.registerHealthCheck(healthCheckHandler);
listOfDiscoveryClients.add(discoveryClientClient);
}

return new DiscoveryClientWrapper(listOfDiscoveryClients);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* 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.gateway.config;

import lombok.AllArgsConstructor;
import lombok.Getter;
import org.zowe.apiml.gateway.discovery.ApimlDiscoveryClient;

import java.util.List;

@AllArgsConstructor
@Getter
public class DiscoveryClientWrapper {
private List<ApimlDiscoveryClient> discoveryClients;

public void shutdown() {
if (discoveryClients != null) {
achmelo marked this conversation as resolved.
Show resolved Hide resolved
discoveryClients.forEach(ApimlDiscoveryClient::shutdown);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

/**
* This controller allows control the caches about services. The main purpose is to evict cached data
* about services when a update happened in discovery service. Discovery service notifies about any
* about services when an update happened in discovery service. Discovery service notifies about any
* change to be sure that cache on gateway is still valid.
*/
@AllArgsConstructor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
import java.util.concurrent.TimeUnit;

/**
* Custom implementation of Eureka client. It support additional feature:
* Custom implementation of Eureka client. It supports additional feature:
* - fetchRegistry - invoke asynchronous task to update registry from discovery client immediatelly
*/
public class ApimlDiscoveryClient extends CloudEurekaClient {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,12 @@

import com.fasterxml.jackson.databind.ObjectMapper;
import com.netflix.discovery.DiscoveryClient;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.ResourceAccessException;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestClientResponseException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.client.*;
import org.zowe.apiml.message.log.ApimlLogger;
import org.zowe.apiml.product.logging.annotations.InjectApimlLogger;
import org.zowe.apiml.security.common.config.AuthConfigurationProperties;
Expand All @@ -30,14 +26,14 @@
import org.zowe.apiml.util.EurekaUtils;

import javax.net.ssl.SSLHandshakeException;

import java.net.ConnectException;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.function.Supplier;

import static org.zowe.apiml.security.SecurityUtils.readPassword;

@RequiredArgsConstructor
@Slf4j
public abstract class AbstractZosmfService {

Expand All @@ -54,18 +50,6 @@ public abstract class AbstractZosmfService {
protected final RestTemplate restTemplateWithoutKeystore;
protected final ObjectMapper securityObjectMapper;

protected AbstractZosmfService(
AuthConfigurationProperties authConfigurationProperties,
DiscoveryClient discovery,
@Qualifier("restTemplateWithoutKeystore") RestTemplate restTemplateWithoutKeystore,
ObjectMapper securityObjectMapper
) {
this.authConfigurationProperties = authConfigurationProperties;
this.discovery = discovery;
this.restTemplateWithoutKeystore = restTemplateWithoutKeystore;
this.securityObjectMapper = securityObjectMapper;
}

/**
* @return serviceId of z/OSMF service from configuration, which is used
*/
Expand Down Expand Up @@ -121,7 +105,6 @@ protected String getAuthenticationValue(Authentication authentication) {
*
* @param zosmf the z/OSMF service id
* @return the uri
*
* @throws ServiceNotAccessibleException if z/OSMF is not available in discovery service
*/
protected String getURI(String zosmf) {
Expand Down
3 changes: 3 additions & 0 deletions gateway-service/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@ spring:
application:
name: ${apiml.service.id}
cloud:
discovery:
reactive:
enabled: false
compatibilityVerifier:
enabled: false # Should be removed when upgrade to Spring Cloud 3.x
client:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* 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.gateway.config;

import com.netflix.appinfo.*;
import com.netflix.discovery.shared.transport.jersey.EurekaJerseyClientImpl;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.cloud.netflix.eureka.EurekaClientConfigBean;
import org.springframework.context.ApplicationContext;
import org.springframework.test.util.ReflectionTestUtils;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

class DiscoveryClientBeanTest {
DiscoveryClientConfig dcConfig;

@BeforeEach
void setup() {
ApplicationContext context = mock(ApplicationContext.class);
EurekaJerseyClientImpl.EurekaJerseyClientBuilder builder = mock(EurekaJerseyClientImpl.EurekaJerseyClientBuilder.class);
dcConfig = new DiscoveryClientConfig(context, null, builder);
}

@Test
void givenListOfCentralRegistryURLs_thenCreateNewDiscoveryClientForEach() {
String[] centralRegistryUrls = {"https://localhost:10021/eureka", "https://localhost:10011/eureka"};
ReflectionTestUtils.setField(dcConfig, "centralRegistryUrls", centralRegistryUrls);
ApplicationInfoManager manager = mock(ApplicationInfoManager.class);
InstanceInfo info = mock(InstanceInfo.class);
when(manager.getInfo()).thenReturn(info);
when(info.getIPAddr()).thenReturn("127.0.0.1");
when(info.getDataCenterInfo()).thenReturn(new MyDataCenterInfo(DataCenterInfo.Name.MyOwn));
LeaseInfo leaseInfo = mock(LeaseInfo.class);
when(info.getLeaseInfo()).thenReturn(leaseInfo);
EurekaClientConfigBean bean = new EurekaClientConfigBean();
DiscoveryClientWrapper wrapper = dcConfig.additionalDiscoverClientWrapper(manager, bean, null);
assertEquals(2, wrapper.getDiscoveryClients().size());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* 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.gateway.config;

import org.junit.jupiter.api.Test;
import org.zowe.apiml.gateway.discovery.ApimlDiscoveryClient;

import java.util.Arrays;

import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.mockito.Mockito.*;

class DiscoveryClientWrapperTest {

@Test
void givenExistingListOfClient_thenCallShutdownForEach() {
ApimlDiscoveryClient client1 = mock(ApimlDiscoveryClient.class);
ApimlDiscoveryClient client2 = mock(ApimlDiscoveryClient.class);
DiscoveryClientWrapper wrapper = new DiscoveryClientWrapper(Arrays.asList(client1, client2));
wrapper.shutdown();
verify(client1, times(1)).shutdown();
verify(client2, times(1)).shutdown();
}

@Test
void givenNullListOfClient_thenSkipShutdown() {
DiscoveryClientWrapper wrapper = new DiscoveryClientWrapper(null);
assertDoesNotThrow(wrapper::shutdown);
}
}
Loading
Loading