Skip to content

Commit

Permalink
Merge branch '2.6.x' into 2.7.x
Browse files Browse the repository at this point in the history
Closes gh-31926
  • Loading branch information
mbhave committed Jul 29, 2022
2 parents 4806881 + cc15eb0 commit 2884ec8
Show file tree
Hide file tree
Showing 6 changed files with 217 additions and 34 deletions.
Expand Up @@ -43,6 +43,10 @@ class AvailabilityProbesHealthEndpointGroups implements HealthEndpointGroups {

private final Set<String> names;

private static final String LIVENESS = "liveness";

private static final String READINESS = "readiness";

AvailabilityProbesHealthEndpointGroups(HealthEndpointGroups groups, boolean addAdditionalPaths) {
Assert.notNull(groups, "Groups must not be null");
this.groups = groups;
Expand All @@ -54,18 +58,32 @@ class AvailabilityProbesHealthEndpointGroups implements HealthEndpointGroups {

private Map<String, HealthEndpointGroup> createProbeGroups(boolean addAdditionalPaths) {
Map<String, HealthEndpointGroup> probeGroups = new LinkedHashMap<>();
probeGroups.put("liveness", createProbeGroup(addAdditionalPaths, "/livez", "livenessState"));
probeGroups.put("readiness", createProbeGroup(addAdditionalPaths, "/readyz", "readinessState"));
probeGroups.put(LIVENESS, getOrCreateProbeGroup(addAdditionalPaths, LIVENESS, "/livez", "livenessState"));
probeGroups.put(READINESS, getOrCreateProbeGroup(addAdditionalPaths, READINESS, "/readyz", "readinessState"));
return Collections.unmodifiableMap(probeGroups);
}

private AvailabilityProbesHealthEndpointGroup createProbeGroup(boolean addAdditionalPath, String path,
private HealthEndpointGroup getOrCreateProbeGroup(boolean addAdditionalPath, String name, String path,
String members) {
HealthEndpointGroup group = this.groups.get(name);
if (group != null) {
return determineAdditionalPathForExistingGroup(addAdditionalPath, path, group);
}
AdditionalHealthEndpointPath additionalPath = (!addAdditionalPath) ? null
: AdditionalHealthEndpointPath.of(WebServerNamespace.SERVER, path);
return new AvailabilityProbesHealthEndpointGroup(additionalPath, members);
}

private HealthEndpointGroup determineAdditionalPathForExistingGroup(boolean addAdditionalPath, String path,
HealthEndpointGroup group) {
if (addAdditionalPath && group.getAdditionalPath() == null) {
AdditionalHealthEndpointPath additionalPath = AdditionalHealthEndpointPath.of(WebServerNamespace.SERVER,
path);
return new DelegatingAvailabilityProbesHealthEndpointGroup(group, additionalPath);
}
return group;
}

@Override
public HealthEndpointGroup getPrimary() {
return this.groups.getPrimary();
Expand All @@ -79,15 +97,14 @@ public Set<String> getNames() {
@Override
public HealthEndpointGroup get(String name) {
HealthEndpointGroup group = this.groups.get(name);
if (group == null) {
if (group == null || isProbeGroup(name)) {
group = this.probeGroups.get(name);
}
return group;
}

static boolean containsAllProbeGroups(HealthEndpointGroups groups) {
Set<String> names = groups.getNames();
return names.contains("liveness") && names.contains("readiness");
private boolean isProbeGroup(String name) {
return name.equals(LIVENESS) || name.equals(READINESS);
}

}
Expand Up @@ -41,9 +41,6 @@ class AvailabilityProbesHealthEndpointGroupsPostProcessor implements HealthEndpo

@Override
public HealthEndpointGroups postProcessHealthEndpointGroups(HealthEndpointGroups groups) {
if (AvailabilityProbesHealthEndpointGroups.containsAllProbeGroups(groups)) {
return groups;
}
return new AvailabilityProbesHealthEndpointGroups(groups, this.addAdditionalPaths);
}

Expand Down
@@ -0,0 +1,75 @@
/*
* Copyright 2012-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License 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.
*/

package org.springframework.boot.actuate.autoconfigure.availability;

import org.springframework.boot.actuate.endpoint.SecurityContext;
import org.springframework.boot.actuate.health.AdditionalHealthEndpointPath;
import org.springframework.boot.actuate.health.HealthEndpointGroup;
import org.springframework.boot.actuate.health.HttpCodeStatusMapper;
import org.springframework.boot.actuate.health.StatusAggregator;
import org.springframework.util.Assert;

/**
* {@link HealthEndpointGroup} used to support availability probes that delegates to an
* existing group.
*
* @author Madhura Bhave
*/
class DelegatingAvailabilityProbesHealthEndpointGroup implements HealthEndpointGroup {

private final HealthEndpointGroup delegate;

private final AdditionalHealthEndpointPath additionalPath;

DelegatingAvailabilityProbesHealthEndpointGroup(HealthEndpointGroup delegate,
AdditionalHealthEndpointPath additionalPath) {
Assert.notNull(delegate, "Delegate must not be null");
this.delegate = delegate;
this.additionalPath = additionalPath;
}

@Override
public boolean isMember(String name) {
return this.delegate.isMember(name);
}

@Override
public boolean showComponents(SecurityContext securityContext) {
return this.delegate.showComponents(securityContext);
}

@Override
public boolean showDetails(SecurityContext securityContext) {
return this.delegate.showDetails(securityContext);
}

@Override
public StatusAggregator getStatusAggregator() {
return this.delegate.getStatusAggregator();
}

@Override
public HttpCodeStatusMapper getHttpCodeStatusMapper() {
return this.delegate.getHttpCodeStatusMapper();
}

@Override
public AdditionalHealthEndpointPath getAdditionalPath() {
return this.additionalPath;
}

}
Expand Up @@ -48,7 +48,8 @@ void postProcessHealthEndpointGroupsWhenGroupsAlreadyContainedReturnsOriginal()
names.add("readiness");
names.add("liveness");
given(groups.getNames()).willReturn(names);
assertThat(this.postProcessor.postProcessHealthEndpointGroups(groups)).isSameAs(groups);
assertThat(this.postProcessor.postProcessHealthEndpointGroups(groups))
.isInstanceOf(AvailabilityProbesHealthEndpointGroups.class);
}

@Test
Expand Down Expand Up @@ -83,6 +84,25 @@ void postProcessHealthEndpointGroupsWhenAdditionalPathPropertyIsTrue() {
assertThat(readiness.getAdditionalPath().toString()).isEqualTo("server:/readyz");
}

@Test
void postProcessHealthEndpointGroupsWhenGroupsAlreadyContainedAndAdditionalPathPropertyIsTrue() {
HealthEndpointGroups groups = mock(HealthEndpointGroups.class);
Set<String> names = new LinkedHashSet<>();
names.add("test");
names.add("readiness");
names.add("liveness");
given(groups.getNames()).willReturn(names);
MockEnvironment environment = new MockEnvironment();
environment.setProperty("management.endpoint.health.probes.add-additional-paths", "true");
AvailabilityProbesHealthEndpointGroupsPostProcessor postProcessor = new AvailabilityProbesHealthEndpointGroupsPostProcessor(
environment);
HealthEndpointGroups postProcessed = postProcessor.postProcessHealthEndpointGroups(groups);
HealthEndpointGroup liveness = postProcessed.get("liveness");
HealthEndpointGroup readiness = postProcessed.get("readiness");
assertThat(liveness.getAdditionalPath().toString()).isEqualTo("server:/livez");
assertThat(readiness.getAdditionalPath().toString()).isEqualTo("server:/readyz");
}

private HealthEndpointGroups getPostProcessed(String value) {
MockEnvironment environment = new MockEnvironment();
environment.setProperty("management.endpoint.health.probes.add-additional-paths", value);
Expand Down
Expand Up @@ -16,15 +16,15 @@

package org.springframework.boot.actuate.autoconfigure.availability;

import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import org.springframework.boot.actuate.health.AdditionalHealthEndpointPath;
import org.springframework.boot.actuate.health.HealthEndpointGroup;
import org.springframework.boot.actuate.health.HealthEndpointGroups;
import org.springframework.boot.actuate.health.HttpCodeStatusMapper;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
Expand All @@ -35,6 +35,7 @@
* Tests for {@link AvailabilityProbesHealthEndpointGroups}.
*
* @author Phillip Webb
* @author Madhura Bhave
*/
class AvailabilityProbesHealthEndpointGroupsTests {

Expand Down Expand Up @@ -69,10 +70,32 @@ void getNamesIncludesAvailabilityProbeGroups() {
}

@Test
void getWhenProbeInDelegateReturnsGroupFromDelegate() {
given(this.delegate.get("liveness")).willReturn(this.group);
void getWhenProbeInDelegateReturnsOriginalGroup() {
HealthEndpointGroup group = mock(HealthEndpointGroup.class);
HttpCodeStatusMapper mapper = mock(HttpCodeStatusMapper.class);
given(group.getHttpCodeStatusMapper()).willReturn(mapper);
given(this.delegate.get("liveness")).willReturn(group);
HealthEndpointGroups availabilityProbes = new AvailabilityProbesHealthEndpointGroups(this.delegate, false);
assertThat(availabilityProbes.get("liveness")).isEqualTo(this.group);
assertThat(availabilityProbes.get("liveness")).isEqualTo(group);
assertThat(group.getHttpCodeStatusMapper()).isEqualTo(mapper);
}

@Test
void getWhenProbeInDelegateAndExistingAdditionalPathReturnsOriginalGroup() {
HealthEndpointGroup group = mock(HealthEndpointGroup.class);
given(group.getAdditionalPath()).willReturn(AdditionalHealthEndpointPath.from("server:test"));
given(this.delegate.get("liveness")).willReturn(group);
HealthEndpointGroups availabilityProbes = new AvailabilityProbesHealthEndpointGroups(this.delegate, true);
HealthEndpointGroup liveness = availabilityProbes.get("liveness");
assertThat(liveness).isEqualTo(group);
assertThat(liveness.getAdditionalPath().getValue()).isEqualTo("test");
}

@Test
void getWhenProbeInDelegateAndAdditionalPathReturnsGroupWithAdditionalPath() {
given(this.delegate.get("liveness")).willReturn(this.group);
HealthEndpointGroups availabilityProbes = new AvailabilityProbesHealthEndpointGroups(this.delegate, true);
assertThat(availabilityProbes.get("liveness").getAdditionalPath().getValue()).isEqualTo("/livez");
}

@Test
Expand Down Expand Up @@ -103,22 +126,4 @@ void getReadinessProbeHasOnlyReadinessStateAsMember() {
assertThat(probeGroup.isMember("readinessState")).isTrue();
}

@Test
void containsAllWhenContainsAllReturnTrue() {
given(this.delegate.getNames()).willReturn(new LinkedHashSet<>(Arrays.asList("test", "liveness", "readiness")));
assertThat(AvailabilityProbesHealthEndpointGroups.containsAllProbeGroups(this.delegate)).isTrue();
}

@Test
void containsAllWhenContainsOneReturnFalse() {
given(this.delegate.getNames()).willReturn(new LinkedHashSet<>(Arrays.asList("test", "liveness")));
assertThat(AvailabilityProbesHealthEndpointGroups.containsAllProbeGroups(this.delegate)).isFalse();
}

@Test
void containsAllWhenContainsNoneReturnFalse() {
given(this.delegate.getNames()).willReturn(new LinkedHashSet<>(Arrays.asList("test", "spring")));
assertThat(AvailabilityProbesHealthEndpointGroups.containsAllProbeGroups(this.delegate)).isFalse();
}

}
@@ -0,0 +1,69 @@
/*
* Copyright 2012-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License 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.
*/

package org.springframework.boot.actuate.autoconfigure.availability;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import org.springframework.boot.actuate.health.AdditionalHealthEndpointPath;
import org.springframework.boot.actuate.health.HealthEndpointGroup;
import org.springframework.boot.actuate.health.HttpCodeStatusMapper;
import org.springframework.boot.actuate.health.StatusAggregator;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;

/**
* Tests for {@link DelegatingAvailabilityProbesHealthEndpointGroup}.
*
* @author Madhura Bhave
*/
class DelegatingAvailabilityProbesHealthEndpointGroupTests {

private DelegatingAvailabilityProbesHealthEndpointGroup group;

private HttpCodeStatusMapper mapper;

private StatusAggregator aggregator;

@BeforeEach
void setup() {
HealthEndpointGroup delegate = mock(HealthEndpointGroup.class);
this.mapper = mock(HttpCodeStatusMapper.class);
this.aggregator = mock(StatusAggregator.class);
given(delegate.getHttpCodeStatusMapper()).willReturn(this.mapper);
given(delegate.getStatusAggregator()).willReturn(this.aggregator);
given(delegate.showComponents(any())).willReturn(true);
given(delegate.showDetails(any())).willReturn(false);
given(delegate.isMember("test")).willReturn(true);
this.group = new DelegatingAvailabilityProbesHealthEndpointGroup(delegate,
AdditionalHealthEndpointPath.from("server:test"));
}

@Test
void groupDelegatesToDelegate() {
assertThat(this.group.getHttpCodeStatusMapper()).isEqualTo(this.mapper);
assertThat(this.group.getStatusAggregator()).isEqualTo(this.aggregator);
assertThat(this.group.isMember("test")).isTrue();
assertThat(this.group.showDetails(null)).isFalse();
assertThat(this.group.showComponents(null)).isTrue();
assertThat(this.group.getAdditionalPath().getValue()).isEqualTo("test");
}

}

0 comments on commit 2884ec8

Please sign in to comment.