Skip to content

Commit 42ae70d

Browse files
authored
fix: accept list of discovery services in catalog (#1376)
* accept list of DSs in catalog Signed-off-by: achmelo <a.chmelo@gmail.com>
1 parent 73e71ef commit 42ae70d

File tree

5 files changed

+99
-64
lines changed

5 files changed

+99
-64
lines changed

api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/instance/InstanceRefreshService.java

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,29 +9,22 @@
99
*/
1010
package org.zowe.apiml.apicatalog.instance;
1111

12-
import org.zowe.apiml.apicatalog.model.APIContainer;
13-
import org.zowe.apiml.apicatalog.services.cached.CachedProductFamilyService;
14-
import org.zowe.apiml.apicatalog.services.cached.CachedServicesService;
15-
import org.zowe.apiml.message.log.ApimlLogger;
16-
import org.zowe.apiml.product.logging.annotations.InjectApimlLogger;
1712
import com.netflix.appinfo.InstanceInfo;
1813
import com.netflix.discovery.shared.Application;
1914
import com.netflix.discovery.shared.Applications;
2015
import lombok.RequiredArgsConstructor;
2116
import lombok.extern.slf4j.Slf4j;
2217
import org.springframework.scheduling.annotation.Scheduled;
2318
import org.springframework.stereotype.Service;
19+
import org.zowe.apiml.apicatalog.model.APIContainer;
20+
import org.zowe.apiml.apicatalog.services.cached.CachedProductFamilyService;
21+
import org.zowe.apiml.apicatalog.services.cached.CachedServicesService;
22+
import org.zowe.apiml.message.log.ApimlLogger;
23+
import org.zowe.apiml.product.logging.annotations.InjectApimlLogger;
2424

2525
import java.util.HashSet;
2626
import java.util.Set;
27-
import java.util.concurrent.Callable;
28-
import java.util.concurrent.ExecutionException;
29-
import java.util.concurrent.ExecutorService;
30-
import java.util.concurrent.Future;
31-
import java.util.concurrent.LinkedBlockingQueue;
32-
import java.util.concurrent.ThreadPoolExecutor;
33-
import java.util.concurrent.TimeUnit;
34-
import java.util.concurrent.TimeoutException;
27+
import java.util.concurrent.*;
3528

3629
import static org.zowe.apiml.constants.EurekaMetadataDefinition.CATALOG_ID;
3730

api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/instance/InstanceRetrievalService.java

Lines changed: 52 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,6 @@
99
*/
1010
package org.zowe.apiml.apicatalog.instance;
1111

12-
import org.zowe.apiml.apicatalog.discovery.DiscoveryConfigProperties;
13-
import org.zowe.apiml.message.log.ApimlLogger;
14-
import org.zowe.apiml.product.instance.InstanceInitializationException;
15-
import org.zowe.apiml.product.logging.annotations.InjectApimlLogger;
16-
import org.zowe.apiml.product.registry.ApplicationWrapper;
1712
import com.fasterxml.jackson.annotation.JsonInclude;
1813
import com.fasterxml.jackson.databind.DeserializationFeature;
1914
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -27,13 +22,19 @@
2722
import org.springframework.http.converter.StringHttpMessageConverter;
2823
import org.springframework.stereotype.Service;
2924
import org.springframework.web.client.RestTemplate;
25+
import org.zowe.apiml.apicatalog.discovery.DiscoveryConfigProperties;
26+
import org.zowe.apiml.message.log.ApimlLogger;
27+
import org.zowe.apiml.product.instance.InstanceInitializationException;
28+
import org.zowe.apiml.product.logging.annotations.InjectApimlLogger;
29+
import org.zowe.apiml.product.registry.ApplicationWrapper;
3030

3131
import javax.validation.constraints.NotBlank;
3232
import java.io.IOException;
3333
import java.nio.charset.Charset;
3434
import java.util.ArrayList;
3535
import java.util.Base64;
3636
import java.util.Collections;
37+
import java.util.List;
3738

3839
/**
3940
* Service for instance retrieval from Eureka
@@ -72,22 +73,21 @@ public InstanceInfo getInstanceInfo(@NotBlank(message = "Service Id must be supp
7273
return null;
7374
}
7475

75-
InstanceInfo instanceInfo = null;
76-
try {
77-
Pair<String, Pair<String, String>> requestInfo = constructServiceInfoQueryRequest(serviceId, false);
78-
79-
// call Eureka REST endpoint to fetch single or all Instances
80-
ResponseEntity<String> response = queryDiscoveryForInstances(requestInfo);
81-
if (response.getStatusCode().is2xxSuccessful()) {
82-
instanceInfo = extractSingleInstanceFromApplication(serviceId, requestInfo.getLeft(), response);
76+
List<Pair<String, Pair<String, String>>> requestInfoList = constructServiceInfoQueryRequest(serviceId, false);
77+
// iterate over list of discovery services, return at first success
78+
for (Pair<String, Pair<String, String>> requestInfo : requestInfoList) {
79+
// call Eureka REST endpoint to fetch single or all Instances
80+
try {
81+
ResponseEntity<String> response = queryDiscoveryForInstances(requestInfo);
82+
if (response.getStatusCode().is2xxSuccessful()) {
83+
return extractSingleInstanceFromApplication(serviceId, requestInfo.getLeft(), response);
84+
}
85+
} catch (Exception e) {
86+
log.debug("Error getting instance info from {}, error message: {}", requestInfo.getLeft(), e.getMessage());
87+
}
8388
}
84-
} catch (Exception e) {
85-
String msg = "An error occurred when trying to get instance info for: " + serviceId;
86-
log.debug(msg, e.getMessage());
87-
throw new InstanceInitializationException(msg);
88-
}
89-
90-
return instanceInfo;
89+
String msg = "An error occurred when trying to get instance info for: " + serviceId;
90+
throw new InstanceInitializationException(msg);
9191
}
9292

9393
/**
@@ -98,12 +98,17 @@ public InstanceInfo getInstanceInfo(@NotBlank(message = "Service Id must be supp
9898
*/
9999
public Applications getAllInstancesFromDiscovery(boolean delta) {
100100

101-
Pair<String, Pair<String, String>> requestInfo = constructServiceInfoQueryRequest(null, delta);
102-
101+
List<Pair<String, Pair<String, String>>> requestInfoList = constructServiceInfoQueryRequest(null, delta);
102+
for (Pair<String, Pair<String, String>> requestInfo : requestInfoList) {
103+
try {
104+
ResponseEntity<String> response = queryDiscoveryForInstances(requestInfo);
105+
return extractApplications(requestInfo, response);
106+
} catch (Exception e) {
107+
log.debug("Not able to contact discovery service: " + requestInfo.getKey(), e);
108+
}
109+
}
103110
// call Eureka REST endpoint to fetch single or all Instances
104-
ResponseEntity<String> response = queryDiscoveryForInstances(requestInfo);
105-
106-
return extractApplications(requestInfo, response);
111+
return null;
107112
}
108113

109114
/**
@@ -188,28 +193,33 @@ private InstanceInfo extractSingleInstanceFromApplication(String serviceId, Stri
188193
* @param serviceId optional service id
189194
* @return request information
190195
*/
191-
private Pair<String, Pair<String, String>> constructServiceInfoQueryRequest(String serviceId, boolean getDelta) {
192-
String discoveryServiceLocatorUrl = discoveryConfigProperties.getLocations() + APPS_ENDPOINT;
193-
if (getDelta) {
194-
discoveryServiceLocatorUrl += DELTA_ENDPOINT;
195-
} else {
196-
if (serviceId != null) {
197-
discoveryServiceLocatorUrl += serviceId.toLowerCase();
196+
private List<Pair<String, Pair<String, String>>> constructServiceInfoQueryRequest(String serviceId, boolean getDelta) {
197+
String[] discoveryServiceUrls = discoveryConfigProperties.getLocations().split(",");
198+
List<Pair<String, Pair<String, String>>> discoveryPairs = new ArrayList<>(discoveryServiceUrls.length);
199+
for (String discoveryUrl : discoveryServiceUrls) {
200+
String discoveryServiceLocatorUrl = discoveryUrl + APPS_ENDPOINT;
201+
if (getDelta) {
202+
discoveryServiceLocatorUrl += DELTA_ENDPOINT;
203+
} else {
204+
if (serviceId != null) {
205+
discoveryServiceLocatorUrl += serviceId.toLowerCase();
206+
}
198207
}
199-
}
200208

201-
String eurekaUsername = discoveryConfigProperties.getEurekaUserName();
202-
String eurekaUserPassword = discoveryConfigProperties.getEurekaUserPassword();
209+
String eurekaUsername = discoveryConfigProperties.getEurekaUserName();
210+
String eurekaUserPassword = discoveryConfigProperties.getEurekaUserPassword();
203211

204-
Pair<String, String> discoveryServiceCredentials = Pair.of(eurekaUsername, eurekaUserPassword);
212+
Pair<String, String> discoveryServiceCredentials = Pair.of(eurekaUsername, eurekaUserPassword);
205213

206-
log.debug("Eureka credentials retrieved for user: {} {}",
207-
eurekaUsername,
208-
(!eurekaUserPassword.isEmpty() ? "*******" : "NO PASSWORD")
209-
);
214+
log.debug("Eureka credentials retrieved for user: {} {}",
215+
eurekaUsername,
216+
(!eurekaUserPassword.isEmpty() ? "*******" : "NO PASSWORD")
217+
);
210218

211-
log.debug("Checking instance info from: " + discoveryServiceLocatorUrl);
212-
return Pair.of(discoveryServiceLocatorUrl, discoveryServiceCredentials);
219+
log.debug("Checking instance info from: " + discoveryServiceLocatorUrl);
220+
discoveryPairs.add(Pair.of(discoveryServiceLocatorUrl, discoveryServiceCredentials));
221+
}
222+
return discoveryPairs;
213223
}
214224

215225
/**

api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/instance/InstanceRetrievalServiceTest.java

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,10 @@
3737

3838
import java.util.*;
3939

40-
import static org.hamcrest.Matchers.*;
4140
import static org.hamcrest.MatcherAssert.assertThat;
41+
import static org.hamcrest.Matchers.*;
4242
import static org.junit.jupiter.api.Assertions.*;
43-
import static org.mockito.Mockito.when;
43+
import static org.mockito.Mockito.*;
4444

4545
@ExtendWith(SpringExtension.class)
4646
@TestPropertySource(locations = "/application.yml")
@@ -61,11 +61,40 @@ class InstanceRetrievalServiceTest {
6161
RestTemplate restTemplate;
6262

6363
private String discoveryServiceAllAppsUrl;
64+
private String[] discoveryServiceList;
6465

6566
@BeforeEach
6667
void setup() {
68+
6769
instanceRetrievalService = new InstanceRetrievalService(discoveryConfigProperties, restTemplate);
68-
discoveryServiceAllAppsUrl = discoveryConfigProperties.getLocations() + APPS_ENDPOINT;
70+
discoveryServiceList = discoveryConfigProperties.getLocations().split(",");
71+
discoveryServiceAllAppsUrl = discoveryServiceList[0] + APPS_ENDPOINT;
72+
}
73+
74+
@Test
75+
void whenDiscoveryServiceIsNotAvailable_thenTryOthersFromTheList() {
76+
when(
77+
restTemplate.exchange(
78+
discoveryServiceList[0] + APPS_ENDPOINT,
79+
HttpMethod.GET,
80+
getHttpEntity(),
81+
String.class
82+
)).thenThrow(RuntimeException.class);
83+
when(
84+
restTemplate.exchange(
85+
discoveryServiceList[1] + APPS_ENDPOINT,
86+
HttpMethod.GET,
87+
getHttpEntity(),
88+
String.class
89+
)).thenReturn(new ResponseEntity<>(null, HttpStatus.OK));
90+
instanceRetrievalService.getAllInstancesFromDiscovery(false);
91+
verify(restTemplate
92+
, times(1)).exchange(
93+
discoveryServiceList[1] + APPS_ENDPOINT,
94+
HttpMethod.GET,
95+
getHttpEntity(),
96+
String.class
97+
);
6998
}
7099

71100
@Test
@@ -75,17 +104,17 @@ void testGetInstanceInfo_whenServiceIdIsUNKNOWN() {
75104
}
76105

77106
@Test
78-
void testGetInstanceInfo_whenResponseCodeIsNotSuccess() {
107+
void providedNoInstanceInfoIsReturned_thenInstanceInitializationExceptionIsThrown() {
108+
String serviceId = CoreService.API_CATALOG.getServiceId();
79109
when(
80110
restTemplate.exchange(
81-
discoveryServiceAllAppsUrl + CoreService.API_CATALOG.getServiceId(),
111+
discoveryServiceAllAppsUrl + serviceId,
82112
HttpMethod.GET,
83113
getHttpEntity(),
84114
String.class
85115
)).thenReturn(new ResponseEntity<>(null, HttpStatus.FORBIDDEN));
116+
assertThrows(InstanceInitializationException.class, () -> instanceRetrievalService.getInstanceInfo(serviceId));
86117

87-
InstanceInfo instanceInfo = instanceRetrievalService.getInstanceInfo(CoreService.API_CATALOG.getServiceId());
88-
assertNull(instanceInfo);
89118
}
90119

91120
@Test
@@ -213,7 +242,7 @@ void testGetAllInstancesFromDiscovery_whenNeedApplicationsWithoutFilter() throws
213242

214243
@Test
215244
void testGetAllInstancesFromDiscovery_whenNeedApplicationsWithDeltaFilter() throws JsonProcessingException {
216-
String discoveryServiceAppsUrl = discoveryConfigProperties.getLocations() + APPS_ENDPOINT + DELTA_ENDPOINT;
245+
String discoveryServiceAppsUrl = this.discoveryServiceAllAppsUrl + DELTA_ENDPOINT;
217246

218247
Map<String, InstanceInfo> instanceInfoMap = createInstances();
219248

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
apiml:
2+
service:
3+
discoveryServiceUrls: https://localhost2:10021/eureka/,https://localhost:10011/eureka/

gateway-service/src/test/java/org/zowe/apiml/gateway/filters/pre/TomcatFilterTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ void shouldThrowServletExceptionOnIOExceptionWhenWritingResponse() throws IOExce
120120

121121
when(mockedRequest.getRequestURI()).thenReturn(ENCODED_REQUEST_URI);
122122
when(mockedResponse.getWriter()).thenThrow(new IOException());
123-
123+
124124
assertThrows(ServletException.class, () -> filter.doFilter(mockedRequest, mockedResponse, filterChain));
125125
}
126126
}

0 commit comments

Comments
 (0)