forked from eclipse-tractusx/item-relationship-service
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request eclipse-tractusx#624 from catenax-ng/feature/TRI-2…
…05-external-dependencies-healthchecks feat(impl):[TRI-205] external dependencies healtchecks impl
- Loading branch information
Showing
7 changed files
with
282 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
41 changes: 41 additions & 0 deletions
41
...src/main/java/org/eclipse/tractusx/irs/configuration/DependenciesHealthConfiguration.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
/******************************************************************************** | ||
* Copyright (c) 2021,2022,2023 | ||
* 2022: ZF Friedrichshafen AG | ||
* 2022: ISTOS GmbH | ||
* 2022,2023: Bayerische Motoren Werke Aktiengesellschaft (BMW AG) | ||
* 2022,2023: BOSCH AG | ||
* Copyright (c) 2021,2022,2023 Contributors to the Eclipse Foundation | ||
* | ||
* See the NOTICE file(s) distributed with this work for additional | ||
* information regarding copyright ownership. | ||
* | ||
* This program and the accompanying materials are made available under the | ||
* terms of the Apache License, Version 2.0 which is available at | ||
* https://www.apache.org/licenses/LICENSE-2.0. | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
* License for the specific language governing permissions and limitations | ||
* under the License. | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
********************************************************************************/ | ||
package org.eclipse.tractusx.irs.configuration; | ||
|
||
import java.util.Map; | ||
|
||
import lombok.Data; | ||
import org.springframework.boot.context.properties.ConfigurationProperties; | ||
import org.springframework.stereotype.Component; | ||
|
||
/** | ||
* Config values for external dependencies health URLs | ||
*/ | ||
@Component | ||
@ConfigurationProperties(prefix = "management.health.dependencies") | ||
@Data | ||
public class DependenciesHealthConfiguration { | ||
|
||
private Map<String, String> urls; | ||
} |
111 changes: 111 additions & 0 deletions
111
...api/src/main/java/org/eclipse/tractusx/irs/configuration/DependenciesHealthIndicator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
/******************************************************************************** | ||
* Copyright (c) 2021,2022,2023 | ||
* 2022: ZF Friedrichshafen AG | ||
* 2022: ISTOS GmbH | ||
* 2022,2023: Bayerische Motoren Werke Aktiengesellschaft (BMW AG) | ||
* 2022,2023: BOSCH AG | ||
* Copyright (c) 2021,2022,2023 Contributors to the Eclipse Foundation | ||
* | ||
* See the NOTICE file(s) distributed with this work for additional | ||
* information regarding copyright ownership. | ||
* | ||
* This program and the accompanying materials are made available under the | ||
* terms of the Apache License, Version 2.0 which is available at | ||
* https://www.apache.org/licenses/LICENSE-2.0. | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
* License for the specific language governing permissions and limitations | ||
* under the License. | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
********************************************************************************/ | ||
package org.eclipse.tractusx.irs.configuration; | ||
|
||
import static org.eclipse.tractusx.irs.configuration.RestTemplateConfig.NO_ERROR_REST_TEMPLATE; | ||
|
||
import java.util.Collection; | ||
import java.util.Map; | ||
import java.util.stream.Collectors; | ||
|
||
import lombok.RequiredArgsConstructor; | ||
import lombok.Value; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.beans.factory.annotation.Qualifier; | ||
import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator; | ||
import org.springframework.boot.actuate.health.Health; | ||
import org.springframework.boot.actuate.health.HealthIndicator; | ||
import org.springframework.boot.actuate.health.Status; | ||
import org.springframework.http.HttpStatusCode; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.stereotype.Component; | ||
import org.springframework.web.client.ResourceAccessException; | ||
import org.springframework.web.client.RestTemplate; | ||
|
||
/** | ||
* External Dependencies health indicator for Spring actuator | ||
*/ | ||
@Component | ||
@Slf4j | ||
@ConditionalOnEnabledHealthIndicator("dependencies") | ||
class DependenciesHealthIndicator implements HealthIndicator { | ||
|
||
private final DependenciesHealthConfiguration dependenciesHealthConfiguration; | ||
private final RestTemplate restTemplate; | ||
|
||
/* package */ DependenciesHealthIndicator(@Qualifier(NO_ERROR_REST_TEMPLATE) final RestTemplate noErrorRestTemplate, | ||
final DependenciesHealthConfiguration dependenciesHealthConfiguration) { | ||
this.dependenciesHealthConfiguration = dependenciesHealthConfiguration; | ||
this.restTemplate = noErrorRestTemplate; | ||
} | ||
|
||
@Override | ||
public Health health() { | ||
final Map<String, Status> details = details(); | ||
return Health.status(globalStatus(details.values())) | ||
.withDetails(details) | ||
.build(); | ||
} | ||
|
||
private Status globalStatus(final Collection<Status> statuses) { | ||
final boolean allDependenciesAreUp = statuses.stream().allMatch(status -> status.equals(Status.UP)); | ||
return allDependenciesAreUp ? Status.UP : Status.DOWN; | ||
} | ||
|
||
private Map<String, Status> details() { | ||
return dependenciesHealthConfiguration.getUrls() | ||
.entrySet() | ||
.stream() | ||
.map(dependency -> { | ||
final String dependencyName = dependency.getKey(); | ||
try { | ||
final String dependencyHealthUrl = dependency.getValue(); | ||
final ResponseEntity<Void> health = restTemplate.getForEntity( | ||
dependencyHealthUrl, Void.class); | ||
log.info("Health endpoint URL for {} dependency pinged with status {}.", | ||
dependencyName, health.getStatusCode()); | ||
return new ExternalServiceHealthStatus(dependencyName, health.getStatusCode()); | ||
} catch (final ResourceAccessException resourceAccessException) { | ||
log.warn("Health endpoint URL for {} dependency is not reachable.", dependencyName); | ||
return new ExternalServiceHealthStatus(dependencyName, Status.UNKNOWN); | ||
} | ||
}).collect(Collectors.toMap(ExternalServiceHealthStatus::getName, ExternalServiceHealthStatus::getStatus)); | ||
} | ||
|
||
/** | ||
* External Service Status DTO | ||
*/ | ||
@Value | ||
@RequiredArgsConstructor | ||
private static final class ExternalServiceHealthStatus { | ||
private final String name; | ||
private final Status status; | ||
|
||
private ExternalServiceHealthStatus(final String name, final HttpStatusCode httpStatusCode) { | ||
this.name = name; | ||
this.status = httpStatusCode.is2xxSuccessful() ? Status.UP : Status.DOWN; | ||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
109 changes: 109 additions & 0 deletions
109
...src/test/java/org/eclipse/tractusx/irs/configuration/DependenciesHealthIndicatorTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
/******************************************************************************** | ||
* Copyright (c) 2021,2022,2023 | ||
* 2022: ZF Friedrichshafen AG | ||
* 2022: ISTOS GmbH | ||
* 2022,2023: Bayerische Motoren Werke Aktiengesellschaft (BMW AG) | ||
* 2022,2023: BOSCH AG | ||
* Copyright (c) 2021,2022,2023 Contributors to the Eclipse Foundation | ||
* | ||
* See the NOTICE file(s) distributed with this work for additional | ||
* information regarding copyright ownership. | ||
* | ||
* This program and the accompanying materials are made available under the | ||
* terms of the Apache License, Version 2.0 which is available at | ||
* https://www.apache.org/licenses/LICENSE-2.0. | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
* License for the specific language governing permissions and limitations | ||
* under the License. | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
********************************************************************************/ | ||
package org.eclipse.tractusx.irs.configuration; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.mockito.ArgumentMatchers.anyString; | ||
import static org.mockito.ArgumentMatchers.eq; | ||
import static org.mockito.Mockito.mock; | ||
import static org.mockito.Mockito.times; | ||
import static org.mockito.Mockito.verify; | ||
import static org.mockito.Mockito.when; | ||
|
||
import java.util.Map; | ||
|
||
import org.jetbrains.annotations.NotNull; | ||
import org.junit.jupiter.api.Test; | ||
import org.springframework.boot.actuate.health.Health; | ||
import org.springframework.boot.actuate.health.Status; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.client.ResourceAccessException; | ||
import org.springframework.web.client.RestTemplate; | ||
|
||
class DependenciesHealthIndicatorTest { | ||
|
||
private final RestTemplate restTemplate = mock(RestTemplate.class); | ||
private final DependenciesHealthConfiguration dependenciesHealthConfiguration = mock(DependenciesHealthConfiguration.class); | ||
|
||
@Test | ||
void shouldReturnStatusUpWhenAllExternalServicesAreUp() { | ||
// given | ||
final DependenciesHealthIndicator dependenciesHealthIndicator = new DependenciesHealthIndicator(restTemplate, dependenciesHealthConfiguration); | ||
final Map<String, String> externalServicesHealthUrls = externalServicesHealthUrls(); | ||
when(dependenciesHealthConfiguration.getUrls()).thenReturn(externalServicesHealthUrls); | ||
when(restTemplate.getForEntity(anyString(), eq(Void.class))).thenReturn(ResponseEntity.ok().build()); | ||
|
||
// when | ||
final Health health = dependenciesHealthIndicator.health(); | ||
|
||
// then | ||
assertThat(health.getStatus()).isEqualTo(Status.UP); | ||
assertThat(health.getDetails()).hasSameSizeAs(externalServicesHealthUrls); | ||
verify(restTemplate, times(externalServicesHealthUrls.size())).getForEntity(anyString(), eq(Void.class)); | ||
} | ||
|
||
@Test | ||
void shouldReturnStatusDownWhenAnyExternalServiceIsNotReachable() { | ||
// given | ||
final DependenciesHealthIndicator dependenciesHealthIndicator = new DependenciesHealthIndicator(restTemplate, dependenciesHealthConfiguration); | ||
final Map<String, String> externalServicesHealthUrls = externalServicesHealthUrls(); | ||
when(dependenciesHealthConfiguration.getUrls()).thenReturn(externalServicesHealthUrls); | ||
when(restTemplate.getForEntity(anyString(), eq(Void.class))).thenThrow(new ResourceAccessException("Not reachable")) | ||
.thenReturn(ResponseEntity.ok().build()); | ||
|
||
// when | ||
final Health health = dependenciesHealthIndicator.health(); | ||
|
||
// then | ||
assertThat(health.getStatus()).isEqualTo(Status.DOWN); | ||
assertThat(health.getDetails()).hasSameSizeAs(externalServicesHealthUrls); | ||
verify(restTemplate, times(externalServicesHealthUrls.size())).getForEntity(anyString(), eq(Void.class)); | ||
} | ||
|
||
@Test | ||
void shouldReturnStatusDownWhenAnyExternalServiceIsDown() { | ||
// given | ||
final DependenciesHealthIndicator dependenciesHealthIndicator = new DependenciesHealthIndicator(restTemplate, dependenciesHealthConfiguration); | ||
final Map<String, String> externalServicesHealthUrls = externalServicesHealthUrls(); | ||
when(dependenciesHealthConfiguration.getUrls()).thenReturn(externalServicesHealthUrls); | ||
when(restTemplate.getForEntity(anyString(), eq(Void.class))).thenReturn(ResponseEntity.notFound().build()) | ||
.thenReturn(ResponseEntity.ok().build()); | ||
|
||
// when | ||
final Health health = dependenciesHealthIndicator.health(); | ||
|
||
// then | ||
assertThat(health.getStatus()).isEqualTo(Status.DOWN); | ||
assertThat(health.getDetails()).hasSameSizeAs(externalServicesHealthUrls); | ||
verify(restTemplate, times(externalServicesHealthUrls.size())).getForEntity(anyString(), eq(Void.class)); | ||
} | ||
|
||
@NotNull | ||
private static Map<String, String> externalServicesHealthUrls() { | ||
return Map.of( | ||
"service_one", "http://service_one/health", | ||
"service_two", "http://service_two/health", | ||
"service_three", "http://service_three/health"); | ||
} | ||
} |