AbstractKubernetesProfileEnvironmentPostProcessor changes + tests (#738)
* initial commit * slightly make test more readable * added test scope
This commit is contained in:
@@ -50,6 +50,12 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -57,6 +63,17 @@
|
||||
<artifactId>junit-vintage-engine</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-inline</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ public abstract class AbstractKubernetesHealthIndicator extends AbstractHealthIn
|
||||
public static final String LABELS = "labels";
|
||||
|
||||
@Override
|
||||
protected void doHealthCheck(Health.Builder builder) throws Exception {
|
||||
protected void doHealthCheck(Health.Builder builder) {
|
||||
try {
|
||||
builder.up().withDetails(getDetails());
|
||||
}
|
||||
|
||||
@@ -16,19 +16,17 @@
|
||||
|
||||
package org.springframework.cloud.kubernetes.commons.profile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.LinkOption;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor;
|
||||
import org.springframework.boot.env.EnvironmentPostProcessor;
|
||||
import org.springframework.boot.logging.DeferredLog;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.Environment;
|
||||
@@ -41,7 +39,7 @@ import static org.springframework.cloud.kubernetes.commons.KubernetesClientPrope
|
||||
*/
|
||||
public abstract class AbstractKubernetesProfileEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered {
|
||||
|
||||
private static final Log LOG = LogFactory.getLog(AbstractKubernetesProfileEnvironmentPostProcessor.class);
|
||||
private static final DeferredLog LOG = new DeferredLog();
|
||||
|
||||
private static final String NAMESPACE_PATH_PROPERTY = "spring.cloud.kubernetes.client.serviceAccountNamespacePath";
|
||||
|
||||
@@ -60,41 +58,20 @@ public abstract class AbstractKubernetesProfileEnvironmentPostProcessor implemen
|
||||
@Override
|
||||
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
|
||||
|
||||
final boolean kubernetesEnabled = environment.getProperty("spring.cloud.kubernetes.enabled", Boolean.class,
|
||||
true);
|
||||
application.addInitializers(ctx -> LOG.replayTo(AbstractKubernetesProfileEnvironmentPostProcessor.class));
|
||||
|
||||
boolean kubernetesEnabled = environment.getProperty("spring.cloud.kubernetes.enabled", Boolean.class, true);
|
||||
if (!kubernetesEnabled) {
|
||||
return;
|
||||
}
|
||||
addNamespaceFromServiceAccountFile(environment);
|
||||
if (isInsideKubernetes(environment)) {
|
||||
if (hasKubernetesProfile(environment)) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("'kubernetes' already in list of active profiles");
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Adding 'kubernetes' to list of active profiles");
|
||||
}
|
||||
environment.addActiveProfile(KUBERNETES_PROFILE);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.warn("Not running inside kubernetes. Skipping 'kubernetes' profile activation.");
|
||||
}
|
||||
}
|
||||
addKubernetesProfileIfMissing(environment);
|
||||
}
|
||||
|
||||
protected abstract boolean isInsideKubernetes(Environment environment);
|
||||
|
||||
private boolean hasKubernetesProfile(Environment environment) {
|
||||
for (String activeProfile : environment.getActiveProfiles()) {
|
||||
if (KUBERNETES_PROFILE.equalsIgnoreCase(activeProfile)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return Arrays.stream(environment.getActiveProfiles()).anyMatch(KUBERNETES_PROFILE::equalsIgnoreCase);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -102,30 +79,43 @@ public abstract class AbstractKubernetesProfileEnvironmentPostProcessor implemen
|
||||
return ORDER;
|
||||
}
|
||||
|
||||
private void addNamespaceFromServiceAccountFile(ConfigurableEnvironment environment) {
|
||||
Map<String, Object> properties = new HashMap<>();
|
||||
private void addKubernetesProfileIfMissing(ConfigurableEnvironment environment) {
|
||||
if (isInsideKubernetes(environment)) {
|
||||
if (hasKubernetesProfile(environment)) {
|
||||
LOG.debug("'kubernetes' already in list of active profiles");
|
||||
}
|
||||
else {
|
||||
LOG.debug("Adding 'kubernetes' to list of active profiles");
|
||||
environment.addActiveProfile(KUBERNETES_PROFILE);
|
||||
}
|
||||
}
|
||||
else {
|
||||
LOG.warn("Not running inside kubernetes. Skipping 'kubernetes' profile activation.");
|
||||
}
|
||||
}
|
||||
|
||||
private void addNamespaceFromServiceAccountFile(ConfigurableEnvironment environment) {
|
||||
String serviceAccountNamespace = environment.getProperty(NAMESPACE_PATH_PROPERTY,
|
||||
SERVICE_ACCOUNT_NAMESPACE_PATH);
|
||||
LOG.debug("Looking for service account namespace at " + serviceAccountNamespace);
|
||||
boolean serviceAccountNamespaceExists = Files.isRegularFile((new File(serviceAccountNamespace)).toPath(),
|
||||
new LinkOption[0]);
|
||||
LOG.debug("Looking for service account namespace at: [" + serviceAccountNamespace + "].");
|
||||
Path serviceAccountNamespacePath = Paths.get(serviceAccountNamespace);
|
||||
boolean serviceAccountNamespaceExists = Files.isRegularFile(serviceAccountNamespacePath);
|
||||
if (serviceAccountNamespaceExists) {
|
||||
LOG.debug("Found service account namespace at: [" + serviceAccountNamespace + "].");
|
||||
|
||||
try {
|
||||
String namespace = new String(Files.readAllBytes((new File(serviceAccountNamespace)).toPath()));
|
||||
String namespace = new String(Files.readAllBytes((serviceAccountNamespacePath)));
|
||||
LOG.debug("Service account namespace value: " + namespace);
|
||||
properties.put(NAMESPACE_PROPERTY, namespace);
|
||||
environment.getPropertySources().addLast(new MapPropertySource(PROPERTY_SOURCE_NAME,
|
||||
Collections.singletonMap(NAMESPACE_PROPERTY, namespace)));
|
||||
}
|
||||
catch (IOException var4) {
|
||||
LOG.error("Error reading service account namespace from: [" + serviceAccountNamespace + "].", var4);
|
||||
catch (IOException ioe) {
|
||||
LOG.error("Error reading service account namespace from: [" + serviceAccountNamespace + "].", ioe);
|
||||
}
|
||||
}
|
||||
else {
|
||||
LOG.info("Did not find service account namespace at: [" + serviceAccountNamespace + "]. Ignoring.");
|
||||
}
|
||||
environment.getPropertySources().addLast(new MapPropertySource(PROPERTY_SOURCE_NAME, properties));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
* Copyright 2013-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.cloud.kubernetes.commons.profile;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.MockedStatic;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.test.context.support.TestPropertySourceUtils;
|
||||
|
||||
/**
|
||||
* @author wind57
|
||||
*/
|
||||
public class AbstractKubernetesProfileEnvironmentPostProcessorTest {
|
||||
|
||||
private static final String FOUNT_IT = "foundIt";
|
||||
|
||||
private static final String PATH = "/some/path";
|
||||
|
||||
private MockedStatic<Paths> paths;
|
||||
|
||||
private MockedStatic<Files> files;
|
||||
|
||||
private final SpringApplication springApplication = Mockito.mock(SpringApplication.class);
|
||||
|
||||
private final ConfigurableApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
|
||||
private static final AbstractKubernetesProfileEnvironmentPostProcessor POST_PROCESSOR_INSIDE = new AbstractKubernetesProfileEnvironmentPostProcessor() {
|
||||
@Override
|
||||
protected boolean isInsideKubernetes(Environment environment) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
private static final AbstractKubernetesProfileEnvironmentPostProcessor POST_PROCESSOR_OUTSIDE = new AbstractKubernetesProfileEnvironmentPostProcessor() {
|
||||
@Override
|
||||
protected boolean isInsideKubernetes(Environment environment) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
paths = Mockito.mockStatic(Paths.class);
|
||||
files = Mockito.mockStatic(Files.class);
|
||||
}
|
||||
|
||||
@After
|
||||
public void after() {
|
||||
paths.close();
|
||||
files.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 1) "spring.cloud.kubernetes.enabled" is false; thus nothing happens
|
||||
* </pre>
|
||||
*/
|
||||
@Test
|
||||
public void testKubernetesDisabled() {
|
||||
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(context, "spring.cloud.kubernetes.enabled=false");
|
||||
POST_PROCESSOR_INSIDE.postProcessEnvironment(context.getEnvironment(), springApplication);
|
||||
|
||||
assertKubernetesProfileNotPresent();
|
||||
assertKubernetesPropertySourceNotPresent();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 1) "spring.cloud.kubernetes.enabled" is true
|
||||
* 2) "spring.cloud.kubernetes.client.serviceAccountNamespacePath" is present, but does not resolve to an actual File
|
||||
* </pre>
|
||||
*/
|
||||
@Test
|
||||
public void testKubernetesEnabledAndServiceAccountNamespacePathIsNotResolved() {
|
||||
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(context, "spring.cloud.kubernetes.enabled=true",
|
||||
"spring.cloud.kubernetes.client.serviceAccountNamespacePath=" + PATH);
|
||||
serviceAccountFileResolved(false, PATH);
|
||||
POST_PROCESSOR_INSIDE.postProcessEnvironment(context.getEnvironment(), springApplication);
|
||||
|
||||
assertKubernetesProfilePresent();
|
||||
assertKubernetesPropertySourceNotPresent();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 1) "spring.cloud.kubernetes.enabled" is true
|
||||
* 2) "spring.cloud.kubernetes.client.serviceAccountNamespacePath" is present and resolves to an actual File
|
||||
* </pre>
|
||||
*/
|
||||
@Test
|
||||
public void testKubernetesEnabledAndServiceAccountNamespacePathIsResolved() {
|
||||
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(context, "spring.cloud.kubernetes.enabled=true",
|
||||
"spring.cloud.kubernetes.client.serviceAccountNamespacePath=" + PATH);
|
||||
|
||||
Path path = serviceAccountFileResolved(true, PATH);
|
||||
mockServiceAccountNamespace(path);
|
||||
|
||||
POST_PROCESSOR_INSIDE.postProcessEnvironment(context.getEnvironment(), springApplication);
|
||||
|
||||
assertKubernetesProfilePresent();
|
||||
assertKubernetesPropertySourcePresent();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 1) "spring.cloud.kubernetes.enabled" is true
|
||||
* 2) "spring.cloud.kubernetes.client.serviceAccountNamespacePath" is not present, as such:
|
||||
* 3) "/var/run/secrets/kubernetes.io/serviceaccount/namespace" is picked up, which is resolved and present
|
||||
* </pre>
|
||||
*/
|
||||
@Test
|
||||
public void testKubernetesEnabledAndServiceAccountNamespacePathIsResolvedViaDefaultLocation() {
|
||||
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(context, "spring.cloud.kubernetes.enabled=true");
|
||||
|
||||
Path path = serviceAccountFileResolved(true, "/var/run/secrets/kubernetes.io/serviceaccount/namespace");
|
||||
mockServiceAccountNamespace(path);
|
||||
|
||||
POST_PROCESSOR_INSIDE.postProcessEnvironment(context.getEnvironment(), springApplication);
|
||||
|
||||
assertKubernetesProfilePresent();
|
||||
assertKubernetesPropertySourcePresent();
|
||||
}
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 1) "spring.cloud.kubernetes.enabled" is true
|
||||
* 2) isInsideKubernetes returns false
|
||||
* 3) "spring.cloud.kubernetes.client.serviceAccountNamespacePath" is not present, as such:
|
||||
* 4) "/var/run/secrets/kubernetes.io/serviceaccount/namespace" is picked up, which is resolved and present
|
||||
* </pre>
|
||||
*/
|
||||
@Test
|
||||
public void testOutsideKubernetes() {
|
||||
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(context, "spring.cloud.kubernetes.enabled=true");
|
||||
|
||||
Path path = serviceAccountFileResolved(true, "/var/run/secrets/kubernetes.io/serviceaccount/namespace");
|
||||
mockServiceAccountNamespace(path);
|
||||
|
||||
POST_PROCESSOR_OUTSIDE.postProcessEnvironment(context.getEnvironment(), springApplication);
|
||||
|
||||
assertKubernetesProfileNotPresent();
|
||||
assertKubernetesPropertySourcePresent();
|
||||
}
|
||||
|
||||
/*
|
||||
* 'kubernetes' profile is not present
|
||||
*/
|
||||
private void assertKubernetesProfileNotPresent() {
|
||||
Assert.assertFalse("'kubernetes' profile must not be present when 'spring.cloud.kubernetes.enabled' is false",
|
||||
kubernetesProfile().isPresent());
|
||||
}
|
||||
|
||||
/*
|
||||
* 'kubernetes' profile is present
|
||||
*/
|
||||
private void assertKubernetesProfilePresent() {
|
||||
Assert.assertTrue("'kubernetes' profile must be present when 'spring.cloud.kubernetes.enabled' is true",
|
||||
kubernetesProfile().isPresent());
|
||||
}
|
||||
|
||||
/*
|
||||
* 'KUBERNETES_NAMESPACE_PROPERTY_SOURCE' source is not present
|
||||
*/
|
||||
private void assertKubernetesPropertySourceNotPresent() {
|
||||
Optional<PropertySource<?>> kubernetesPropertySource = kubernetesPropertySource();
|
||||
|
||||
Assert.assertFalse(
|
||||
"'KUBERNETES_NAMESPACE_PROPERTY_SOURCE' source must not be present when 'spring.cloud.kubernetes.enabled' is false",
|
||||
kubernetesPropertySource.isPresent());
|
||||
}
|
||||
|
||||
/*
|
||||
* 'KUBERNETES_NAMESPACE_PROPERTY_SOURCE' source is present
|
||||
*/
|
||||
private void assertKubernetesPropertySourcePresent() {
|
||||
|
||||
Optional<PropertySource<?>> kubernetesPropertySource = kubernetesPropertySource();
|
||||
Assert.assertTrue(
|
||||
"'KUBERNETES_NAMESPACE_PROPERTY_SOURCE' source must be present when 'spring.cloud.kubernetes.enabled' is true",
|
||||
kubernetesPropertySource.isPresent());
|
||||
|
||||
String property = (String) kubernetesPropertySource.get()
|
||||
.getProperty("spring.cloud.kubernetes.client.namespace");
|
||||
Assert.assertEquals("'spring.cloud.kubernetes.client.namespace' must be set to 'foundIt'", property, FOUNT_IT);
|
||||
}
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 1) serviceAccountNamespace File is present or not
|
||||
* 2) if the above is present, under what actualPath
|
||||
* </pre>
|
||||
*/
|
||||
private Path serviceAccountFileResolved(boolean present, String actualPath) {
|
||||
Path path = Mockito.mock(Path.class);
|
||||
paths.when(() -> Paths.get(actualPath)).thenReturn(path);
|
||||
files.when(() -> Files.isRegularFile(path)).thenReturn(present);
|
||||
return path;
|
||||
}
|
||||
|
||||
/*
|
||||
* returns "foundIt" for service account namespace
|
||||
*/
|
||||
private void mockServiceAccountNamespace(Path path) {
|
||||
files.when(() -> Files.readAllBytes(path)).thenReturn(FOUNT_IT.getBytes());
|
||||
}
|
||||
|
||||
private Optional<String> kubernetesProfile() {
|
||||
return Arrays.stream(context.getEnvironment().getActiveProfiles()).filter("kubernetes"::equals).findFirst();
|
||||
}
|
||||
|
||||
private Optional<PropertySource<?>> kubernetesPropertySource() {
|
||||
return context.getEnvironment().getPropertySources().stream()
|
||||
.filter(x -> "KUBERNETES_NAMESPACE_PROPERTY_SOURCE".equals(x.getName())).findAny();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user