Skip to content

Commit

Permalink
Cloud Gateway - create additional registrations - Unit tests
Browse files Browse the repository at this point in the history
Signed-off-by: alexandr cumarav <[email protected]>
  • Loading branch information
cumarav committed Nov 3, 2023
1 parent b9634a8 commit 18c2296
Show file tree
Hide file tree
Showing 5 changed files with 199 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
import org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping;
import org.springframework.cloud.netflix.eureka.CloudEurekaClient;
import org.springframework.cloud.netflix.eureka.EurekaClientConfigBean;
import org.springframework.cloud.netflix.eureka.InstanceInfoFactory;
import org.springframework.cloud.netflix.eureka.MutableDiscoveryClientOptionalArgs;
import org.springframework.cloud.util.ProxyUtils;
import org.springframework.context.ApplicationContext;
Expand Down Expand Up @@ -226,41 +225,39 @@ public List<AdditionalRegistration> additionalRegistration(StandardEnvironment e
@Bean(destroyMethod = "shutdown")
@Conditional(AdditionalRegistrationCondition.class)
@RefreshScope
public AdditionalEurekaClientsHolder additionalDiscoveryClientWrapper(ApplicationInfoManager manager,
EurekaClientConfig config,
List<AdditionalRegistration> additionalRegistrations,
@Autowired(required = false) HealthCheckHandler healthCheckHandler
public AdditionalEurekaClientsHolder additionalEurekaClientsHolder(ApplicationInfoManager manager,
EurekaClientConfig config,
List<AdditionalRegistration> additionalRegistrations,
EurekaFactory eurekaFactory,
@Autowired(required = false) HealthCheckHandler healthCheckHandler
) {
List<CloudEurekaClient> additionalClients = new ArrayList<>(additionalRegistrations.size());
for (AdditionalRegistration apimlRegistration : additionalRegistrations) {
CloudEurekaClient cloudEurekaClient = registerInTheApimlInstance(config, apimlRegistration, manager);
CloudEurekaClient cloudEurekaClient = registerInTheApimlInstance(config, apimlRegistration, manager, eurekaFactory);
additionalClients.add(cloudEurekaClient);
cloudEurekaClient.registerHealthCheck(healthCheckHandler);
}
return new AdditionalEurekaClientsHolder(additionalClients);
}

private CloudEurekaClient registerInTheApimlInstance(EurekaClientConfig config, AdditionalRegistration apimlRegistration, ApplicationInfoManager appManager) {
private CloudEurekaClient registerInTheApimlInstance(EurekaClientConfig config, AdditionalRegistration apimlRegistration, ApplicationInfoManager appManager, EurekaFactory eurekaFactory) {

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

Map<String, String> urls = new HashMap<>();
log.debug("additional registration: {}", apimlRegistration.getDiscoveryServiceUrls());
Map<String, String> urls = new HashMap<>();
urls.put(DEFAULT_ZONE, apimlRegistration.getDiscoveryServiceUrls());

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

EurekaJerseyClient jerseyClient = factory().createEurekaJerseyClientBuilder(eurekaServerUrl, serviceId).build();
MutableDiscoveryClientOptionalArgs args = new MutableDiscoveryClientOptionalArgs();
args.setEurekaJerseyClient(factory().createEurekaJerseyClientBuilder(eurekaServerUrl, serviceId).build());
InstanceInfo newInfo = createInstanceInfo(appManager.getEurekaInstanceConfig());
args.setEurekaJerseyClient(jerseyClient);

ApplicationInfoManager perClientAppManager = new ApplicationInfoManager(appManager.getEurekaInstanceConfig(), newInfo, null);
return new CloudEurekaClient(perClientAppManager, configBean, args, context);
}
EurekaInstanceConfig eurekaInstanceConfig = appManager.getEurekaInstanceConfig();
InstanceInfo newInfo = eurekaFactory.createInstanceInfo(eurekaInstanceConfig);

InstanceInfo createInstanceInfo(EurekaInstanceConfig instanceConfig) {
return new InstanceInfoFactory().create(instanceConfig);
return eurekaFactory.createCloudEurekaClient(eurekaInstanceConfig, newInfo, configBean, args, context);
}

@Bean
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* This program and the accompanying materials are made available under the terms of the
* Eclipse Public License v2.0 which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v20.html
*
* SPDX-License-Identifier: EPL-2.0
*
* Copyright Contributors to the Zowe Project.
*/

package org.zowe.apiml.cloudgatewayservice.config;

import com.netflix.appinfo.ApplicationInfoManager;
import com.netflix.appinfo.EurekaInstanceConfig;
import com.netflix.appinfo.InstanceInfo;
import org.springframework.cloud.netflix.eureka.CloudEurekaClient;
import org.springframework.cloud.netflix.eureka.EurekaClientConfigBean;
import org.springframework.cloud.netflix.eureka.InstanceInfoFactory;
import org.springframework.cloud.netflix.eureka.MutableDiscoveryClientOptionalArgs;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

/**
* Eureka dependencies injection helper
*/
@Component
public class EurekaFactory {

/**
* Create new copy of instance info
*
* @param instanceConfig eureka instance config to copy from
*/
InstanceInfo createInstanceInfo(EurekaInstanceConfig instanceConfig) {
return new InstanceInfoFactory().create(instanceConfig);
}

public CloudEurekaClient createCloudEurekaClient(EurekaInstanceConfig eurekaInstanceConfig, InstanceInfo newInfo, EurekaClientConfigBean configBean, MutableDiscoveryClientOptionalArgs args, ApplicationContext context) {
ApplicationInfoManager perClientAppManager = new ApplicationInfoManager(eurekaInstanceConfig, newInfo, null);
return new CloudEurekaClient(perClientAppManager, configBean, args, context);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/*
* This program and the accompanying materials are made available under the terms of the
* Eclipse Public License v2.0 which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v20.html
*
* SPDX-License-Identifier: EPL-2.0
*
* Copyright Contributors to the Zowe Project.
*/

package org.zowe.apiml.cloudgatewayservice.config;

import com.netflix.appinfo.ApplicationInfoManager;
import com.netflix.appinfo.EurekaInstanceConfig;
import com.netflix.appinfo.HealthCheckHandler;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.discovery.EurekaClientConfig;
import com.netflix.discovery.shared.transport.jersey.EurekaJerseyClientImpl;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.cloud.netflix.eureka.CloudEurekaClient;
import org.springframework.cloud.netflix.eureka.EurekaClientConfigBean;
import org.springframework.context.ApplicationContext;
import org.zowe.apiml.config.AdditionalRegistration;
import org.zowe.apiml.security.HttpsFactory;

import java.util.AbstractMap;

import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.cloud.netflix.eureka.EurekaClientConfigBean.DEFAULT_ZONE;

@ExtendWith(MockitoExtension.class)
public class AdditionalRegistrationTest {

private ConnectionsConfig connectionsConfig;
@Mock
private CloudEurekaClient additionalClientOne;
@Mock
private CloudEurekaClient additionalClientTwo;
@Mock
private EurekaFactory eurekaFactory;
@Mock
ApplicationContext context;

@BeforeEach
void setUp() {
connectionsConfig = new ConnectionsConfig(context);
}

@ExtendWith(MockitoExtension.class)
@Nested
class WhenInitializingAdditionalRegistrations {
private ConnectionsConfig configSpy;
@Mock
private ApplicationInfoManager manager;
@Mock
private EurekaClientConfig clientConfig;
@Mock
private HealthCheckHandler healthCheckHandler;
@Mock
private HttpsFactory httpsFactory;

@Captor
private ArgumentCaptor<EurekaClientConfigBean> clientConfigCaptor;

private final AdditionalRegistration registration = AdditionalRegistration.builder().discoveryServiceUrls("https://another-eureka-1").build();

@BeforeEach
public void setUp() {
configSpy = Mockito.spy(connectionsConfig);
lenient().doReturn(httpsFactory).when(configSpy).factory();
lenient().when(httpsFactory.createEurekaJerseyClientBuilder(any(), any())).thenReturn(mock(EurekaJerseyClientImpl.EurekaJerseyClientBuilder.class));

lenient().when(eurekaFactory.createCloudEurekaClient(any(), any(), clientConfigCaptor.capture(), any(), any())).thenReturn(additionalClientOne, additionalClientTwo);
}

@Test
void shouldCreateEurekaClientForAdditionalDiscoveryUrl() {

AdditionalEurekaClientsHolder holder = configSpy.additionalEurekaClientsHolder(manager, clientConfig, singletonList(registration), eurekaFactory, healthCheckHandler);

assertThat(holder.getDiscoveryClients()).hasSize(1);
EurekaClientConfigBean eurekaClientConfigBean = clientConfigCaptor.getValue();
assertThat(eurekaClientConfigBean.getServiceUrl()).containsOnly(new AbstractMap.SimpleEntry<String, String>(DEFAULT_ZONE, "https://another-eureka-1"));
}

@Test
void shouldCreateTwoAdditionalRegistrations() {
AdditionalRegistration secondRegistration = AdditionalRegistration.builder().discoveryServiceUrls("https://another-eureka-2").build();
AdditionalEurekaClientsHolder holder = configSpy.additionalEurekaClientsHolder(manager, clientConfig, asList(registration, secondRegistration), eurekaFactory, healthCheckHandler);

assertThat(holder.getDiscoveryClients()).hasSize(2);
verify(additionalClientOne).registerHealthCheck(healthCheckHandler);
verify(additionalClientTwo).registerHealthCheck(healthCheckHandler);
}

@Test
void shouldCreateInstanceInfoFromEurekaConfig() {
EurekaInstanceConfig config = mock(EurekaInstanceConfig.class);
when(config.getNamespace()).thenReturn("");
when(config.getAppname()).thenReturn("CLOUD-GATEWAY");

InstanceInfo instanceInfo = new EurekaFactory().createInstanceInfo(config);

assertThat(instanceInfo.getAppName()).isEqualTo("CLOUD-GATEWAY");
}
}

@Nested
class WhenClientHolderShutDown {
@Test
void shouldTriggerShutdownCallToWrappedClients() {
AdditionalEurekaClientsHolder holder = new AdditionalEurekaClientsHolder(asList(additionalClientOne, additionalClientTwo));
holder.shutdown();

verify(additionalClientOne).shutdown();
verify(additionalClientTwo).shutdown();
}

@Test
void shouldHandleNullsOnShutdownCall() {
AdditionalEurekaClientsHolder holder = new AdditionalEurekaClientsHolder(null);
holder.shutdown();

assertThat(holder.getDiscoveryClients()).isNull();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,15 @@
import com.netflix.appinfo.HealthCheckHandler;
import com.netflix.discovery.EurekaClientConfig;
import com.netflix.discovery.shared.transport.jersey.EurekaJerseyClient;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.netflix.eureka.CloudEurekaClient;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.test.util.ReflectionTestUtils;

import java.util.Arrays;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.verify;

@SpringBootTest
@ComponentScan(basePackages = "org.zowe.apiml.cloudgatewayservice")
Expand Down Expand Up @@ -100,41 +92,6 @@ void whenKeystore_thenDoNothing() {
assertThat(ReflectionTestUtils.getField(connectionsConfig, "keyStorePassword")).isNull();
assertThat(ReflectionTestUtils.getField(connectionsConfig, "trustStorePassword")).isNull();
}

}

@Nested
@ExtendWith(MockitoExtension.class)
class WhenInitializingAdditionalRegistrations {
private ConnectionsConfig config;

@Mock
private CloudEurekaClient additionalClientOne;
@Mock
private CloudEurekaClient additionalClientTwo;

@BeforeEach
public void setUp() {
ConnectionsConfig config = Mockito.spy(connectionsConfig);
}


@Test
void shouldTriggerShutdownCallToWrappedClients() {
AdditionalEurekaClientsHolder holder = new AdditionalEurekaClientsHolder(Arrays.asList(additionalClientOne, additionalClientTwo));
holder.shutdown();

verify(additionalClientOne).shutdown();
verify(additionalClientTwo).shutdown();
}

@Test
void shouldHandleNullOnShutdownCall() {
AdditionalEurekaClientsHolder holder = new AdditionalEurekaClientsHolder(null);
holder.shutdown();

assertThat(holder.getDiscoveryClients()).isNull();
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ class CentralRegistryTest implements TestWithStartedInstances {
static void setupAll() {
//In order to avoid config customization
ConfigReader.environmentConfiguration().getGatewayServiceConfiguration().setInstances(2);
ConfigReader.environmentConfiguration().getGatewayServiceConfiguration().setInstances(2);

TlsConfiguration tlsCfg = ConfigReader.environmentConfiguration().getTlsConfiguration();
SslContextConfigurer sslContextConfigurer = new SslContextConfigurer(tlsCfg.getKeyStorePassword(), tlsCfg.getClientKeystore(), tlsCfg.getKeyStore());
Expand Down

0 comments on commit 18c2296

Please sign in to comment.