Initial commit
This commit is contained in:
86
spring-cloud-kubernetes-controllers/.flattened-pom.xml
Normal file
86
spring-cloud-kubernetes-controllers/.flattened-pom.xml
Normal file
@@ -0,0 +1,86 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-kubernetes</artifactId>
|
||||
<version>3.0.4-SNAPSHOT</version>
|
||||
</parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-kubernetes-controllers</artifactId>
|
||||
<version>3.0.4-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
<description>Spring Cloud parent pom, managing plugins and dependencies for Spring
|
||||
Cloud projects</description>
|
||||
<url>https://cloud.spring.io/spring-cloud-kubernetes-controllers</url>
|
||||
<inceptionYear>2017</inceptionYear>
|
||||
<organization>
|
||||
<name>Pivotal Software, Inc.</name>
|
||||
<url>https://www.spring.io</url>
|
||||
</organization>
|
||||
<licenses>
|
||||
<license>
|
||||
<name>Apache License, Version 2.0</name>
|
||||
<url>https://www.apache.org/licenses/LICENSE-2.0.txt</url>
|
||||
<distribution>repo</distribution>
|
||||
</license>
|
||||
</licenses>
|
||||
<developers>
|
||||
<developer>
|
||||
<id>dsyer</id>
|
||||
<name>Dave Syer</name>
|
||||
<email>dsyer at pivotal.io</email>
|
||||
<organization>Pivotal Software, Inc.</organization>
|
||||
<organizationUrl>https://www.spring.io</organizationUrl>
|
||||
<roles>
|
||||
<role>lead</role>
|
||||
</roles>
|
||||
</developer>
|
||||
<developer>
|
||||
<id>sgibb</id>
|
||||
<name>Spencer Gibb</name>
|
||||
<email>sgibb at pivotal.io</email>
|
||||
<organization>Pivotal Software, Inc.</organization>
|
||||
<organizationUrl>https://www.spring.io</organizationUrl>
|
||||
<roles>
|
||||
<role>lead</role>
|
||||
</roles>
|
||||
</developer>
|
||||
<developer>
|
||||
<id>mgrzejszczak</id>
|
||||
<name>Marcin Grzejszczak</name>
|
||||
<email>mgrzejszczak at pivotal.io</email>
|
||||
<organization>Pivotal Software, Inc.</organization>
|
||||
<organizationUrl>https://www.spring.io</organizationUrl>
|
||||
<roles>
|
||||
<role>developer</role>
|
||||
</roles>
|
||||
</developer>
|
||||
<developer>
|
||||
<id>rbaxter</id>
|
||||
<name>Ryan Baxter</name>
|
||||
<email>rbaxter at pivotal.io</email>
|
||||
<organization>Pivotal Software, Inc.</organization>
|
||||
<organizationUrl>https://www.spring.io</organizationUrl>
|
||||
<roles>
|
||||
<role>developer</role>
|
||||
</roles>
|
||||
</developer>
|
||||
<developer>
|
||||
<id>omaciaszeksharma</id>
|
||||
<name>Olga Maciaszek-Sharma</name>
|
||||
<email>omaciaszeksharma at pivotal.io</email>
|
||||
<organization>Pivotal Software, Inc.</organization>
|
||||
<organizationUrl>https://www.spring.io</organizationUrl>
|
||||
<roles>
|
||||
<role>developer</role>
|
||||
</roles>
|
||||
</developer>
|
||||
</developers>
|
||||
<scm>
|
||||
<connection>scm:git:git://github.com/spring-cloud/spring-cloud-kubernetes.git/spring-cloud-kubernetes-controllers</connection>
|
||||
<developerConnection>scm:git:ssh://git@github.com/spring-cloud/spring-cloud-kubernetes.git/spring-cloud-kubernetes-controllers</developerConnection>
|
||||
<url>https://github.com/spring-cloud/spring-cloud-kubernetes/spring-cloud-kubernetes-controllers</url>
|
||||
</scm>
|
||||
</project>
|
||||
@@ -1,21 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>spring-cloud-kubernetes</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>3.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<artifactId>spring-cloud-kubernetes-controllers</artifactId>
|
||||
|
||||
<modules>
|
||||
<module>spring-cloud-kubernetes-configuration-watcher</module>
|
||||
<module>spring-cloud-kubernetes-discoveryserver</module>
|
||||
<module>spring-cloud-kubernetes-configserver</module>
|
||||
</modules>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,105 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-kubernetes-controllers</artifactId>
|
||||
<version>3.0.4-SNAPSHOT</version>
|
||||
</parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-kubernetes-configserver</artifactId>
|
||||
<version>3.0.4-SNAPSHOT</version>
|
||||
<description>Spring Cloud parent pom, managing plugins and dependencies for Spring
|
||||
Cloud projects</description>
|
||||
<url>https://cloud.spring.io/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configserver</url>
|
||||
<inceptionYear>2017</inceptionYear>
|
||||
<organization>
|
||||
<name>Pivotal Software, Inc.</name>
|
||||
<url>https://www.spring.io</url>
|
||||
</organization>
|
||||
<licenses>
|
||||
<license>
|
||||
<name>Apache License, Version 2.0</name>
|
||||
<url>https://www.apache.org/licenses/LICENSE-2.0.txt</url>
|
||||
<distribution>repo</distribution>
|
||||
</license>
|
||||
</licenses>
|
||||
<developers>
|
||||
<developer>
|
||||
<id>dsyer</id>
|
||||
<name>Dave Syer</name>
|
||||
<email>dsyer at pivotal.io</email>
|
||||
<organization>Pivotal Software, Inc.</organization>
|
||||
<organizationUrl>https://www.spring.io</organizationUrl>
|
||||
<roles>
|
||||
<role>lead</role>
|
||||
</roles>
|
||||
</developer>
|
||||
<developer>
|
||||
<id>sgibb</id>
|
||||
<name>Spencer Gibb</name>
|
||||
<email>sgibb at pivotal.io</email>
|
||||
<organization>Pivotal Software, Inc.</organization>
|
||||
<organizationUrl>https://www.spring.io</organizationUrl>
|
||||
<roles>
|
||||
<role>lead</role>
|
||||
</roles>
|
||||
</developer>
|
||||
<developer>
|
||||
<id>mgrzejszczak</id>
|
||||
<name>Marcin Grzejszczak</name>
|
||||
<email>mgrzejszczak at pivotal.io</email>
|
||||
<organization>Pivotal Software, Inc.</organization>
|
||||
<organizationUrl>https://www.spring.io</organizationUrl>
|
||||
<roles>
|
||||
<role>developer</role>
|
||||
</roles>
|
||||
</developer>
|
||||
<developer>
|
||||
<id>rbaxter</id>
|
||||
<name>Ryan Baxter</name>
|
||||
<email>rbaxter at pivotal.io</email>
|
||||
<organization>Pivotal Software, Inc.</organization>
|
||||
<organizationUrl>https://www.spring.io</organizationUrl>
|
||||
<roles>
|
||||
<role>developer</role>
|
||||
</roles>
|
||||
</developer>
|
||||
<developer>
|
||||
<id>omaciaszeksharma</id>
|
||||
<name>Olga Maciaszek-Sharma</name>
|
||||
<email>omaciaszeksharma at pivotal.io</email>
|
||||
<organization>Pivotal Software, Inc.</organization>
|
||||
<organizationUrl>https://www.spring.io</organizationUrl>
|
||||
<roles>
|
||||
<role>developer</role>
|
||||
</roles>
|
||||
</developer>
|
||||
</developers>
|
||||
<scm>
|
||||
<connection>scm:git:git://github.com/spring-cloud/spring-cloud-kubernetes.git/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configserver</connection>
|
||||
<developerConnection>scm:git:ssh://git@github.com/spring-cloud/spring-cloud-kubernetes.git/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configserver</developerConnection>
|
||||
<url>https://github.com/spring-cloud/spring-cloud-kubernetes/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configserver</url>
|
||||
</scm>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-config-server</artifactId>
|
||||
<version>4.0.4-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
<version>3.0.7</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-kubernetes-client-config</artifactId>
|
||||
<version>3.0.4-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -1,138 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>spring-cloud-kubernetes-controllers</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>3.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>spring-cloud-kubernetes-configserver</artifactId>
|
||||
|
||||
<properties>
|
||||
<docker.registry.organization>springcloud</docker.registry.organization>
|
||||
<plexus-archiver.version>4.1.0</plexus-archiver.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-config-server</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-kubernetes-client-config</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<!-- in favor of mockito-inline -->
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.tomakehurst</groupId>
|
||||
<artifactId>wiremock-jre8-standalone</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-inline</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-kubernetes-test-support</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<image>
|
||||
<name>${env.IMAGE}</name>
|
||||
</image>
|
||||
<goal>build-image</goal>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>build-image</id>
|
||||
<configuration>
|
||||
<skip>${skip.build.image}</skip>
|
||||
</configuration>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>build-image</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>repackage</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>dockerpush</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>com.spotify</groupId>
|
||||
<artifactId>dockerfile-maven-plugin</artifactId>
|
||||
<version>1.4.12</version>
|
||||
<configuration>
|
||||
<repository>${docker.registry.organization}/${artifactId}</repository>
|
||||
<tag>${project.version}</tag>
|
||||
<username>${env.DOCKER_HUB_USERNAME}</username>
|
||||
<password>${env.DOCKER_HUB_PASSWORD}</password>
|
||||
<build>
|
||||
<noCache>true</noCache>
|
||||
</build>
|
||||
</configuration>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.codehaus.plexus</groupId>
|
||||
<artifactId>plexus-archiver</artifactId>
|
||||
<version>${plexus-archiver.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>imagename</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>!env.IMAGE</name>
|
||||
</property>
|
||||
</activation>
|
||||
<properties>
|
||||
<env.IMAGE>springcloud/${project.artifactId}:${project.version}</env.IMAGE>
|
||||
</properties>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* 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.configserver;
|
||||
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.cloud.config.server.ConfigServerApplication;
|
||||
import org.springframework.cloud.config.server.EnableConfigServer;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@EnableConfigServer
|
||||
public class KubernetesConfigServerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
new SpringApplicationBuilder(ConfigServerApplication.class).run(args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
/*
|
||||
* 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.configserver;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import io.kubernetes.client.openapi.apis.CoreV1Api;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnCloudPlatform;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.cloud.CloudPlatform;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.config.server.config.ConfigServerAutoConfiguration;
|
||||
import org.springframework.cloud.config.server.environment.EnvironmentRepository;
|
||||
import org.springframework.cloud.kubernetes.client.KubernetesClientAutoConfiguration;
|
||||
import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigContext;
|
||||
import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigMapPropertySource;
|
||||
import org.springframework.cloud.kubernetes.client.config.KubernetesClientSecretsPropertySource;
|
||||
import org.springframework.cloud.kubernetes.commons.ConditionalOnKubernetesConfigEnabled;
|
||||
import org.springframework.cloud.kubernetes.commons.ConditionalOnKubernetesSecretsEnabled;
|
||||
import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider;
|
||||
import org.springframework.cloud.kubernetes.commons.config.NamedConfigMapNormalizedSource;
|
||||
import org.springframework.cloud.kubernetes.commons.config.NamedSecretNormalizedSource;
|
||||
import org.springframework.cloud.kubernetes.commons.config.NormalizedSource;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
|
||||
import static org.springframework.cloud.kubernetes.configserver.KubernetesPropertySourceSupplier.namespaceSplitter;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@Configuration
|
||||
@AutoConfigureAfter({ KubernetesClientAutoConfiguration.class })
|
||||
@AutoConfigureBefore({ ConfigServerAutoConfiguration.class })
|
||||
@ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES)
|
||||
@EnableConfigurationProperties(KubernetesConfigServerProperties.class)
|
||||
public class KubernetesConfigServerAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@Profile("kubernetes")
|
||||
public EnvironmentRepository kubernetesEnvironmentRepository(CoreV1Api coreV1Api,
|
||||
List<KubernetesPropertySourceSupplier> kubernetesPropertySourceSuppliers,
|
||||
KubernetesNamespaceProvider kubernetesNamespaceProvider) {
|
||||
return new KubernetesEnvironmentRepository(coreV1Api, kubernetesPropertySourceSuppliers,
|
||||
kubernetesNamespaceProvider.getNamespace());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnKubernetesConfigEnabled
|
||||
@ConditionalOnProperty(value = "spring.cloud.kubernetes.config.enableApi", matchIfMissing = true)
|
||||
public KubernetesPropertySourceSupplier configMapPropertySourceSupplier(
|
||||
KubernetesConfigServerProperties properties) {
|
||||
return (coreApi, applicationName, namespace, springEnv) -> {
|
||||
List<String> namespaces = namespaceSplitter(properties.getConfigMapNamespaces(), namespace);
|
||||
List<MapPropertySource> propertySources = new ArrayList<>();
|
||||
|
||||
namespaces.forEach(space -> {
|
||||
|
||||
NamedConfigMapNormalizedSource source = new NamedConfigMapNormalizedSource(applicationName, space,
|
||||
false, true);
|
||||
KubernetesClientConfigContext context = new KubernetesClientConfigContext(coreApi, source, space,
|
||||
springEnv);
|
||||
|
||||
propertySources.add(new KubernetesClientConfigMapPropertySource(context));
|
||||
});
|
||||
return propertySources;
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnKubernetesSecretsEnabled
|
||||
@ConditionalOnProperty("spring.cloud.kubernetes.secrets.enableApi")
|
||||
public KubernetesPropertySourceSupplier secretsPropertySourceSupplier(KubernetesConfigServerProperties properties) {
|
||||
return (coreApi, applicationName, namespace, springEnv) -> {
|
||||
List<String> namespaces = namespaceSplitter(properties.getSecretsNamespaces(), namespace);
|
||||
List<MapPropertySource> propertySources = new ArrayList<>();
|
||||
|
||||
namespaces.forEach(space -> {
|
||||
NormalizedSource source = new NamedSecretNormalizedSource(applicationName, space, false, false);
|
||||
KubernetesClientConfigContext context = new KubernetesClientConfigContext(coreApi, source, space,
|
||||
springEnv);
|
||||
propertySources.add(new KubernetesClientSecretsPropertySource(context));
|
||||
});
|
||||
|
||||
return propertySources;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* 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.configserver;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
|
||||
@ConfigurationProperties("spring.cloud.kubernetes.configserver")
|
||||
public class KubernetesConfigServerProperties {
|
||||
|
||||
private String configMapNamespaces = "";
|
||||
|
||||
private String secretsNamespaces = "";
|
||||
|
||||
public String getConfigMapNamespaces() {
|
||||
return configMapNamespaces;
|
||||
}
|
||||
|
||||
public void setConfigMapNamespaces(String configMapNamespaces) {
|
||||
this.configMapNamespaces = configMapNamespaces;
|
||||
}
|
||||
|
||||
public String getSecretsNamespaces() {
|
||||
return secretsNamespaces;
|
||||
}
|
||||
|
||||
public void setSecretsNamespaces(String secretsNamespaces) {
|
||||
this.secretsNamespaces = secretsNamespaces;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
/*
|
||||
* 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.configserver;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.kubernetes.client.openapi.apis.CoreV1Api;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.cloud.config.environment.Environment;
|
||||
import org.springframework.cloud.config.environment.PropertySource;
|
||||
import org.springframework.cloud.config.server.environment.EnvironmentRepository;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class KubernetesEnvironmentRepository implements EnvironmentRepository {
|
||||
|
||||
private static final Log LOG = LogFactory.getLog(KubernetesEnvironmentRepository.class);
|
||||
|
||||
private CoreV1Api coreApi;
|
||||
|
||||
private List<KubernetesPropertySourceSupplier> kubernetesPropertySourceSuppliers;
|
||||
|
||||
private String namespace;
|
||||
|
||||
public KubernetesEnvironmentRepository(CoreV1Api coreApi,
|
||||
List<KubernetesPropertySourceSupplier> kubernetesPropertySourceSuppliers, String namespace) {
|
||||
this.coreApi = coreApi;
|
||||
this.kubernetesPropertySourceSuppliers = kubernetesPropertySourceSuppliers;
|
||||
this.namespace = namespace;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Environment findOne(String application, String profile, String label) {
|
||||
return findOne(application, profile, label, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Environment findOne(String application, String profile, String label, boolean includeOrigin) {
|
||||
String[] profiles = StringUtils.commaDelimitedListToStringArray(profile);
|
||||
LOG.info("Profiles: " + profile);
|
||||
LOG.info("Application: " + application);
|
||||
LOG.info("Label: " + label);
|
||||
Environment environment = new Environment(application, profiles, label, null, null);
|
||||
try {
|
||||
StandardEnvironment springEnv = new StandardEnvironment();
|
||||
springEnv.setActiveProfiles(profiles);
|
||||
if (!"application".equalsIgnoreCase(application)) {
|
||||
addApplicationConfiguration(environment, springEnv, application);
|
||||
}
|
||||
addApplicationConfiguration(environment, springEnv, "application");
|
||||
return environment;
|
||||
}
|
||||
catch (Exception e) {
|
||||
LOG.warn(e);
|
||||
}
|
||||
return environment;
|
||||
}
|
||||
|
||||
private void addApplicationConfiguration(Environment environment, StandardEnvironment springEnv,
|
||||
String applicationName) {
|
||||
kubernetesPropertySourceSuppliers.stream().forEach(supplier -> {
|
||||
List<MapPropertySource> propertySources = supplier.get(coreApi, applicationName, namespace, springEnv);
|
||||
propertySources.forEach(propertySource -> {
|
||||
if (propertySource.getPropertyNames().length > 0) {
|
||||
LOG.debug("Adding PropertySource " + propertySource.getName());
|
||||
LOG.debug("PropertySource Names: "
|
||||
+ StringUtils.arrayToCommaDelimitedString(propertySource.getPropertyNames()));
|
||||
environment.add(new PropertySource(propertySource.getName(), propertySource.getSource()));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* 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.configserver;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import io.kubernetes.client.openapi.apis.CoreV1Api;
|
||||
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public interface KubernetesPropertySourceSupplier {
|
||||
|
||||
List<MapPropertySource> get(CoreV1Api coreV1Api, String name, String namespace, Environment environment);
|
||||
|
||||
static List<String> namespaceSplitter(String namespacesString, String currentNamespace) {
|
||||
List<String> namespaces = Collections.singletonList(currentNamespace);
|
||||
String[] namespacesArray = StringUtils.commaDelimitedListToStringArray(namespacesString);
|
||||
if (namespacesArray.length > 0) {
|
||||
namespaces = Arrays.asList(namespacesArray);
|
||||
}
|
||||
return namespaces;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
org.springframework.cloud.kubernetes.configserver.KubernetesConfigServerAutoConfiguration
|
||||
@@ -1,13 +0,0 @@
|
||||
spring:
|
||||
application:
|
||||
name: kubernetesconfigserver
|
||||
|
||||
server:
|
||||
port: 8888
|
||||
|
||||
---
|
||||
spring:
|
||||
config:
|
||||
activate:
|
||||
on-cloud-platform: kubernetes
|
||||
import: "optional:kubernetes:"
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* 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.configserver;
|
||||
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
properties = { "spring.main.cloud-platform=KUBERNETES", "spring.cloud.kubernetes.client.namespace=default",
|
||||
"spring.profiles.include=kubernetes", "spring.cloud.kubernetes.secrets.enableApi=true",
|
||||
"spring.cloud.bootstrap.enabled=true" },
|
||||
classes = { KubernetesConfigServerApplication.class })
|
||||
public class BootstrapConfigServerIntegrationTest extends ConfigServerIntegrationTest {
|
||||
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* 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.configserver;
|
||||
|
||||
import com.github.tomakehurst.wiremock.WireMockServer;
|
||||
import com.github.tomakehurst.wiremock.client.WireMock;
|
||||
import io.kubernetes.client.util.ClientBuilder;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.mockito.MockedStatic;
|
||||
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.kubernetes.client.KubernetesClientUtils;
|
||||
|
||||
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
|
||||
import static org.mockito.Mockito.mockStatic;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
properties = { "spring.main.cloud-platform=KUBERNETES", "spring.cloud.kubernetes.client.namespace=default",
|
||||
"spring.profiles.include=kubernetes", "spring.cloud.kubernetes.secrets.enableApi=true" },
|
||||
classes = { KubernetesConfigServerApplication.class })
|
||||
public class ConfigDataConfigServerIntegrationTest extends ConfigServerIntegrationTest {
|
||||
|
||||
private static WireMockServer wireMockServer;
|
||||
|
||||
private static MockedStatic<KubernetesClientUtils> clientUtilsMock;
|
||||
|
||||
@BeforeAll
|
||||
static void setup() {
|
||||
wireMockServer = new WireMockServer(options().dynamicPort());
|
||||
wireMockServer.start();
|
||||
WireMock.configureFor(wireMockServer.port());
|
||||
|
||||
clientUtilsMock = mockStatic(KubernetesClientUtils.class);
|
||||
clientUtilsMock.when(KubernetesClientUtils::kubernetesApiClient)
|
||||
.thenReturn(new ClientBuilder().setBasePath(wireMockServer.baseUrl()).build());
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void teardown() {
|
||||
wireMockServer.stop();
|
||||
clientUtilsMock.close();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void afterEach() {
|
||||
WireMock.reset();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.cloud.kubernetes.configserver;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
properties = { "spring.profiles.include=kubernetes,kubernetesdisabled" },
|
||||
classes = { KubernetesConfigServerApplication.class, MockConfig.class })
|
||||
class ConfigServerAutoConfigurationKubernetesDisabled {
|
||||
|
||||
@Autowired
|
||||
private ConfigurableApplicationContext context;
|
||||
|
||||
@Test
|
||||
void runTest() {
|
||||
assertThat(context.getBeanNamesForType(KubernetesEnvironmentRepository.class)).hasSize(0);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.cloud.kubernetes.configserver;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
classes = { KubernetesConfigServerApplication.class }, properties = { "spring.main.cloud-platform=KUBERNETES",
|
||||
"spring.profiles.include=kubernetes", "spring.cloud.kubernetes.client.namespace=default" })
|
||||
class ConfigServerAutoConfigurationKubernetesEnabledProfileIncluded {
|
||||
|
||||
@Autowired
|
||||
private ConfigurableApplicationContext context;
|
||||
|
||||
@Test
|
||||
void runTest() {
|
||||
assertThat(context.getBeanNamesForType(KubernetesEnvironmentRepository.class)).hasSize(1);
|
||||
assertThat(context.getBeanNamesForType(KubernetesPropertySourceSupplier.class)).hasSize(1);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.cloud.kubernetes.configserver;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
classes = { KubernetesConfigServerApplication.class },
|
||||
properties = { "spring.main.cloud-platform=KUBERNETES", "spring.profiles.include=kubernetes",
|
||||
"spring.cloud.kubernetes.client.namespace=default", "spring.cloud.kubernetes.config.enableApi=false" })
|
||||
class ConfigServerAutoConfigurationKubernetesEnabledProfileIncludedConfigApiDisabled {
|
||||
|
||||
@Autowired
|
||||
private ConfigurableApplicationContext context;
|
||||
|
||||
@Test
|
||||
void runTest() {
|
||||
assertThat(context.getBeanNamesForType(KubernetesEnvironmentRepository.class)).hasSize(1);
|
||||
assertThat(context.getBeanNamesForType(KubernetesPropertySourceSupplier.class)).hasSize(0);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.cloud.kubernetes.configserver;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
classes = { KubernetesConfigServerApplication.class }, properties = { "spring.main.cloud-platform=KUBERNETES",
|
||||
"spring.profiles.include=kubernetes", "spring.cloud.kubernetes.config.enabled=false" })
|
||||
class ConfigServerAutoConfigurationKubernetesEnabledProfileIncludedConfigMapDisabled {
|
||||
|
||||
@Autowired
|
||||
private ConfigurableApplicationContext context;
|
||||
|
||||
@Test
|
||||
void runTest() {
|
||||
assertThat(context.getBeanNamesForType(KubernetesEnvironmentRepository.class)).hasSize(1);
|
||||
assertThat(context.getBeanNamesForType(KubernetesPropertySourceSupplier.class)).hasSize(0);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.cloud.kubernetes.configserver;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
classes = { KubernetesConfigServerApplication.class }, properties = { "spring.main.cloud-platform=KUBERNETES",
|
||||
"spring.profiles.include=kubernetes", "spring.cloud.kubernetes.client.namespace=default" })
|
||||
class ConfigServerAutoConfigurationKubernetesEnabledProfileIncludedSecretsApiDisabled {
|
||||
|
||||
@Autowired
|
||||
private ConfigurableApplicationContext context;
|
||||
|
||||
@Test
|
||||
void runTest() {
|
||||
assertThat(context.getBeanNamesForType(KubernetesEnvironmentRepository.class)).hasSize(1);
|
||||
assertThat(context.getBeanNamesForType(KubernetesPropertySourceSupplier.class)).hasSize(1);
|
||||
assertThat(context.getBeanNamesForType(KubernetesPropertySourceSupplier.class)[0])
|
||||
.isEqualTo("configMapPropertySourceSupplier");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.cloud.kubernetes.configserver;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
classes = { KubernetesConfigServerApplication.class },
|
||||
properties = { "spring.profiles.include=kubernetes", "spring.main.cloud-platform=KUBERNETES",
|
||||
"spring.cloud.kubernetes.client.namespace=default", "spring.cloud.kubernetes.secrets.enableApi=true" })
|
||||
class ConfigServerAutoConfigurationKubernetesEnabledProfileIncludedSecretsApiEnabled {
|
||||
|
||||
@Autowired
|
||||
private ConfigurableApplicationContext context;
|
||||
|
||||
@Test
|
||||
void runTest() {
|
||||
assertThat(context.getBeanNamesForType(KubernetesEnvironmentRepository.class)).hasSize(1);
|
||||
assertThat(context.getBeanNamesForType(KubernetesPropertySourceSupplier.class)).hasSize(2);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.cloud.kubernetes.configserver;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
classes = { KubernetesConfigServerApplication.class, MockConfig.class },
|
||||
properties = { "spring.profiles.include=kubernetes,kubernetesdisabled" })
|
||||
class ConfigServerAutoConfigurationKubernetesProfileMissing {
|
||||
|
||||
@Autowired
|
||||
private ConfigurableApplicationContext context;
|
||||
|
||||
@Test
|
||||
void runTest() {
|
||||
assertThat(context.getBeanNamesForType(KubernetesEnvironmentRepository.class)).hasSize(0);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
/*
|
||||
* 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.configserver;
|
||||
|
||||
import com.github.tomakehurst.wiremock.client.WireMock;
|
||||
import io.kubernetes.client.openapi.JSON;
|
||||
import io.kubernetes.client.openapi.models.V1ConfigMapBuilder;
|
||||
import io.kubernetes.client.openapi.models.V1ConfigMapList;
|
||||
import io.kubernetes.client.openapi.models.V1ListMetaBuilder;
|
||||
import io.kubernetes.client.openapi.models.V1ObjectMetaBuilder;
|
||||
import io.kubernetes.client.openapi.models.V1SecretBuilder;
|
||||
import io.kubernetes.client.openapi.models.V1SecretList;
|
||||
import io.kubernetes.client.openapi.models.V1SecretListBuilder;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||
import org.springframework.cloud.config.environment.Environment;
|
||||
|
||||
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
|
||||
import static com.github.tomakehurst.wiremock.client.WireMock.get;
|
||||
import static com.github.tomakehurst.wiremock.client.WireMock.urlMatching;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
abstract class ConfigServerIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
private TestRestTemplate testRestTemplate;
|
||||
|
||||
@BeforeEach
|
||||
public void beforeEach() {
|
||||
V1ConfigMapList TEST_CONFIGMAP = new V1ConfigMapList().addItemsItem(new V1ConfigMapBuilder().withMetadata(
|
||||
new V1ObjectMetaBuilder().withName("test-cm").withNamespace("default").withResourceVersion("1").build())
|
||||
.addToData("app.name", "test").build());
|
||||
|
||||
V1SecretList TEST_SECRET = new V1SecretListBuilder()
|
||||
.withMetadata(new V1ListMetaBuilder().withResourceVersion("1").build())
|
||||
.addToItems(new V1SecretBuilder()
|
||||
.withMetadata(new V1ObjectMetaBuilder().withName("test-cm").withResourceVersion("0")
|
||||
.withNamespace("default").build())
|
||||
.addToData("password", "p455w0rd".getBytes()).addToData("username", "user".getBytes()).build())
|
||||
.build();
|
||||
|
||||
WireMock.stubFor(get(urlMatching("^/api/v1/namespaces/default/configmaps.*"))
|
||||
.willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(TEST_CONFIGMAP))));
|
||||
|
||||
WireMock.stubFor(get(urlMatching("^/api/v1/namespaces/default/secrets.*"))
|
||||
.willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(TEST_SECRET))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enabled() {
|
||||
Environment env = testRestTemplate.getForObject("/test-cm/default", Environment.class);
|
||||
assertThat(env.getPropertySources().size()).isEqualTo(2);
|
||||
assertThat(env.getPropertySources().get(0).getName().equals("configmap.test-cm.default")).isTrue();
|
||||
assertThat(env.getPropertySources().get(0).getSource().get("app.name")).isEqualTo("test");
|
||||
assertThat(env.getPropertySources().get(1).getName().equals("secret.test-cm.default")).isTrue();
|
||||
assertThat(env.getPropertySources().get(1).getSource().get("password")).isEqualTo("p455w0rd");
|
||||
assertThat(env.getPropertySources().get(1).getSource().get("username")).isEqualTo("user");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,295 +0,0 @@
|
||||
/*
|
||||
* 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.configserver;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import io.kubernetes.client.openapi.ApiException;
|
||||
import io.kubernetes.client.openapi.apis.CoreV1Api;
|
||||
import io.kubernetes.client.openapi.models.V1ConfigMapBuilder;
|
||||
import io.kubernetes.client.openapi.models.V1ConfigMapList;
|
||||
import io.kubernetes.client.openapi.models.V1ObjectMetaBuilder;
|
||||
import io.kubernetes.client.openapi.models.V1SecretBuilder;
|
||||
import io.kubernetes.client.openapi.models.V1SecretList;
|
||||
import io.kubernetes.client.openapi.models.V1SecretListBuilder;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.cloud.config.environment.Environment;
|
||||
import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigContext;
|
||||
import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigMapPropertySource;
|
||||
import org.springframework.cloud.kubernetes.client.config.KubernetesClientSecretsPropertySource;
|
||||
import org.springframework.cloud.kubernetes.commons.config.NamedConfigMapNormalizedSource;
|
||||
import org.springframework.cloud.kubernetes.commons.config.NamedSecretNormalizedSource;
|
||||
import org.springframework.cloud.kubernetes.commons.config.NormalizedSource;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
class KubernetesEnvironmentRepositoryTests {
|
||||
|
||||
private static final List<KubernetesPropertySourceSupplier> KUBERNETES_PROPERTY_SOURCE_SUPPLIER = new ArrayList<>();
|
||||
|
||||
private static final String VALUE = "dummy:\n property:\n string2: \"a\"\n int2: 1\n bool2: true\n";
|
||||
|
||||
private static final String DEFAULT_NAMESPACE = "default";
|
||||
|
||||
private static final V1ConfigMapList CONFIGMAP_DEFAULT_LIST = new V1ConfigMapList()
|
||||
.addItemsItem(new V1ConfigMapBuilder()
|
||||
.withMetadata(
|
||||
new V1ObjectMetaBuilder().withName("application").withNamespace(DEFAULT_NAMESPACE).build())
|
||||
.addToData("application.yaml", VALUE).build())
|
||||
.addItemsItem(new V1ConfigMapBuilder()
|
||||
.withMetadata(new V1ObjectMetaBuilder().withName("stores").withNamespace(DEFAULT_NAMESPACE).build())
|
||||
.addToData("application.yaml", VALUE).build())
|
||||
.addItemsItem(new V1ConfigMapBuilder()
|
||||
.withMetadata(
|
||||
new V1ObjectMetaBuilder().withName("stores-dev").withNamespace(DEFAULT_NAMESPACE).build())
|
||||
.addToData("application.yaml",
|
||||
"dummy:\n property:\n string1: \"a\"\n string2: \"b\"\n int2: 2\n bool2: false\n")
|
||||
.build());
|
||||
|
||||
private static final V1ConfigMapList CONFIGMAP_DEV_LIST = new V1ConfigMapList()
|
||||
.addItemsItem(new V1ConfigMapBuilder()
|
||||
.withMetadata(new V1ObjectMetaBuilder().withName("stores").withNamespace("dev")
|
||||
.withResourceVersion("1").build())
|
||||
.addToData("application.yaml",
|
||||
"dummy:\n property:\n string2: \"dev\"\n int2: 1\n bool2: true\n")
|
||||
.build());
|
||||
|
||||
private static final V1SecretList SECRET_LIST = new V1SecretListBuilder()
|
||||
.addToItems(new V1SecretBuilder()
|
||||
.withMetadata(new V1ObjectMetaBuilder().withName("application").withResourceVersion("0")
|
||||
.withNamespace("default").build())
|
||||
.addToData("password", "p455w0rd".getBytes()).addToData("username", "user".getBytes()).build())
|
||||
.addToItems(new V1SecretBuilder()
|
||||
.withMetadata(new V1ObjectMetaBuilder().withName("stores").withResourceVersion("0")
|
||||
.withNamespace("default").build())
|
||||
.addToData("password", "password-from-stores".getBytes()).addToData("username", "stores".getBytes())
|
||||
.build())
|
||||
.addToItems(new V1SecretBuilder()
|
||||
.withMetadata(new V1ObjectMetaBuilder().withName("stores-dev").withResourceVersion("0")
|
||||
.withNamespace("default").build())
|
||||
.addToData("password", "password-from-stores-dev".getBytes())
|
||||
.addToData("username", "stores-dev".getBytes()).build())
|
||||
.build();
|
||||
|
||||
@BeforeAll
|
||||
public static void before() {
|
||||
KUBERNETES_PROPERTY_SOURCE_SUPPLIER.add((coreApi, applicationName, namespace, springEnv) -> {
|
||||
List<MapPropertySource> propertySources = new ArrayList<>();
|
||||
|
||||
NormalizedSource defaultSource = new NamedConfigMapNormalizedSource(applicationName, "default", false,
|
||||
true);
|
||||
KubernetesClientConfigContext defaultContext = new KubernetesClientConfigContext(coreApi, defaultSource,
|
||||
"default", springEnv);
|
||||
|
||||
NormalizedSource devSource = new NamedConfigMapNormalizedSource(applicationName, "dev", false, true);
|
||||
KubernetesClientConfigContext devContext = new KubernetesClientConfigContext(coreApi, devSource, "dev",
|
||||
springEnv);
|
||||
|
||||
propertySources.add(new KubernetesClientConfigMapPropertySource(defaultContext));
|
||||
propertySources.add(new KubernetesClientConfigMapPropertySource(devContext));
|
||||
return propertySources;
|
||||
});
|
||||
KUBERNETES_PROPERTY_SOURCE_SUPPLIER.add((coreApi, applicationName, namespace, springEnv) -> {
|
||||
List<MapPropertySource> propertySources = new ArrayList<>();
|
||||
|
||||
NormalizedSource source = new NamedSecretNormalizedSource(applicationName, "default", false, true);
|
||||
KubernetesClientConfigContext context = new KubernetesClientConfigContext(coreApi, source, "default",
|
||||
springEnv);
|
||||
|
||||
propertySources.add(new KubernetesClientSecretsPropertySource(context));
|
||||
return propertySources;
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testApplicationCase() throws ApiException {
|
||||
CoreV1Api coreApi = mock(CoreV1Api.class);
|
||||
when(coreApi.listNamespacedConfigMap(eq("default"), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null),
|
||||
eq(null), eq(null), eq(null), eq(null))).thenReturn(CONFIGMAP_DEFAULT_LIST);
|
||||
when(coreApi.listNamespacedSecret(eq("default"), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null),
|
||||
eq(null), eq(null), eq(null), eq(null))).thenReturn(SECRET_LIST);
|
||||
when(coreApi.listNamespacedConfigMap(eq("dev"), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null),
|
||||
eq(null), eq(null), eq(null), eq(null))).thenReturn(CONFIGMAP_DEV_LIST);
|
||||
KubernetesEnvironmentRepository environmentRepository = new KubernetesEnvironmentRepository(coreApi,
|
||||
KUBERNETES_PROPERTY_SOURCE_SUPPLIER, "default");
|
||||
Environment environment = environmentRepository.findOne("application", "", "");
|
||||
assertThat(environment.getPropertySources().size()).isEqualTo(2);
|
||||
environment.getPropertySources().forEach(propertySource -> {
|
||||
assertThat(propertySource.getName().equals("configmap.application.default")
|
||||
|| propertySource.getName().equals("secret.application.default")).isTrue();
|
||||
if (propertySource.getName().equals("configmap.application.default")) {
|
||||
assertThat(propertySource.getSource().size()).isEqualTo(3);
|
||||
assertThat(propertySource.getSource().get("dummy.property.int2")).isEqualTo(1);
|
||||
assertThat(propertySource.getSource().get("dummy.property.bool2")).isEqualTo(true);
|
||||
assertThat(propertySource.getSource().get("dummy.property.string2")).isEqualTo("a");
|
||||
}
|
||||
if (propertySource.getName().equals("secrets.application.default")) {
|
||||
assertThat(propertySource.getSource().size()).isEqualTo(2);
|
||||
assertThat(propertySource.getSource().get("username")).isEqualTo("user");
|
||||
assertThat(propertySource.getSource().get("password")).isEqualTo("p455w0rd");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoresCase() throws ApiException {
|
||||
CoreV1Api coreApi = mock(CoreV1Api.class);
|
||||
when(coreApi.listNamespacedConfigMap(eq("default"), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null),
|
||||
eq(null), eq(null), eq(null), eq(null))).thenReturn(CONFIGMAP_DEFAULT_LIST);
|
||||
when(coreApi.listNamespacedConfigMap(eq("dev"), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null),
|
||||
eq(null), eq(null), eq(null), eq(null))).thenReturn(CONFIGMAP_DEV_LIST);
|
||||
when(coreApi.listNamespacedSecret(eq("default"), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null),
|
||||
eq(null), eq(null), eq(null), eq(null))).thenReturn(SECRET_LIST);
|
||||
KubernetesEnvironmentRepository environmentRepository = new KubernetesEnvironmentRepository(coreApi,
|
||||
KUBERNETES_PROPERTY_SOURCE_SUPPLIER, "default");
|
||||
Environment environment = environmentRepository.findOne("stores", "", "");
|
||||
assertThat(environment.getPropertySources().size()).isEqualTo(5);
|
||||
environment.getPropertySources().forEach(propertySource -> {
|
||||
assertThat(propertySource.getName().equals("configmap.application.default")
|
||||
|| propertySource.getName().equals("secret.application.default")
|
||||
|| propertySource.getName().equals("configmap.stores.default")
|
||||
|| propertySource.getName().equals("configmap.stores.dev")
|
||||
|| propertySource.getName().equals("secret.stores.default")).isTrue();
|
||||
if (propertySource.getName().equals("configmap.application.default")) {
|
||||
assertThat(propertySource.getSource().size()).isEqualTo(3);
|
||||
assertThat(propertySource.getSource().get("dummy.property.int2")).isEqualTo(1);
|
||||
assertThat(propertySource.getSource().get("dummy.property.bool2")).isEqualTo(true);
|
||||
assertThat(propertySource.getSource().get("dummy.property.string2")).isEqualTo("a");
|
||||
}
|
||||
if (propertySource.getName().equals("secret.application.default")) {
|
||||
assertThat(propertySource.getSource().size()).isEqualTo(2);
|
||||
assertThat(propertySource.getSource().get("username")).isEqualTo("user");
|
||||
assertThat(propertySource.getSource().get("password")).isEqualTo("p455w0rd");
|
||||
}
|
||||
if (propertySource.getName().equals("configmap.stores.default")) {
|
||||
assertThat(propertySource.getSource().size()).isEqualTo(3);
|
||||
assertThat(propertySource.getSource().get("dummy.property.int2")).isEqualTo(1);
|
||||
assertThat(propertySource.getSource().get("dummy.property.bool2")).isEqualTo(true);
|
||||
assertThat(propertySource.getSource().get("dummy.property.string2")).isEqualTo("a");
|
||||
}
|
||||
if (propertySource.getName().equals("configmap.stores.dev")) {
|
||||
assertThat(propertySource.getSource().size()).isEqualTo(3);
|
||||
assertThat(propertySource.getSource().get("dummy.property.int2")).isEqualTo(1);
|
||||
assertThat(propertySource.getSource().get("dummy.property.bool2")).isEqualTo(true);
|
||||
assertThat(propertySource.getSource().get("dummy.property.string2")).isEqualTo("dev");
|
||||
}
|
||||
if (propertySource.getName().equals("secret.stores.default")) {
|
||||
assertThat(propertySource.getSource().size()).isEqualTo(2);
|
||||
assertThat(propertySource.getSource().get("username")).isEqualTo("stores");
|
||||
assertThat(propertySource.getSource().get("password")).isEqualTo("password-from-stores");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoresProfileCase() throws ApiException {
|
||||
CoreV1Api coreApi = mock(CoreV1Api.class);
|
||||
when(coreApi.listNamespacedConfigMap(eq("default"), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null),
|
||||
eq(null), eq(null), eq(null), eq(null))).thenReturn(CONFIGMAP_DEFAULT_LIST);
|
||||
when(coreApi.listNamespacedSecret(eq("default"), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null),
|
||||
eq(null), eq(null), eq(null), eq(null))).thenReturn(SECRET_LIST);
|
||||
when(coreApi.listNamespacedConfigMap(eq("dev"), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null),
|
||||
eq(null), eq(null), eq(null), eq(null))).thenReturn(CONFIGMAP_DEV_LIST);
|
||||
KubernetesEnvironmentRepository environmentRepository = new KubernetesEnvironmentRepository(coreApi,
|
||||
KUBERNETES_PROPERTY_SOURCE_SUPPLIER, "default");
|
||||
Environment environment = environmentRepository.findOne("stores", "dev", "");
|
||||
assertThat(environment.getPropertySources().size()).isEqualTo(5);
|
||||
environment.getPropertySources().forEach(propertySource -> {
|
||||
assertThat(propertySource.getName().equals("configmap.application.default")
|
||||
|| propertySource.getName().equals("secret.application.default")
|
||||
|| propertySource.getName().equals("configmap.stores.stores-dev.default")
|
||||
|| propertySource.getName().equals("configmap.stores.dev")
|
||||
|| propertySource.getName().equals("secret.stores.stores-dev.default")).isTrue();
|
||||
if (propertySource.getName().equals("configmap.application.default")) {
|
||||
assertThat(propertySource.getSource().size()).isEqualTo(3);
|
||||
assertThat(propertySource.getSource().get("dummy.property.int2")).isEqualTo(1);
|
||||
assertThat(propertySource.getSource().get("dummy.property.bool2")).isEqualTo(true);
|
||||
assertThat(propertySource.getSource().get("dummy.property.string2")).isEqualTo("a");
|
||||
}
|
||||
else if (propertySource.getName().equals("secret.application.default")) {
|
||||
assertThat(propertySource.getSource().size()).isEqualTo(2);
|
||||
assertThat(propertySource.getSource().get("username")).isEqualTo("user");
|
||||
assertThat(propertySource.getSource().get("password")).isEqualTo("p455w0rd");
|
||||
}
|
||||
else if (propertySource.getName().equals("configmap.stores.stores-dev.default")) {
|
||||
assertThat(propertySource.getSource().size()).isEqualTo(4);
|
||||
assertThat(propertySource.getSource().get("dummy.property.int2")).isEqualTo(2);
|
||||
assertThat(propertySource.getSource().get("dummy.property.bool2")).isEqualTo(false);
|
||||
assertThat(propertySource.getSource().get("dummy.property.string2")).isEqualTo("b");
|
||||
assertThat(propertySource.getSource().get("dummy.property.string1")).isEqualTo("a");
|
||||
}
|
||||
else if (propertySource.getName().equals("configmap.stores.dev")) {
|
||||
assertThat(propertySource.getSource().size()).isEqualTo(3);
|
||||
assertThat(propertySource.getSource().get("dummy.property.int2")).isEqualTo(1);
|
||||
assertThat(propertySource.getSource().get("dummy.property.bool2")).isEqualTo(true);
|
||||
assertThat(propertySource.getSource().get("dummy.property.string2")).isEqualTo("dev");
|
||||
}
|
||||
else if (propertySource.getName().equals("secret.stores.stores-dev.default")) {
|
||||
assertThat(propertySource.getSource().size()).isEqualTo(2);
|
||||
assertThat(propertySource.getSource().get("username")).isEqualTo("stores-dev");
|
||||
assertThat(propertySource.getSource().get("password")).isEqualTo("password-from-stores-dev");
|
||||
}
|
||||
else {
|
||||
Assertions.fail("no match in property source names");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testApplicationPropertiesAnSecretsOverride() throws ApiException {
|
||||
CoreV1Api coreApi = mock(CoreV1Api.class);
|
||||
when(coreApi.listNamespacedConfigMap(eq("default"), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null),
|
||||
eq(null), eq(null), eq(null), eq(null))).thenReturn(CONFIGMAP_DEFAULT_LIST);
|
||||
when(coreApi.listNamespacedSecret(eq("default"), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null),
|
||||
eq(null), eq(null), eq(null), eq(null))).thenReturn(SECRET_LIST);
|
||||
when(coreApi.listNamespacedConfigMap(eq("dev"), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null),
|
||||
eq(null), eq(null), eq(null), eq(null))).thenReturn(CONFIGMAP_DEV_LIST);
|
||||
KubernetesEnvironmentRepository environmentRepository = new KubernetesEnvironmentRepository(coreApi,
|
||||
KUBERNETES_PROPERTY_SOURCE_SUPPLIER, "default");
|
||||
Environment environment = environmentRepository.findOne("stores-dev", "", "");
|
||||
environment.getPropertySources().stream()
|
||||
.filter(propertySource -> propertySource.getName().startsWith("configmap"))
|
||||
.reduce((first, second) -> second).ifPresent(propertySource -> {
|
||||
assertThat(propertySource.getName()).isEqualTo("configmap.application.default");
|
||||
});
|
||||
environment.getPropertySources().stream()
|
||||
.filter(propertySource -> propertySource.getName().startsWith("configmap")).findFirst()
|
||||
.ifPresent(propertySource -> {
|
||||
assertThat(propertySource.getSource().get("dummy.property.int2")).isEqualTo(2);
|
||||
assertThat(propertySource.getSource().get("dummy.property.bool2")).isEqualTo(false);
|
||||
assertThat(propertySource.getSource().get("dummy.property.string2")).isEqualTo("b");
|
||||
});
|
||||
environment.getPropertySources().stream()
|
||||
.filter(propertySource -> propertySource.getName().startsWith("secrets")).findFirst()
|
||||
.ifPresent(propertySource -> {
|
||||
assertThat(propertySource.getSource().get("username")).isEqualTo("stores-dev");
|
||||
assertThat(propertySource.getSource().get("password")).isEqualTo("p455w0rd");
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,184 +0,0 @@
|
||||
/*
|
||||
* 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.configserver;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import io.kubernetes.client.openapi.ApiException;
|
||||
import io.kubernetes.client.openapi.apis.CoreV1Api;
|
||||
import io.kubernetes.client.openapi.models.V1ConfigMap;
|
||||
import io.kubernetes.client.openapi.models.V1ConfigMapBuilder;
|
||||
import io.kubernetes.client.openapi.models.V1ConfigMapList;
|
||||
import io.kubernetes.client.openapi.models.V1ObjectMetaBuilder;
|
||||
import io.kubernetes.client.openapi.models.V1Secret;
|
||||
import io.kubernetes.client.openapi.models.V1SecretBuilder;
|
||||
import io.kubernetes.client.openapi.models.V1SecretList;
|
||||
import io.kubernetes.client.openapi.models.V1SecretListBuilder;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.cloud.config.environment.Environment;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* Tests for verifying the behavior of the {@link KubernetesPropertySourceSupplier} beans
|
||||
* for ConfigMaps and Secrets created by the auto-configuration.
|
||||
*
|
||||
* @author Thomas Vitale
|
||||
*/
|
||||
class KubernetesPropertySourceSupplierTests {
|
||||
|
||||
private static final CoreV1Api coreApi = mock(CoreV1Api.class);
|
||||
|
||||
private static final V1ConfigMapList CONFIGMAP_DEFAULT_LIST = new V1ConfigMapList()
|
||||
.addItemsItem(buildConfigMap("gateway", "tdefault"));
|
||||
|
||||
private static final V1ConfigMapList CONFIGMAP_TEAM_A_LIST = new V1ConfigMapList()
|
||||
.addItemsItem(buildConfigMap("stores", "team-a"));
|
||||
|
||||
private static final V1ConfigMapList CONFIGMAP_TEAM_B_LIST = new V1ConfigMapList()
|
||||
.addItemsItem(buildConfigMap("orders", "team-b"));
|
||||
|
||||
private static final V1SecretList SECRET_DEFAULT_LIST = new V1SecretListBuilder()
|
||||
.addToItems(buildSecret("gateway", "default")).build();
|
||||
|
||||
private static final V1SecretList SECRET_TEAM_A_LIST = new V1SecretListBuilder()
|
||||
.addToItems(buildSecret("stores", "team-a")).build();
|
||||
|
||||
private static final V1SecretList SECRET_TEAM_B_LIST = new V1SecretListBuilder()
|
||||
.addToItems(buildSecret("orders", "team-b")).build();
|
||||
|
||||
@BeforeAll
|
||||
public static void before() throws ApiException {
|
||||
when(coreApi.listNamespacedConfigMap(eq("default"), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null),
|
||||
eq(null), eq(null), eq(null), eq(null))).thenReturn(CONFIGMAP_DEFAULT_LIST);
|
||||
when(coreApi.listNamespacedConfigMap(eq("team-a"), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null),
|
||||
eq(null), eq(null), eq(null), eq(null))).thenReturn(CONFIGMAP_TEAM_A_LIST);
|
||||
when(coreApi.listNamespacedConfigMap(eq("team-b"), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null),
|
||||
eq(null), eq(null), eq(null), eq(null))).thenReturn(CONFIGMAP_TEAM_B_LIST);
|
||||
|
||||
when(coreApi.listNamespacedSecret(eq("default"), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null),
|
||||
eq(null), eq(null), eq(null), eq(null))).thenReturn(SECRET_DEFAULT_LIST);
|
||||
when(coreApi.listNamespacedSecret(eq("team-a"), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null),
|
||||
eq(null), eq(null), eq(null), eq(null))).thenReturn(SECRET_TEAM_A_LIST);
|
||||
when(coreApi.listNamespacedSecret(eq("team-b"), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null),
|
||||
eq(null), eq(null), eq(null), eq(null))).thenReturn(SECRET_TEAM_B_LIST);
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenCurrentAndExtraNamespacesAddedThenAllConfigMapsAreIncluded() {
|
||||
KubernetesConfigServerProperties kubernetesConfigServerProperties = new KubernetesConfigServerProperties();
|
||||
kubernetesConfigServerProperties.setConfigMapNamespaces("default,team-a,team-b");
|
||||
|
||||
KubernetesPropertySourceSupplier kubernetesPropertySourceSupplier = new KubernetesConfigServerAutoConfiguration()
|
||||
.configMapPropertySourceSupplier(kubernetesConfigServerProperties);
|
||||
|
||||
KubernetesEnvironmentRepository environmentRepository = new KubernetesEnvironmentRepository(coreApi,
|
||||
Collections.singletonList(kubernetesPropertySourceSupplier), "default");
|
||||
|
||||
Environment environmentGateway = environmentRepository.findOne("gateway", "", "");
|
||||
assertThat(environmentGateway.getPropertySources().size()).isEqualTo(1);
|
||||
|
||||
Environment environmentStores = environmentRepository.findOne("stores", "", "");
|
||||
assertThat(environmentStores.getPropertySources().size()).isEqualTo(1);
|
||||
|
||||
Environment environmentOrders = environmentRepository.findOne("orders", "", "");
|
||||
assertThat(environmentOrders.getPropertySources().size()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenExtraNamespacesAddedThenConfigMapsInCurrentNamespaceAreNotIncluded() {
|
||||
KubernetesConfigServerProperties kubernetesConfigServerProperties = new KubernetesConfigServerProperties();
|
||||
kubernetesConfigServerProperties.setConfigMapNamespaces("team-a,team-b");
|
||||
|
||||
KubernetesPropertySourceSupplier kubernetesPropertySourceSupplier = new KubernetesConfigServerAutoConfiguration()
|
||||
.configMapPropertySourceSupplier(kubernetesConfigServerProperties);
|
||||
|
||||
KubernetesEnvironmentRepository environmentRepository = new KubernetesEnvironmentRepository(coreApi,
|
||||
Collections.singletonList(kubernetesPropertySourceSupplier), "default");
|
||||
|
||||
Environment environmentGateway = environmentRepository.findOne("gateway", "", "");
|
||||
assertThat(environmentGateway.getPropertySources().size()).isEqualTo(0);
|
||||
|
||||
Environment environmentStores = environmentRepository.findOne("stores", "", "");
|
||||
assertThat(environmentStores.getPropertySources().size()).isEqualTo(1);
|
||||
|
||||
Environment environmentOrders = environmentRepository.findOne("orders", "", "");
|
||||
assertThat(environmentOrders.getPropertySources().size()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenCurrentAndExtraNamespacesAddedThenAllSecretsAreIncluded() {
|
||||
KubernetesConfigServerProperties kubernetesConfigServerProperties = new KubernetesConfigServerProperties();
|
||||
kubernetesConfigServerProperties.setSecretsNamespaces("default,team-a,team-b");
|
||||
|
||||
KubernetesPropertySourceSupplier kubernetesPropertySourceSupplier = new KubernetesConfigServerAutoConfiguration()
|
||||
.secretsPropertySourceSupplier(kubernetesConfigServerProperties);
|
||||
|
||||
KubernetesEnvironmentRepository environmentRepository = new KubernetesEnvironmentRepository(coreApi,
|
||||
Collections.singletonList(kubernetesPropertySourceSupplier), "default");
|
||||
|
||||
Environment environmentGateway = environmentRepository.findOne("gateway", "", "");
|
||||
assertThat(environmentGateway.getPropertySources().size()).isEqualTo(1);
|
||||
|
||||
Environment environmentStores = environmentRepository.findOne("stores", "", "");
|
||||
assertThat(environmentStores.getPropertySources().size()).isEqualTo(1);
|
||||
|
||||
Environment environmentOrders = environmentRepository.findOne("orders", "", "");
|
||||
assertThat(environmentOrders.getPropertySources().size()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenExtraNamespacesAddedThenSecretsInCurrentNamespaceAreNotIncluded() {
|
||||
KubernetesConfigServerProperties kubernetesConfigServerProperties = new KubernetesConfigServerProperties();
|
||||
kubernetesConfigServerProperties.setSecretsNamespaces("team-a,team-b");
|
||||
|
||||
KubernetesPropertySourceSupplier kubernetesPropertySourceSupplier = new KubernetesConfigServerAutoConfiguration()
|
||||
.secretsPropertySourceSupplier(kubernetesConfigServerProperties);
|
||||
|
||||
KubernetesEnvironmentRepository environmentRepository = new KubernetesEnvironmentRepository(coreApi,
|
||||
Collections.singletonList(kubernetesPropertySourceSupplier), "default");
|
||||
|
||||
Environment environmentGateway = environmentRepository.findOne("gateway", "", "");
|
||||
assertThat(environmentGateway.getPropertySources().size()).isEqualTo(0);
|
||||
|
||||
Environment environmentStores = environmentRepository.findOne("stores", "", "");
|
||||
assertThat(environmentStores.getPropertySources().size()).isEqualTo(1);
|
||||
|
||||
Environment environmentOrders = environmentRepository.findOne("orders", "", "");
|
||||
assertThat(environmentOrders.getPropertySources().size()).isEqualTo(1);
|
||||
}
|
||||
|
||||
private static V1ConfigMap buildConfigMap(String name, String namespace) {
|
||||
return new V1ConfigMapBuilder()
|
||||
.withMetadata(new V1ObjectMetaBuilder().withName(name).withNamespace(namespace).withResourceVersion("1")
|
||||
.build())
|
||||
.addToData("application.yaml", "dummy:\n property:\n string: \"" + name + "\"\n").build();
|
||||
}
|
||||
|
||||
private static V1Secret buildSecret(String name, String namespace) {
|
||||
return new V1SecretBuilder()
|
||||
.withMetadata(new V1ObjectMetaBuilder().withName(name).withResourceVersion("0").withNamespace(namespace)
|
||||
.build())
|
||||
.addToData("password", "p455w0rd".getBytes()).addToData("username", "user".getBytes()).build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.cloud.kubernetes.configserver;
|
||||
|
||||
import org.springframework.cloud.config.server.environment.EnvironmentRepository;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
@Configuration
|
||||
class MockConfig {
|
||||
|
||||
@Bean
|
||||
@Profile("kubernetesdisabled")
|
||||
public EnvironmentRepository environmentRepository() {
|
||||
return mock(EnvironmentRepository.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* 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.configserver;
|
||||
|
||||
import com.github.tomakehurst.wiremock.WireMockServer;
|
||||
import com.github.tomakehurst.wiremock.client.WireMock;
|
||||
import io.kubernetes.client.openapi.ApiClient;
|
||||
import io.kubernetes.client.openapi.apis.CoreV1Api;
|
||||
import io.kubernetes.client.util.ClientBuilder;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class TestBootstrapConfig {
|
||||
|
||||
@Bean
|
||||
WireMockServer wireMockServer() {
|
||||
WireMockServer wireMockServer = new WireMockServer(options().dynamicPort());
|
||||
wireMockServer.start();
|
||||
WireMock.configureFor(wireMockServer.port());
|
||||
return wireMockServer;
|
||||
}
|
||||
|
||||
@Bean
|
||||
ApiClient apiClient(WireMockServer wireMockServer) {
|
||||
ApiClient apiClient = new ClientBuilder().setBasePath(wireMockServer.baseUrl()).build();
|
||||
apiClient.setDebugging(true);
|
||||
return apiClient;
|
||||
}
|
||||
|
||||
@Bean
|
||||
CoreV1Api coreApi(ApiClient apiClient) {
|
||||
return new CoreV1Api(apiClient);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
|
||||
org.springframework.cloud.kubernetes.configserver.TestBootstrapConfig
|
||||
@@ -1 +0,0 @@
|
||||
org.springframework.cloud.kubernetes.configserver.KubernetesConfigServerAutoConfiguration
|
||||
@@ -0,0 +1,117 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-kubernetes-controllers</artifactId>
|
||||
<version>3.0.4-SNAPSHOT</version>
|
||||
</parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-kubernetes-configuration-watcher</artifactId>
|
||||
<version>3.0.4-SNAPSHOT</version>
|
||||
<description>Spring Cloud parent pom, managing plugins and dependencies for Spring
|
||||
Cloud projects</description>
|
||||
<url>https://cloud.spring.io/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher</url>
|
||||
<inceptionYear>2017</inceptionYear>
|
||||
<organization>
|
||||
<name>Pivotal Software, Inc.</name>
|
||||
<url>https://www.spring.io</url>
|
||||
</organization>
|
||||
<licenses>
|
||||
<license>
|
||||
<name>Apache License, Version 2.0</name>
|
||||
<url>https://www.apache.org/licenses/LICENSE-2.0.txt</url>
|
||||
<distribution>repo</distribution>
|
||||
</license>
|
||||
</licenses>
|
||||
<developers>
|
||||
<developer>
|
||||
<id>dsyer</id>
|
||||
<name>Dave Syer</name>
|
||||
<email>dsyer at pivotal.io</email>
|
||||
<organization>Pivotal Software, Inc.</organization>
|
||||
<organizationUrl>https://www.spring.io</organizationUrl>
|
||||
<roles>
|
||||
<role>lead</role>
|
||||
</roles>
|
||||
</developer>
|
||||
<developer>
|
||||
<id>sgibb</id>
|
||||
<name>Spencer Gibb</name>
|
||||
<email>sgibb at pivotal.io</email>
|
||||
<organization>Pivotal Software, Inc.</organization>
|
||||
<organizationUrl>https://www.spring.io</organizationUrl>
|
||||
<roles>
|
||||
<role>lead</role>
|
||||
</roles>
|
||||
</developer>
|
||||
<developer>
|
||||
<id>mgrzejszczak</id>
|
||||
<name>Marcin Grzejszczak</name>
|
||||
<email>mgrzejszczak at pivotal.io</email>
|
||||
<organization>Pivotal Software, Inc.</organization>
|
||||
<organizationUrl>https://www.spring.io</organizationUrl>
|
||||
<roles>
|
||||
<role>developer</role>
|
||||
</roles>
|
||||
</developer>
|
||||
<developer>
|
||||
<id>rbaxter</id>
|
||||
<name>Ryan Baxter</name>
|
||||
<email>rbaxter at pivotal.io</email>
|
||||
<organization>Pivotal Software, Inc.</organization>
|
||||
<organizationUrl>https://www.spring.io</organizationUrl>
|
||||
<roles>
|
||||
<role>developer</role>
|
||||
</roles>
|
||||
</developer>
|
||||
<developer>
|
||||
<id>omaciaszeksharma</id>
|
||||
<name>Olga Maciaszek-Sharma</name>
|
||||
<email>omaciaszeksharma at pivotal.io</email>
|
||||
<organization>Pivotal Software, Inc.</organization>
|
||||
<organizationUrl>https://www.spring.io</organizationUrl>
|
||||
<roles>
|
||||
<role>developer</role>
|
||||
</roles>
|
||||
</developer>
|
||||
</developers>
|
||||
<scm>
|
||||
<connection>scm:git:git://github.com/spring-cloud/spring-cloud-kubernetes.git/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher</connection>
|
||||
<developerConnection>scm:git:ssh://git@github.com/spring-cloud/spring-cloud-kubernetes.git/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher</developerConnection>
|
||||
<url>https://github.com/spring-cloud/spring-cloud-kubernetes/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher</url>
|
||||
</scm>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-kubernetes-client-all</artifactId>
|
||||
<version>3.0.4-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
|
||||
<version>4.0.3-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-bus-kafka</artifactId>
|
||||
<version>4.0.3-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
<version>3.0.7</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-webflux</artifactId>
|
||||
<version>3.0.7</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -1,158 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-kubernetes-controllers</artifactId>
|
||||
<version>3.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>spring-cloud-kubernetes-configuration-watcher</artifactId>
|
||||
|
||||
<properties>
|
||||
<docker.registry.organization>springcloud</docker.registry.organization>
|
||||
<plexus-archiver.version>4.1.0</plexus-archiver.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-kubernetes-client-all</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-bus-kafka</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-webflux</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
<!-- In favor of mockito-inline -->
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.tomakehurst</groupId>
|
||||
<artifactId>wiremock-jre8-standalone</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.amqp</groupId>
|
||||
<artifactId>spring-rabbit-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-inline</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-kubernetes-test-support</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<image>
|
||||
<name>${env.IMAGE}</name>
|
||||
</image>
|
||||
<goal>build-image</goal>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>build-image</id>
|
||||
<configuration>
|
||||
<skip>${skip.build.image}</skip>
|
||||
</configuration>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>build-image</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>repackage</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>dockerpush</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>com.spotify</groupId>
|
||||
<artifactId>dockerfile-maven-plugin</artifactId>
|
||||
<version>1.4.12</version>
|
||||
<configuration>
|
||||
<repository>${docker.registry.organization}/${artifactId}</repository>
|
||||
<tag>${project.version}</tag>
|
||||
<username>${env.DOCKER_HUB_USERNAME}</username>
|
||||
<password>${env.DOCKER_HUB_PASSWORD}</password>
|
||||
<build>
|
||||
<noCache>true</noCache>
|
||||
</build>
|
||||
</configuration>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.codehaus.plexus</groupId>
|
||||
<artifactId>plexus-archiver</artifactId>
|
||||
<version>${plexus-archiver.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>imagename</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>!env.IMAGE</name>
|
||||
</property>
|
||||
</activation>
|
||||
<properties>
|
||||
<env.IMAGE>springcloud/${project.artifactId}:${project.version}</env.IMAGE>
|
||||
</properties>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
|
||||
</project>
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.cloud.kubernetes.configuration.watcher;
|
||||
|
||||
import io.kubernetes.client.common.KubernetesObject;
|
||||
import io.kubernetes.client.openapi.apis.CoreV1Api;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigMapPropertySourceLocator;
|
||||
import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider;
|
||||
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties;
|
||||
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
* @author Kris Iyer
|
||||
*/
|
||||
final class BusEventBasedConfigMapWatcherChangeDetector extends ConfigMapWatcherChangeDetector {
|
||||
|
||||
private final BusRefreshTrigger busRefreshTrigger;
|
||||
|
||||
BusEventBasedConfigMapWatcherChangeDetector(CoreV1Api coreV1Api, ConfigurableEnvironment environment,
|
||||
ConfigReloadProperties properties, ConfigurationUpdateStrategy strategy,
|
||||
KubernetesClientConfigMapPropertySourceLocator propertySourceLocator,
|
||||
KubernetesNamespaceProvider kubernetesNamespaceProvider,
|
||||
ConfigurationWatcherConfigurationProperties k8SConfigurationProperties,
|
||||
ThreadPoolTaskExecutor threadPoolTaskExecutor, BusRefreshTrigger busRefreshTrigger) {
|
||||
super(coreV1Api, environment, properties, strategy, propertySourceLocator, kubernetesNamespaceProvider,
|
||||
k8SConfigurationProperties, threadPoolTaskExecutor);
|
||||
this.busRefreshTrigger = busRefreshTrigger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> triggerRefresh(KubernetesObject configMap, String appName) {
|
||||
return busRefreshTrigger.triggerRefresh(configMap, appName);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.cloud.kubernetes.configuration.watcher;
|
||||
|
||||
import io.kubernetes.client.common.KubernetesObject;
|
||||
import io.kubernetes.client.openapi.apis.CoreV1Api;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.cloud.kubernetes.client.config.KubernetesClientSecretsPropertySourceLocator;
|
||||
import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider;
|
||||
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties;
|
||||
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
* @author Kris Iyer
|
||||
*/
|
||||
final class BusEventBasedSecretsWatcherChangeDetector extends SecretsWatcherChangeDetector {
|
||||
|
||||
private final BusRefreshTrigger busRefreshTrigger;
|
||||
|
||||
BusEventBasedSecretsWatcherChangeDetector(CoreV1Api coreV1Api, ConfigurableEnvironment environment,
|
||||
ConfigReloadProperties properties, ConfigurationUpdateStrategy strategy,
|
||||
KubernetesClientSecretsPropertySourceLocator propertySourceLocator,
|
||||
KubernetesNamespaceProvider kubernetesNamespaceProvider,
|
||||
ConfigurationWatcherConfigurationProperties k8SConfigurationProperties,
|
||||
ThreadPoolTaskExecutor threadPoolTaskExecutor, BusRefreshTrigger busRefreshTrigger) {
|
||||
super(coreV1Api, environment, properties, strategy, propertySourceLocator, kubernetesNamespaceProvider,
|
||||
k8SConfigurationProperties, threadPoolTaskExecutor);
|
||||
this.busRefreshTrigger = busRefreshTrigger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> triggerRefresh(KubernetesObject secret, String appName) {
|
||||
return busRefreshTrigger.triggerRefresh(secret, appName);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.cloud.kubernetes.configuration.watcher;
|
||||
|
||||
import io.kubernetes.client.common.KubernetesObject;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.cloud.bus.event.PathDestinationFactory;
|
||||
import org.springframework.cloud.bus.event.RefreshRemoteApplicationEvent;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
|
||||
/**
|
||||
* An event publisher for an 'event bus' type of application.
|
||||
*
|
||||
* @author wind57
|
||||
*/
|
||||
final class BusRefreshTrigger implements RefreshTrigger {
|
||||
|
||||
private final ApplicationEventPublisher applicationEventPublisher;
|
||||
|
||||
private final String busId;
|
||||
|
||||
BusRefreshTrigger(ApplicationEventPublisher applicationEventPublisher, String busId) {
|
||||
this.applicationEventPublisher = applicationEventPublisher;
|
||||
this.busId = busId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> triggerRefresh(KubernetesObject configMap, String appName) {
|
||||
applicationEventPublisher.publishEvent(new RefreshRemoteApplicationEvent(configMap, busId,
|
||||
new PathDestinationFactory().getDestination(appName)));
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2020 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.configuration.watcher;
|
||||
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
import io.kubernetes.client.common.KubernetesObject;
|
||||
import io.kubernetes.client.openapi.apis.CoreV1Api;
|
||||
|
||||
import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigMapPropertySourceLocator;
|
||||
import org.springframework.cloud.kubernetes.client.config.reload.KubernetesClientEventBasedConfigMapChangeDetector;
|
||||
import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider;
|
||||
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties;
|
||||
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
|
||||
import static org.springframework.cloud.kubernetes.configuration.watcher.ConfigurationWatcherConfigurationProperties.CONFIG_MAP_APPS_ANNOTATION;
|
||||
import static org.springframework.cloud.kubernetes.configuration.watcher.ConfigurationWatcherConfigurationProperties.CONFIG_MAP_LABEL;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
* @author Kris Iyer
|
||||
*/
|
||||
abstract sealed class ConfigMapWatcherChangeDetector extends KubernetesClientEventBasedConfigMapChangeDetector
|
||||
implements
|
||||
RefreshTrigger permits BusEventBasedConfigMapWatcherChangeDetector, HttpBasedConfigMapWatchChangeDetector {
|
||||
|
||||
private final ScheduledExecutorService executorService;
|
||||
|
||||
private final long refreshDelay;
|
||||
|
||||
ConfigMapWatcherChangeDetector(CoreV1Api coreV1Api, ConfigurableEnvironment environment,
|
||||
ConfigReloadProperties properties, ConfigurationUpdateStrategy strategy,
|
||||
KubernetesClientConfigMapPropertySourceLocator propertySourceLocator,
|
||||
KubernetesNamespaceProvider kubernetesNamespaceProvider,
|
||||
ConfigurationWatcherConfigurationProperties k8SConfigurationProperties,
|
||||
ThreadPoolTaskExecutor threadPoolTaskExecutor) {
|
||||
super(coreV1Api, environment, properties, strategy, propertySourceLocator, kubernetesNamespaceProvider);
|
||||
this.executorService = Executors.newScheduledThreadPool(k8SConfigurationProperties.getThreadPoolSize(),
|
||||
threadPoolTaskExecutor);
|
||||
this.refreshDelay = k8SConfigurationProperties.getRefreshDelay().toMillis();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void onEvent(KubernetesObject configMap) {
|
||||
// this::refreshTrigger is coming from BusEventBasedConfigMapWatcherChangeDetector
|
||||
WatcherUtil.onEvent(configMap, CONFIG_MAP_LABEL, CONFIG_MAP_APPS_ANNOTATION, refreshDelay, executorService,
|
||||
"config-map", this::triggerRefresh);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2023 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.configuration.watcher;
|
||||
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.log.LogAccessor;
|
||||
|
||||
/**
|
||||
* @author wind57
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnProperty(value = "spring.cloud.kubernetes.reload.enabled", havingValue = "false", matchIfMissing = true)
|
||||
class ConfigUpdateStrategyAutoConfiguration {
|
||||
|
||||
private static final LogAccessor LOG = new LogAccessor(
|
||||
LogFactory.getLog(ConfigUpdateStrategyAutoConfiguration.class));
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
ConfigurationUpdateStrategy noopConfigurationUpdateStrategy() {
|
||||
LOG.debug(() -> "creating NOOP strategy because reload is disabled");
|
||||
return ConfigurationUpdateStrategy.NOOP;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2019 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.configuration.watcher;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.amqp.RabbitHealthContributorAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.cloud.function.context.config.ContextFunctionCatalogAutoConfiguration;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@SpringBootApplication(
|
||||
exclude = { ContextFunctionCatalogAutoConfiguration.class, RabbitHealthContributorAutoConfiguration.class })
|
||||
public class ConfigurationWatcherApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
new SpringApplicationBuilder(ConfigurationWatcherApplication.class).run(args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,181 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2020 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.configuration.watcher;
|
||||
|
||||
import io.kubernetes.client.openapi.apis.CoreV1Api;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.amqp.RabbitHealthContributorAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.bus.BusProperties;
|
||||
import org.springframework.cloud.function.context.config.ContextFunctionCatalogAutoConfiguration;
|
||||
import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigMapPropertySourceLocator;
|
||||
import org.springframework.cloud.kubernetes.client.config.KubernetesClientSecretsPropertySourceLocator;
|
||||
import org.springframework.cloud.kubernetes.client.discovery.reactive.KubernetesInformerReactiveDiscoveryClient;
|
||||
import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider;
|
||||
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties;
|
||||
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.core.env.AbstractEnvironment;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
* @author Kris Iyer
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableConfigurationProperties({ ConfigurationWatcherConfigurationProperties.class })
|
||||
@Import({ ConfigurationWatcherAutoConfiguration.RefreshTriggerConfiguration.class })
|
||||
public class ConfigurationWatcherAutoConfiguration {
|
||||
|
||||
private static final String AMQP = "bus-amqp";
|
||||
|
||||
private static final String KAFKA = "bus-kafka";
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public WebClient webClient(WebClient.Builder webClientBuilder) {
|
||||
return webClientBuilder.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(ConfigMapWatcherChangeDetector.class)
|
||||
@ConditionalOnBean(KubernetesClientConfigMapPropertySourceLocator.class)
|
||||
public ConfigMapWatcherChangeDetector httpBasedConfigMapWatchChangeDetector(AbstractEnvironment environment,
|
||||
CoreV1Api coreV1Api, KubernetesClientConfigMapPropertySourceLocator configMapPropertySourceLocator,
|
||||
ConfigReloadProperties properties, ConfigurationUpdateStrategy strategy,
|
||||
ConfigurationWatcherConfigurationProperties k8SConfigurationProperties,
|
||||
KubernetesNamespaceProvider namespaceProvider, ThreadPoolTaskExecutor threadFactory,
|
||||
HttpRefreshTrigger httpRefreshTrigger) {
|
||||
return new HttpBasedConfigMapWatchChangeDetector(coreV1Api, environment, properties, strategy,
|
||||
configMapPropertySourceLocator, namespaceProvider, k8SConfigurationProperties, threadFactory,
|
||||
httpRefreshTrigger);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnBean(KubernetesClientSecretsPropertySourceLocator.class)
|
||||
public SecretsWatcherChangeDetector httpBasedSecretsWatchChangeDetector(AbstractEnvironment environment,
|
||||
CoreV1Api coreV1Api, KubernetesClientSecretsPropertySourceLocator secretsPropertySourceLocator,
|
||||
KubernetesNamespaceProvider namespaceProvider, ConfigReloadProperties properties,
|
||||
ConfigurationUpdateStrategy strategy,
|
||||
ConfigurationWatcherConfigurationProperties k8SConfigurationProperties,
|
||||
ThreadPoolTaskExecutor threadFactory, HttpRefreshTrigger httpRefreshTrigger) {
|
||||
return new HttpBasedSecretsWatchChangeDetector(coreV1Api, environment, properties, strategy,
|
||||
secretsPropertySourceLocator, namespaceProvider, k8SConfigurationProperties, threadFactory,
|
||||
httpRefreshTrigger);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Profile(AMQP)
|
||||
@Import({ ContextFunctionCatalogAutoConfiguration.class, RabbitHealthContributorAutoConfiguration.class,
|
||||
RefreshTriggerConfiguration.class })
|
||||
static class BusRabbitConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(ConfigMapWatcherChangeDetector.class)
|
||||
@ConditionalOnBean(KubernetesClientConfigMapPropertySourceLocator.class)
|
||||
public ConfigMapWatcherChangeDetector busConfigMapChangeWatcher(AbstractEnvironment environment,
|
||||
CoreV1Api coreV1Api, KubernetesClientConfigMapPropertySourceLocator configMapPropertySourceLocator,
|
||||
KubernetesNamespaceProvider kubernetesNamespaceProvider, ConfigReloadProperties properties,
|
||||
ConfigurationUpdateStrategy strategy,
|
||||
ConfigurationWatcherConfigurationProperties k8SConfigurationProperties,
|
||||
ThreadPoolTaskExecutor threadFactory, BusRefreshTrigger busRefreshTrigger) {
|
||||
return new BusEventBasedConfigMapWatcherChangeDetector(coreV1Api, environment, properties, strategy,
|
||||
configMapPropertySourceLocator, kubernetesNamespaceProvider, k8SConfigurationProperties,
|
||||
threadFactory, busRefreshTrigger);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(SecretsWatcherChangeDetector.class)
|
||||
@ConditionalOnBean(KubernetesClientSecretsPropertySourceLocator.class)
|
||||
public SecretsWatcherChangeDetector busSecretsChangeWatcher(AbstractEnvironment environment,
|
||||
CoreV1Api coreV1Api, KubernetesClientSecretsPropertySourceLocator secretsPropertySourceLocator,
|
||||
ConfigReloadProperties properties, KubernetesNamespaceProvider kubernetesNamespaceProvider,
|
||||
ConfigurationUpdateStrategy strategy,
|
||||
ConfigurationWatcherConfigurationProperties k8SConfigurationProperties,
|
||||
ThreadPoolTaskExecutor threadFactory, BusRefreshTrigger busRefreshTrigger) {
|
||||
return new BusEventBasedSecretsWatcherChangeDetector(coreV1Api, environment, properties, strategy,
|
||||
secretsPropertySourceLocator, kubernetesNamespaceProvider, k8SConfigurationProperties,
|
||||
threadFactory, busRefreshTrigger);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Profile(KAFKA)
|
||||
@Import({ ContextFunctionCatalogAutoConfiguration.class, RefreshTriggerConfiguration.class })
|
||||
static class BusKafkaConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(ConfigMapWatcherChangeDetector.class)
|
||||
@ConditionalOnBean(KubernetesClientConfigMapPropertySourceLocator.class)
|
||||
public ConfigMapWatcherChangeDetector busConfigMapChangeWatcher(AbstractEnvironment environment,
|
||||
CoreV1Api coreV1Api, KubernetesClientConfigMapPropertySourceLocator configMapPropertySourceLocator,
|
||||
ConfigReloadProperties properties, KubernetesNamespaceProvider namespaceProvider,
|
||||
ConfigurationUpdateStrategy strategy,
|
||||
ConfigurationWatcherConfigurationProperties k8SConfigurationProperties,
|
||||
ThreadPoolTaskExecutor threadFactory, BusRefreshTrigger busRefreshTrigger) {
|
||||
return new BusEventBasedConfigMapWatcherChangeDetector(coreV1Api, environment, properties, strategy,
|
||||
configMapPropertySourceLocator, namespaceProvider, k8SConfigurationProperties, threadFactory,
|
||||
busRefreshTrigger);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(SecretsWatcherChangeDetector.class)
|
||||
@ConditionalOnBean(KubernetesClientSecretsPropertySourceLocator.class)
|
||||
public SecretsWatcherChangeDetector busSecretsChangeWatcher(AbstractEnvironment environment,
|
||||
CoreV1Api coreV1Api, KubernetesClientSecretsPropertySourceLocator secretsPropertySourceLocator,
|
||||
ConfigReloadProperties properties, ConfigurationUpdateStrategy strategy,
|
||||
ConfigurationWatcherConfigurationProperties k8SConfigurationProperties,
|
||||
ThreadPoolTaskExecutor threadFactory, KubernetesNamespaceProvider namespaceProvider,
|
||||
BusRefreshTrigger busRefreshTrigger) {
|
||||
return new BusEventBasedSecretsWatcherChangeDetector(coreV1Api, environment, properties, strategy,
|
||||
secretsPropertySourceLocator, namespaceProvider, k8SConfigurationProperties, threadFactory,
|
||||
busRefreshTrigger);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@AutoConfiguration
|
||||
static class RefreshTriggerConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@Profile({ AMQP, KAFKA })
|
||||
public BusRefreshTrigger busRefreshTrigger(ApplicationEventPublisher applicationEventPublisher,
|
||||
BusProperties busProperties) {
|
||||
return new BusRefreshTrigger(applicationEventPublisher, busProperties.getId());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public HttpRefreshTrigger httpRefreshTrigger(KubernetesInformerReactiveDiscoveryClient client,
|
||||
ConfigurationWatcherConfigurationProperties properties, WebClient webClient) {
|
||||
return new HttpRefreshTrigger(client, properties, webClient);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2020 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.configuration.watcher;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.convert.DurationUnit;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@ConfigurationProperties("spring.cloud.kubernetes.configuration.watcher")
|
||||
public class ConfigurationWatcherConfigurationProperties {
|
||||
|
||||
/**
|
||||
* label to enable refresh/restart when using configmaps.
|
||||
*/
|
||||
public static final String CONFIG_MAP_LABEL = "spring.cloud.kubernetes.config";
|
||||
|
||||
/**
|
||||
* label to enable refresh/restart when using secrets.
|
||||
*/
|
||||
public static final String SECRET_LABEL = "spring.cloud.kubernetes.secret";
|
||||
|
||||
/**
|
||||
* annotation name to enable refresh/restart for specific apps when using configmaps.
|
||||
*/
|
||||
public static final String CONFIG_MAP_APPS_ANNOTATION = "spring.cloud.kubernetes.configmap.apps";
|
||||
|
||||
/**
|
||||
* annotation name to enable refresh/restart for specific apps when using secrets.
|
||||
*/
|
||||
public static final String SECRET_APPS_ANNOTATION = "spring.cloud.kubernetes.secret.apps";
|
||||
|
||||
/**
|
||||
* Annotation key for actuator port and path.
|
||||
*/
|
||||
public static final String ANNOTATION_KEY = "boot.spring.io/actuator";
|
||||
|
||||
/**
|
||||
* Amount of time to delay the posting of the event to allow the app volume to update
|
||||
* data.
|
||||
*/
|
||||
@DurationUnit(ChronoUnit.MILLIS)
|
||||
private Duration refreshDelay = Duration.ofMillis(120000);
|
||||
|
||||
private int threadPoolSize = 1;
|
||||
|
||||
private String actuatorPath = "/actuator";
|
||||
|
||||
private Integer actuatorPort = -1;
|
||||
|
||||
public String getActuatorPath() {
|
||||
return actuatorPath;
|
||||
}
|
||||
|
||||
public void setActuatorPath(String actuatorPath) {
|
||||
String normalizedPath = actuatorPath;
|
||||
if (!normalizedPath.startsWith("/")) {
|
||||
normalizedPath = "/" + normalizedPath;
|
||||
}
|
||||
if (normalizedPath.endsWith("/")) {
|
||||
normalizedPath = normalizedPath.substring(0, normalizedPath.length() - 1);
|
||||
}
|
||||
this.actuatorPath = normalizedPath;
|
||||
}
|
||||
|
||||
public Integer getActuatorPort() {
|
||||
return actuatorPort;
|
||||
}
|
||||
|
||||
public void setActuatorPort(Integer actuatorPort) {
|
||||
this.actuatorPort = actuatorPort;
|
||||
}
|
||||
|
||||
public Duration getRefreshDelay() {
|
||||
return refreshDelay;
|
||||
}
|
||||
|
||||
public void setRefreshDelay(Duration refreshDelay) {
|
||||
this.refreshDelay = refreshDelay;
|
||||
}
|
||||
|
||||
public int getThreadPoolSize() {
|
||||
return threadPoolSize;
|
||||
}
|
||||
|
||||
public void setThreadPoolSize(int threadPoolSize) {
|
||||
this.threadPoolSize = threadPoolSize;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2020 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.configuration.watcher;
|
||||
|
||||
import io.kubernetes.client.common.KubernetesObject;
|
||||
import io.kubernetes.client.openapi.apis.CoreV1Api;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigMapPropertySourceLocator;
|
||||
import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider;
|
||||
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties;
|
||||
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
* @author Kris Iyer
|
||||
*/
|
||||
final class HttpBasedConfigMapWatchChangeDetector extends ConfigMapWatcherChangeDetector {
|
||||
|
||||
private final HttpRefreshTrigger httpRefreshTrigger;
|
||||
|
||||
HttpBasedConfigMapWatchChangeDetector(CoreV1Api coreV1Api, ConfigurableEnvironment environment,
|
||||
ConfigReloadProperties properties, ConfigurationUpdateStrategy strategy,
|
||||
KubernetesClientConfigMapPropertySourceLocator propertySourceLocator,
|
||||
KubernetesNamespaceProvider kubernetesNamespaceProvider,
|
||||
ConfigurationWatcherConfigurationProperties k8SConfigurationProperties,
|
||||
ThreadPoolTaskExecutor threadPoolTaskExecutor, HttpRefreshTrigger httpRefreshTrigger) {
|
||||
super(coreV1Api, environment, properties, strategy, propertySourceLocator, kubernetesNamespaceProvider,
|
||||
k8SConfigurationProperties, threadPoolTaskExecutor);
|
||||
|
||||
this.httpRefreshTrigger = httpRefreshTrigger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> triggerRefresh(KubernetesObject configMap, String appName) {
|
||||
return httpRefreshTrigger.triggerRefresh(configMap, appName);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2020 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.configuration.watcher;
|
||||
|
||||
import io.kubernetes.client.common.KubernetesObject;
|
||||
import io.kubernetes.client.openapi.apis.CoreV1Api;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.cloud.kubernetes.client.config.KubernetesClientSecretsPropertySourceLocator;
|
||||
import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider;
|
||||
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties;
|
||||
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
* @author Kris Iyer
|
||||
*/
|
||||
final class HttpBasedSecretsWatchChangeDetector extends SecretsWatcherChangeDetector {
|
||||
|
||||
private final HttpRefreshTrigger httpRefreshTrigger;
|
||||
|
||||
HttpBasedSecretsWatchChangeDetector(CoreV1Api coreV1Api, ConfigurableEnvironment environment,
|
||||
ConfigReloadProperties properties, ConfigurationUpdateStrategy strategy,
|
||||
KubernetesClientSecretsPropertySourceLocator propertySourceLocator,
|
||||
KubernetesNamespaceProvider kubernetesNamespaceProvider,
|
||||
ConfigurationWatcherConfigurationProperties k8SConfigurationProperties,
|
||||
ThreadPoolTaskExecutor threadPoolTaskExecutor, HttpRefreshTrigger httpRefreshTrigger) {
|
||||
super(coreV1Api, environment, properties, strategy, propertySourceLocator, kubernetesNamespaceProvider,
|
||||
k8SConfigurationProperties, threadPoolTaskExecutor);
|
||||
|
||||
this.httpRefreshTrigger = httpRefreshTrigger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> triggerRefresh(KubernetesObject secret, String appName) {
|
||||
return httpRefreshTrigger.triggerRefresh(secret, appName);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.cloud.kubernetes.configuration.watcher;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import io.kubernetes.client.common.KubernetesObject;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.kubernetes.client.config.reload.KubernetesClientEventBasedSecretsChangeDetector;
|
||||
import org.springframework.cloud.kubernetes.client.discovery.reactive.KubernetesInformerReactiveDiscoveryClient;
|
||||
import org.springframework.core.log.LogAccessor;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
/**
|
||||
* @author wind57
|
||||
*/
|
||||
final class HttpRefreshTrigger implements RefreshTrigger {
|
||||
|
||||
private static final LogAccessor LOG = new LogAccessor(
|
||||
LogFactory.getLog(KubernetesClientEventBasedSecretsChangeDetector.class));
|
||||
|
||||
private final KubernetesInformerReactiveDiscoveryClient kubernetesReactiveDiscoveryClient;
|
||||
|
||||
private final ConfigurationWatcherConfigurationProperties k8SConfigurationProperties;
|
||||
|
||||
private final WebClient webClient;
|
||||
|
||||
HttpRefreshTrigger(KubernetesInformerReactiveDiscoveryClient kubernetesReactiveDiscoveryClient,
|
||||
ConfigurationWatcherConfigurationProperties k8SConfigurationProperties, WebClient webClient) {
|
||||
this.kubernetesReactiveDiscoveryClient = kubernetesReactiveDiscoveryClient;
|
||||
this.k8SConfigurationProperties = k8SConfigurationProperties;
|
||||
this.webClient = webClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> triggerRefresh(KubernetesObject kubernetesObject, String appName) {
|
||||
|
||||
return kubernetesReactiveDiscoveryClient.getInstances(appName).flatMap(si -> {
|
||||
URI actuatorUri = getActuatorUri(si, k8SConfigurationProperties.getActuatorPath(),
|
||||
k8SConfigurationProperties.getActuatorPort());
|
||||
LOG.debug(() -> "Sending refresh request for " + appName + " to URI " + actuatorUri);
|
||||
return webClient.post().uri(actuatorUri).retrieve().toBodilessEntity()
|
||||
.doOnSuccess(onSuccess(appName, actuatorUri)).doOnError(onError(appName));
|
||||
}).then();
|
||||
}
|
||||
|
||||
private Consumer<ResponseEntity<Void>> onSuccess(String name, URI actuatorUri) {
|
||||
return re -> LOG.debug(() -> "Refresh sent to " + name + " at URI address " + actuatorUri + " returned a "
|
||||
+ re.getStatusCode());
|
||||
}
|
||||
|
||||
private Consumer<Throwable> onError(String name) {
|
||||
return t -> LOG.warn(t, () -> "Refresh sent to " + name + " failed");
|
||||
}
|
||||
|
||||
private URI getActuatorUri(ServiceInstance si, String actuatorPath, int actuatorPort) {
|
||||
String metadataUri = si.getMetadata().getOrDefault(ConfigurationWatcherConfigurationProperties.ANNOTATION_KEY,
|
||||
"");
|
||||
LOG.debug(() -> "Metadata actuator uri is: " + metadataUri);
|
||||
|
||||
UriComponentsBuilder actuatorUriBuilder = UriComponentsBuilder.newInstance().scheme(si.getScheme())
|
||||
.host(si.getHost());
|
||||
|
||||
if (StringUtils.hasText(metadataUri)) {
|
||||
LOG.debug(() -> "Found actuator URI in service instance metadata");
|
||||
setActuatorUriFromAnnotation(actuatorUriBuilder, metadataUri);
|
||||
}
|
||||
else {
|
||||
int port = actuatorPort < 0 ? si.getPort() : actuatorPort;
|
||||
actuatorUriBuilder = actuatorUriBuilder.path(actuatorPath + "/refresh").port(port);
|
||||
}
|
||||
|
||||
return actuatorUriBuilder.build().toUri();
|
||||
}
|
||||
|
||||
private void setActuatorUriFromAnnotation(UriComponentsBuilder actuatorUriBuilder, String metadataUri) {
|
||||
URI annotationUri = URI.create(metadataUri);
|
||||
actuatorUriBuilder.path(annotationUri.getPath() + "/refresh");
|
||||
|
||||
// The URI may not contain a host so if that is the case the port in the URI will
|
||||
// be -1. The authority of the URI will be :<port> for example :9090, we just need
|
||||
// the 9090 in this case
|
||||
if (annotationUri.getPort() < 0) {
|
||||
if (annotationUri.getAuthority() != null) {
|
||||
actuatorUriBuilder.port(annotationUri.getAuthority().replaceFirst(":", ""));
|
||||
}
|
||||
}
|
||||
else {
|
||||
actuatorUriBuilder.port(annotationUri.getPort());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.cloud.kubernetes.configuration.watcher;
|
||||
|
||||
import io.kubernetes.client.common.KubernetesObject;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* Defines the refresh trigger contract.
|
||||
*
|
||||
* @author wind57
|
||||
*/
|
||||
sealed interface RefreshTrigger permits BusRefreshTrigger, ConfigMapWatcherChangeDetector, HttpRefreshTrigger, SecretsWatcherChangeDetector {
|
||||
|
||||
/**
|
||||
* @param kubernetesObject either a config-map or secret at the moment.
|
||||
* @param appName which is not necessarily equal to
|
||||
* kubernetesObject.getMetadata().getName()
|
||||
*/
|
||||
Mono<Void> triggerRefresh(KubernetesObject kubernetesObject, String appName);
|
||||
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2020 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.configuration.watcher;
|
||||
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
import io.kubernetes.client.common.KubernetesObject;
|
||||
import io.kubernetes.client.openapi.apis.CoreV1Api;
|
||||
|
||||
import org.springframework.cloud.kubernetes.client.config.KubernetesClientSecretsPropertySourceLocator;
|
||||
import org.springframework.cloud.kubernetes.client.config.reload.KubernetesClientEventBasedSecretsChangeDetector;
|
||||
import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider;
|
||||
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties;
|
||||
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
|
||||
import static org.springframework.cloud.kubernetes.configuration.watcher.ConfigurationWatcherConfigurationProperties.SECRET_APPS_ANNOTATION;
|
||||
import static org.springframework.cloud.kubernetes.configuration.watcher.ConfigurationWatcherConfigurationProperties.SECRET_LABEL;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
* @author Kris Iyer
|
||||
*/
|
||||
abstract sealed class SecretsWatcherChangeDetector extends KubernetesClientEventBasedSecretsChangeDetector implements
|
||||
RefreshTrigger permits BusEventBasedSecretsWatcherChangeDetector, HttpBasedSecretsWatchChangeDetector {
|
||||
|
||||
private final ScheduledExecutorService executorService;
|
||||
|
||||
private final long refreshDelay;
|
||||
|
||||
SecretsWatcherChangeDetector(CoreV1Api coreV1Api, ConfigurableEnvironment environment,
|
||||
ConfigReloadProperties properties, ConfigurationUpdateStrategy strategy,
|
||||
KubernetesClientSecretsPropertySourceLocator propertySourceLocator,
|
||||
KubernetesNamespaceProvider kubernetesNamespaceProvider,
|
||||
ConfigurationWatcherConfigurationProperties k8SConfigurationProperties,
|
||||
ThreadPoolTaskExecutor threadPoolTaskExecutor) {
|
||||
super(coreV1Api, environment, properties, strategy, propertySourceLocator, kubernetesNamespaceProvider);
|
||||
this.executorService = Executors.newScheduledThreadPool(k8SConfigurationProperties.getThreadPoolSize(),
|
||||
threadPoolTaskExecutor);
|
||||
this.refreshDelay = k8SConfigurationProperties.getRefreshDelay().toMillis();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void onEvent(KubernetesObject secret) {
|
||||
// this::refreshTrigger is coming from BusEventBasedSecretsWatcherChangeDetector
|
||||
WatcherUtil.onEvent(secret, SECRET_LABEL, SECRET_APPS_ANNOTATION, refreshDelay, executorService, "secret",
|
||||
this::triggerRefresh);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2020 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.configuration.watcher;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import io.kubernetes.client.common.KubernetesObject;
|
||||
import io.kubernetes.client.openapi.models.V1ObjectMeta;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.log.LogAccessor;
|
||||
|
||||
/**
|
||||
* A common place where 'onEvent' code delegates to.
|
||||
*
|
||||
* @author wind57
|
||||
*/
|
||||
final class WatcherUtil {
|
||||
|
||||
private static final LogAccessor LOG = new LogAccessor(LogFactory.getLog(WatcherUtil.class));
|
||||
|
||||
private WatcherUtil() {
|
||||
}
|
||||
|
||||
static void onEvent(KubernetesObject kubernetesObject, String label, String annotationName, long refreshDelay,
|
||||
ScheduledExecutorService executorService, String type,
|
||||
BiFunction<KubernetesObject, String, Mono<Void>> triggerRefresh) {
|
||||
|
||||
String name = kubernetesObject.getMetadata().getName();
|
||||
boolean isSpringCloudKubernetes = isSpringCloudKubernetes(kubernetesObject, label);
|
||||
|
||||
if (isSpringCloudKubernetes) {
|
||||
|
||||
Set<String> apps = apps(kubernetesObject, annotationName);
|
||||
|
||||
if (apps.isEmpty()) {
|
||||
apps.add(name);
|
||||
}
|
||||
|
||||
LOG.info(() -> "will schedule remote refresh based on apps : " + apps);
|
||||
apps.forEach(appName -> schedule(type, appName, refreshDelay, executorService, triggerRefresh,
|
||||
kubernetesObject));
|
||||
|
||||
}
|
||||
else {
|
||||
LOG.debug(() -> "Not publishing event." + type + ": " + name + " does not contain the label " + label);
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isSpringCloudKubernetes(KubernetesObject kubernetesObject, String label) {
|
||||
if (kubernetesObject.getMetadata() == null) {
|
||||
return false;
|
||||
}
|
||||
return Boolean.parseBoolean(labels(kubernetesObject).getOrDefault(label, "false"));
|
||||
}
|
||||
|
||||
static Set<String> apps(KubernetesObject kubernetesObject, String annotationName) {
|
||||
|
||||
// mutable on purpose
|
||||
Set<String> apps = new HashSet<>(1);
|
||||
Map<String, String> annotations = annotations(kubernetesObject);
|
||||
|
||||
if (annotations.isEmpty()) {
|
||||
LOG.debug(() -> annotationName + " not present (empty data)");
|
||||
return apps;
|
||||
}
|
||||
|
||||
String appsValue = annotations.get(annotationName);
|
||||
|
||||
if (appsValue == null) {
|
||||
LOG.debug(() -> annotationName + " not present (missing in annotations)");
|
||||
return apps;
|
||||
}
|
||||
|
||||
if (appsValue.isBlank()) {
|
||||
LOG.debug(() -> appsValue + " not present (blanks only)");
|
||||
return apps;
|
||||
}
|
||||
|
||||
return Arrays.stream(appsValue.split(",")).map(String::trim).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
static Map<String, String> labels(KubernetesObject kubernetesObject) {
|
||||
return Optional.ofNullable(kubernetesObject.getMetadata()).map(V1ObjectMeta::getLabels).orElse(Map.of());
|
||||
}
|
||||
|
||||
static Map<String, String> annotations(KubernetesObject kubernetesObject) {
|
||||
return Optional.ofNullable(kubernetesObject.getMetadata()).map(V1ObjectMeta::getAnnotations).orElse(Map.of());
|
||||
}
|
||||
|
||||
private static void schedule(String type, String appName, long refreshDelay,
|
||||
ScheduledExecutorService executorService, BiFunction<KubernetesObject, String, Mono<Void>> triggerRefresh,
|
||||
KubernetesObject kubernetesObject) {
|
||||
LOG.debug(() -> "Scheduling remote refresh event to be published for " + type + ": with appName : " + appName
|
||||
+ " to be published in " + refreshDelay + " milliseconds");
|
||||
executorService.schedule(() -> {
|
||||
try {
|
||||
triggerRefresh.apply(kubernetesObject, appName).subscribe();
|
||||
}
|
||||
catch (Throwable t) {
|
||||
LOG.warn(t, "Error when refreshing appName " + appName);
|
||||
}
|
||||
}, refreshDelay, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
org.springframework.cloud.kubernetes.configuration.watcher.ConfigurationWatcherAutoConfiguration
|
||||
org.springframework.cloud.kubernetes.configuration.watcher.ConfigUpdateStrategyAutoConfiguration
|
||||
@@ -1,11 +0,0 @@
|
||||
spring:
|
||||
cloud:
|
||||
bus:
|
||||
enabled: true
|
||||
stream:
|
||||
default-binder: rabbit
|
||||
management:
|
||||
health:
|
||||
rabbit:
|
||||
enabled: true
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
spring:
|
||||
cloud:
|
||||
bus:
|
||||
enabled: true
|
||||
stream:
|
||||
default-binder: kafka
|
||||
@@ -1,23 +0,0 @@
|
||||
spring:
|
||||
config:
|
||||
import: "kubernetes:"
|
||||
application:
|
||||
name: spring-cloud-kubernetes-configuration-watcher
|
||||
cloud:
|
||||
kubernetes:
|
||||
reload:
|
||||
enabled: true
|
||||
monitoring-secrets: true
|
||||
strategy: shutdown
|
||||
bus:
|
||||
enabled: false
|
||||
management:
|
||||
health:
|
||||
rabbit:
|
||||
enabled: false
|
||||
endpoint:
|
||||
health:
|
||||
probes:
|
||||
enabled: true
|
||||
server:
|
||||
port: 8888
|
||||
@@ -1,98 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2020 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.configuration.watcher;
|
||||
|
||||
import io.kubernetes.client.openapi.apis.CoreV1Api;
|
||||
import io.kubernetes.client.openapi.models.V1ConfigMap;
|
||||
import io.kubernetes.client.openapi.models.V1ObjectMeta;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import org.springframework.cloud.bus.BusProperties;
|
||||
import org.springframework.cloud.bus.event.RefreshRemoteApplicationEvent;
|
||||
import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigMapPropertySourceLocator;
|
||||
import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider;
|
||||
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties;
|
||||
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider.NAMESPACE_PROPERTY;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
* @author Kris Iyer
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class BusEventBasedConfigMapWatcherChangeDetectorTests {
|
||||
|
||||
private static final ConfigurationUpdateStrategy UPDATE_STRATEGY = new ConfigurationUpdateStrategy("strategy",
|
||||
() -> {
|
||||
|
||||
});
|
||||
|
||||
@Mock
|
||||
private CoreV1Api coreV1Api;
|
||||
|
||||
@Mock
|
||||
private KubernetesClientConfigMapPropertySourceLocator configMapPropertySourceLocator;
|
||||
|
||||
@Mock
|
||||
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
|
||||
|
||||
@Mock
|
||||
private ApplicationEventPublisher applicationEventPublisher;
|
||||
|
||||
private BusEventBasedConfigMapWatcherChangeDetector changeDetector;
|
||||
|
||||
private BusProperties busProperties;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
MockEnvironment mockEnvironment = new MockEnvironment();
|
||||
mockEnvironment.setProperty(NAMESPACE_PROPERTY, "default");
|
||||
ConfigurationWatcherConfigurationProperties configurationWatcherConfigurationProperties = new ConfigurationWatcherConfigurationProperties();
|
||||
busProperties = new BusProperties();
|
||||
changeDetector = new BusEventBasedConfigMapWatcherChangeDetector(coreV1Api, mockEnvironment,
|
||||
ConfigReloadProperties.DEFAULT, UPDATE_STRATEGY, configMapPropertySourceLocator,
|
||||
new KubernetesNamespaceProvider(mockEnvironment), configurationWatcherConfigurationProperties,
|
||||
threadPoolTaskExecutor, new BusRefreshTrigger(applicationEventPublisher, busProperties.getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void triggerRefreshWithConfigMap() {
|
||||
V1ObjectMeta objectMeta = new V1ObjectMeta();
|
||||
objectMeta.setName("foo");
|
||||
V1ConfigMap configMap = new V1ConfigMap();
|
||||
configMap.setMetadata(objectMeta);
|
||||
changeDetector.triggerRefresh(configMap, configMap.getMetadata().getName());
|
||||
ArgumentCaptor<RefreshRemoteApplicationEvent> argumentCaptor = ArgumentCaptor
|
||||
.forClass(RefreshRemoteApplicationEvent.class);
|
||||
verify(applicationEventPublisher).publishEvent(argumentCaptor.capture());
|
||||
assertThat(argumentCaptor.getValue().getSource()).isEqualTo(configMap);
|
||||
assertThat(argumentCaptor.getValue().getOriginService()).isEqualTo(busProperties.getId());
|
||||
assertThat(argumentCaptor.getValue().getDestinationService()).isEqualTo("foo:**");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2020 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.configuration.watcher;
|
||||
|
||||
import io.kubernetes.client.openapi.apis.CoreV1Api;
|
||||
import io.kubernetes.client.openapi.models.V1ObjectMeta;
|
||||
import io.kubernetes.client.openapi.models.V1Secret;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import org.springframework.cloud.bus.BusProperties;
|
||||
import org.springframework.cloud.bus.event.RefreshRemoteApplicationEvent;
|
||||
import org.springframework.cloud.kubernetes.client.config.KubernetesClientSecretsPropertySourceLocator;
|
||||
import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider;
|
||||
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties;
|
||||
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider.NAMESPACE_PROPERTY;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
* @author Kris Iyer
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class BusEventBasedSecretsWatcherChangeDetectorTests {
|
||||
|
||||
private static final ConfigurationUpdateStrategy UPDATE_STRATEGY = new ConfigurationUpdateStrategy("strategy",
|
||||
() -> {
|
||||
|
||||
});
|
||||
|
||||
@Mock
|
||||
private CoreV1Api coreV1Api;
|
||||
|
||||
@Mock
|
||||
private KubernetesClientSecretsPropertySourceLocator secretsPropertySourceLocator;
|
||||
|
||||
@Mock
|
||||
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
|
||||
|
||||
@Mock
|
||||
private ApplicationEventPublisher applicationEventPublisher;
|
||||
|
||||
private BusEventBasedSecretsWatcherChangeDetector changeDetector;
|
||||
|
||||
private BusProperties busProperties;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
MockEnvironment mockEnvironment = new MockEnvironment();
|
||||
mockEnvironment.setProperty(NAMESPACE_PROPERTY, "default");
|
||||
ConfigurationWatcherConfigurationProperties configurationWatcherConfigurationProperties = new ConfigurationWatcherConfigurationProperties();
|
||||
busProperties = new BusProperties();
|
||||
changeDetector = new BusEventBasedSecretsWatcherChangeDetector(coreV1Api, mockEnvironment,
|
||||
ConfigReloadProperties.DEFAULT, UPDATE_STRATEGY, secretsPropertySourceLocator,
|
||||
new KubernetesNamespaceProvider(mockEnvironment), configurationWatcherConfigurationProperties,
|
||||
threadPoolTaskExecutor, new BusRefreshTrigger(applicationEventPublisher, busProperties.getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void triggerRefreshWithSecret() {
|
||||
V1ObjectMeta objectMeta = new V1ObjectMeta();
|
||||
objectMeta.setName("foo");
|
||||
V1Secret secret = new V1Secret();
|
||||
secret.setMetadata(objectMeta);
|
||||
changeDetector.triggerRefresh(secret, secret.getMetadata().getName());
|
||||
ArgumentCaptor<RefreshRemoteApplicationEvent> argumentCaptor = ArgumentCaptor
|
||||
.forClass(RefreshRemoteApplicationEvent.class);
|
||||
verify(applicationEventPublisher).publishEvent(argumentCaptor.capture());
|
||||
assertThat(argumentCaptor.getValue().getSource()).isEqualTo(secret);
|
||||
assertThat(argumentCaptor.getValue().getOriginService()).isEqualTo(busProperties.getId());
|
||||
assertThat(argumentCaptor.getValue().getDestinationService()).isEqualTo("foo:**");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2020 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.configuration.watcher;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
class ConfigurationWatcherConfigurationPropertiesTests {
|
||||
|
||||
@Test
|
||||
void setActuatorPath() {
|
||||
ConfigurationWatcherConfigurationProperties properties = new ConfigurationWatcherConfigurationProperties();
|
||||
properties.setActuatorPath("foo");
|
||||
assertThat(properties.getActuatorPath()).isEqualTo("/foo");
|
||||
properties.setActuatorPath("/foo/bar/");
|
||||
assertThat(properties.getActuatorPath()).isEqualTo("/foo/bar");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,200 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2020 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.configuration.watcher;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.github.tomakehurst.wiremock.WireMockServer;
|
||||
import com.github.tomakehurst.wiremock.client.WireMock;
|
||||
import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
|
||||
import io.kubernetes.client.openapi.apis.CoreV1Api;
|
||||
import io.kubernetes.client.openapi.models.CoreV1EndpointPort;
|
||||
import io.kubernetes.client.openapi.models.V1ConfigMap;
|
||||
import io.kubernetes.client.openapi.models.V1EndpointAddress;
|
||||
import io.kubernetes.client.openapi.models.V1ObjectMeta;
|
||||
import io.kubernetes.client.util.ClientBuilder;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockedStatic;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.kubernetes.client.KubernetesClientUtils;
|
||||
import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigMapPropertySourceLocator;
|
||||
import org.springframework.cloud.kubernetes.client.discovery.reactive.KubernetesInformerReactiveDiscoveryClient;
|
||||
import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider;
|
||||
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties;
|
||||
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy;
|
||||
import org.springframework.cloud.kubernetes.commons.discovery.DefaultKubernetesServiceInstance;
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mockStatic;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider.NAMESPACE_PROPERTY;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
* @author Kris Iyer
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class HttpBasedConfigMapWatchChangeDetectorTests {
|
||||
|
||||
private static MockedStatic<KubernetesClientUtils> clientUtilsMock;
|
||||
|
||||
private static final WireMockServer WIRE_MOCK_SERVER = new WireMockServer(
|
||||
WireMockConfiguration.options().dynamicPort());
|
||||
|
||||
@Mock
|
||||
private CoreV1Api coreV1Api;
|
||||
|
||||
@Mock
|
||||
private KubernetesClientConfigMapPropertySourceLocator configMapPropertySourceLocator;
|
||||
|
||||
@Mock
|
||||
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
|
||||
|
||||
@Mock
|
||||
private KubernetesInformerReactiveDiscoveryClient reactiveDiscoveryClient;
|
||||
|
||||
private HttpBasedConfigMapWatchChangeDetector changeDetector;
|
||||
|
||||
private ConfigurationWatcherConfigurationProperties configurationWatcherConfigurationProperties;
|
||||
|
||||
@BeforeAll
|
||||
static void beforeAll() {
|
||||
WIRE_MOCK_SERVER.start();
|
||||
clientUtilsMock = mockStatic(KubernetesClientUtils.class);
|
||||
clientUtilsMock.when(KubernetesClientUtils::createApiClientForInformerClient)
|
||||
.thenReturn(new ClientBuilder().setBasePath(WIRE_MOCK_SERVER.baseUrl()).build());
|
||||
clientUtilsMock
|
||||
.when(() -> KubernetesClientUtils.getApplicationNamespace(Mockito.any(), Mockito.any(), Mockito.any()))
|
||||
.thenReturn("default");
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void teardown() {
|
||||
clientUtilsMock.close();
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
|
||||
MockEnvironment mockEnvironment = new MockEnvironment();
|
||||
mockEnvironment.setProperty(NAMESPACE_PROPERTY, "default");
|
||||
configurationWatcherConfigurationProperties = new ConfigurationWatcherConfigurationProperties();
|
||||
WebClient webClient = WebClient.builder().build();
|
||||
|
||||
ConfigurationUpdateStrategy strategy = new ConfigurationUpdateStrategy("refresh", () -> {
|
||||
|
||||
});
|
||||
|
||||
changeDetector = new HttpBasedConfigMapWatchChangeDetector(coreV1Api, mockEnvironment,
|
||||
ConfigReloadProperties.DEFAULT, strategy, configMapPropertySourceLocator,
|
||||
new KubernetesNamespaceProvider(mockEnvironment), configurationWatcherConfigurationProperties,
|
||||
threadPoolTaskExecutor, new HttpRefreshTrigger(reactiveDiscoveryClient,
|
||||
configurationWatcherConfigurationProperties, webClient));
|
||||
}
|
||||
|
||||
@Test
|
||||
void triggerConfigMapRefresh() {
|
||||
stubReactiveCall();
|
||||
V1ConfigMap configMap = new V1ConfigMap();
|
||||
V1ObjectMeta objectMeta = new V1ObjectMeta();
|
||||
objectMeta.setName("foo");
|
||||
configMap.setMetadata(objectMeta);
|
||||
WireMock.configureFor("localhost", WIRE_MOCK_SERVER.port());
|
||||
WireMock.stubFor(WireMock.post(WireMock.urlEqualTo("/actuator/refresh"))
|
||||
.willReturn(WireMock.aResponse().withStatus(200)));
|
||||
StepVerifier.create(changeDetector.triggerRefresh(configMap, configMap.getMetadata().getName()))
|
||||
.verifyComplete();
|
||||
WireMock.verify(WireMock.postRequestedFor(WireMock.urlEqualTo("/actuator/refresh")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void triggerConfigMapRefreshWithPropertiesBasedActuatorPath() {
|
||||
stubReactiveCall();
|
||||
configurationWatcherConfigurationProperties.setActuatorPath("/my/custom/actuator");
|
||||
V1ConfigMap configMap = new V1ConfigMap();
|
||||
V1ObjectMeta objectMeta = new V1ObjectMeta();
|
||||
objectMeta.setName("foo");
|
||||
configMap.setMetadata(objectMeta);
|
||||
WireMock.configureFor("localhost", WIRE_MOCK_SERVER.port());
|
||||
WireMock.stubFor(WireMock.post(WireMock.urlEqualTo("/my/custom/actuator/refresh"))
|
||||
.willReturn(WireMock.aResponse().withStatus(200)));
|
||||
StepVerifier.create(changeDetector.triggerRefresh(configMap, configMap.getMetadata().getName()))
|
||||
.verifyComplete();
|
||||
WireMock.verify(WireMock.postRequestedFor(WireMock.urlEqualTo("/my/custom/actuator/refresh")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void triggerConfigMapRefreshWithAnnotationActuatorPath() {
|
||||
int port = WIRE_MOCK_SERVER.port();
|
||||
WireMock.configureFor("localhost", port);
|
||||
Map<String, String> metadata = new HashMap<>();
|
||||
metadata.put(ConfigurationWatcherConfigurationProperties.ANNOTATION_KEY,
|
||||
"http://:" + port + "/my/custom/actuator");
|
||||
V1EndpointAddress fooEndpointAddress = new V1EndpointAddress();
|
||||
fooEndpointAddress.setIp("127.0.0.1");
|
||||
fooEndpointAddress.setHostname("localhost");
|
||||
CoreV1EndpointPort fooEndpointPort = new CoreV1EndpointPort();
|
||||
fooEndpointPort.setPort(port);
|
||||
List<ServiceInstance> instances = new ArrayList<>();
|
||||
DefaultKubernetesServiceInstance fooServiceInstance = new DefaultKubernetesServiceInstance("foo", "foo",
|
||||
fooEndpointAddress.getIp(), fooEndpointPort.getPort(), metadata, false);
|
||||
instances.add(fooServiceInstance);
|
||||
when(reactiveDiscoveryClient.getInstances(eq("foo"))).thenReturn(Flux.fromIterable(instances));
|
||||
V1ConfigMap configMap = new V1ConfigMap();
|
||||
V1ObjectMeta objectMeta = new V1ObjectMeta();
|
||||
objectMeta.setName("foo");
|
||||
configMap.setMetadata(objectMeta);
|
||||
WireMock.stubFor(WireMock.post(WireMock.urlEqualTo("/my/custom/actuator/refresh"))
|
||||
.willReturn(WireMock.aResponse().withStatus(200)));
|
||||
StepVerifier.create(changeDetector.triggerRefresh(configMap, configMap.getMetadata().getName()))
|
||||
.verifyComplete();
|
||||
WireMock.verify(WireMock.postRequestedFor(WireMock.urlEqualTo("/my/custom/actuator/refresh")));
|
||||
}
|
||||
|
||||
private void stubReactiveCall() {
|
||||
|
||||
V1EndpointAddress fooEndpointAddress = new V1EndpointAddress();
|
||||
fooEndpointAddress.setIp("127.0.0.1");
|
||||
fooEndpointAddress.setHostname("localhost");
|
||||
|
||||
CoreV1EndpointPort fooEndpointPort = new CoreV1EndpointPort();
|
||||
fooEndpointPort.setPort(WIRE_MOCK_SERVER.port());
|
||||
|
||||
List<ServiceInstance> instances = new ArrayList<>();
|
||||
DefaultKubernetesServiceInstance fooServiceInstance = new DefaultKubernetesServiceInstance("foo", "foo",
|
||||
fooEndpointAddress.getIp(), fooEndpointPort.getPort(), new HashMap<>(), false);
|
||||
instances.add(fooServiceInstance);
|
||||
when(reactiveDiscoveryClient.getInstances(eq("foo"))).thenReturn(Flux.fromIterable(instances));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,190 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2020 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.configuration.watcher;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.github.tomakehurst.wiremock.WireMockServer;
|
||||
import com.github.tomakehurst.wiremock.client.WireMock;
|
||||
import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
|
||||
import io.kubernetes.client.openapi.apis.CoreV1Api;
|
||||
import io.kubernetes.client.openapi.models.CoreV1EndpointPort;
|
||||
import io.kubernetes.client.openapi.models.V1EndpointAddress;
|
||||
import io.kubernetes.client.openapi.models.V1ObjectMeta;
|
||||
import io.kubernetes.client.openapi.models.V1Secret;
|
||||
import io.kubernetes.client.util.ClientBuilder;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockedStatic;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.kubernetes.client.KubernetesClientUtils;
|
||||
import org.springframework.cloud.kubernetes.client.config.KubernetesClientSecretsPropertySourceLocator;
|
||||
import org.springframework.cloud.kubernetes.client.discovery.reactive.KubernetesInformerReactiveDiscoveryClient;
|
||||
import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider;
|
||||
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties;
|
||||
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy;
|
||||
import org.springframework.cloud.kubernetes.commons.discovery.DefaultKubernetesServiceInstance;
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mockStatic;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider.NAMESPACE_PROPERTY;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
* @author Kris Iyer
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class HttpBasedSecretsWatchChangeDetectorTests {
|
||||
|
||||
private static MockedStatic<KubernetesClientUtils> clientUtilsMock;
|
||||
|
||||
private static final WireMockServer WIRE_MOCK_SERVER = new WireMockServer(
|
||||
WireMockConfiguration.options().dynamicPort());
|
||||
|
||||
@Mock
|
||||
private CoreV1Api coreV1Api;
|
||||
|
||||
@Mock
|
||||
private ConfigurationUpdateStrategy updateStrategy;
|
||||
|
||||
@Mock
|
||||
private KubernetesClientSecretsPropertySourceLocator secretsPropertySourceLocator;
|
||||
|
||||
@Mock
|
||||
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
|
||||
|
||||
@Mock
|
||||
private KubernetesInformerReactiveDiscoveryClient reactiveDiscoveryClient;
|
||||
|
||||
private HttpBasedSecretsWatchChangeDetector changeDetector;
|
||||
|
||||
private ConfigurationWatcherConfigurationProperties configurationWatcherConfigurationProperties;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
MockEnvironment mockEnvironment = new MockEnvironment();
|
||||
mockEnvironment.setProperty(NAMESPACE_PROPERTY, "default");
|
||||
configurationWatcherConfigurationProperties = new ConfigurationWatcherConfigurationProperties();
|
||||
WebClient webClient = WebClient.builder().build();
|
||||
changeDetector = new HttpBasedSecretsWatchChangeDetector(coreV1Api, mockEnvironment,
|
||||
ConfigReloadProperties.DEFAULT, updateStrategy, secretsPropertySourceLocator,
|
||||
new KubernetesNamespaceProvider(mockEnvironment), configurationWatcherConfigurationProperties,
|
||||
threadPoolTaskExecutor, new HttpRefreshTrigger(reactiveDiscoveryClient,
|
||||
configurationWatcherConfigurationProperties, webClient));
|
||||
}
|
||||
|
||||
@BeforeAll
|
||||
static void beforeAll() {
|
||||
WIRE_MOCK_SERVER.start();
|
||||
clientUtilsMock = mockStatic(KubernetesClientUtils.class);
|
||||
clientUtilsMock.when(KubernetesClientUtils::createApiClientForInformerClient)
|
||||
.thenReturn(new ClientBuilder().setBasePath(WIRE_MOCK_SERVER.baseUrl()).build());
|
||||
clientUtilsMock
|
||||
.when(() -> KubernetesClientUtils.getApplicationNamespace(Mockito.any(), Mockito.any(), Mockito.any()))
|
||||
.thenReturn("default");
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void teardown() {
|
||||
clientUtilsMock.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void triggerSecretRefresh() {
|
||||
stubReactiveCall();
|
||||
V1Secret secret = new V1Secret();
|
||||
V1ObjectMeta objectMeta = new V1ObjectMeta();
|
||||
objectMeta.setName("foo");
|
||||
secret.setMetadata(objectMeta);
|
||||
WireMock.configureFor("localhost", WIRE_MOCK_SERVER.port());
|
||||
WireMock.stubFor(WireMock.post(WireMock.urlEqualTo("/actuator/refresh"))
|
||||
.willReturn(WireMock.aResponse().withStatus(200)));
|
||||
StepVerifier.create(changeDetector.triggerRefresh(secret, secret.getMetadata().getName())).verifyComplete();
|
||||
WireMock.verify(WireMock.postRequestedFor(WireMock.urlEqualTo("/actuator/refresh")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void triggerSecretRefreshWithPropertiesBasedActuatorPath() {
|
||||
stubReactiveCall();
|
||||
configurationWatcherConfigurationProperties.setActuatorPath("/my/custom/actuator");
|
||||
V1Secret secret = new V1Secret();
|
||||
V1ObjectMeta objectMeta = new V1ObjectMeta();
|
||||
objectMeta.setName("foo");
|
||||
secret.setMetadata(objectMeta);
|
||||
WireMock.configureFor("localhost", WIRE_MOCK_SERVER.port());
|
||||
WireMock.stubFor(WireMock.post(WireMock.urlEqualTo("/my/custom/actuator/refresh"))
|
||||
.willReturn(WireMock.aResponse().withStatus(200)));
|
||||
StepVerifier.create(changeDetector.triggerRefresh(secret, secret.getMetadata().getName())).verifyComplete();
|
||||
WireMock.verify(WireMock.postRequestedFor(WireMock.urlEqualTo("/my/custom/actuator/refresh")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void triggerSecretRefreshWithAnnotationActuatorPath() {
|
||||
WireMock.configureFor("localhost", WIRE_MOCK_SERVER.port());
|
||||
Map<String, String> metadata = new HashMap<>();
|
||||
metadata.put(ConfigurationWatcherConfigurationProperties.ANNOTATION_KEY,
|
||||
"http://:" + WIRE_MOCK_SERVER.port() + "/my/custom/actuator");
|
||||
V1EndpointAddress fooEndpointAddress = new V1EndpointAddress();
|
||||
fooEndpointAddress.setIp("127.0.0.1");
|
||||
fooEndpointAddress.setHostname("localhost");
|
||||
CoreV1EndpointPort fooEndpointPort = new CoreV1EndpointPort();
|
||||
fooEndpointPort.setPort(WIRE_MOCK_SERVER.port());
|
||||
List<ServiceInstance> instances = new ArrayList<>();
|
||||
DefaultKubernetesServiceInstance fooServiceInstance = new DefaultKubernetesServiceInstance("foo", "foo",
|
||||
fooEndpointAddress.getIp(), fooEndpointPort.getPort(), metadata, false);
|
||||
instances.add(fooServiceInstance);
|
||||
when(reactiveDiscoveryClient.getInstances(eq("foo"))).thenReturn(Flux.fromIterable(instances));
|
||||
V1Secret secret = new V1Secret();
|
||||
V1ObjectMeta objectMeta = new V1ObjectMeta();
|
||||
objectMeta.setName("foo");
|
||||
secret.setMetadata(objectMeta);
|
||||
WireMock.stubFor(WireMock.post(WireMock.urlEqualTo("/my/custom/actuator/refresh"))
|
||||
.willReturn(WireMock.aResponse().withStatus(200)));
|
||||
StepVerifier.create(changeDetector.triggerRefresh(secret, secret.getMetadata().getName())).verifyComplete();
|
||||
WireMock.verify(WireMock.postRequestedFor(WireMock.urlEqualTo("/my/custom/actuator/refresh")));
|
||||
}
|
||||
|
||||
private void stubReactiveCall() {
|
||||
V1EndpointAddress fooEndpointAddress = new V1EndpointAddress();
|
||||
fooEndpointAddress.setIp("127.0.0.1");
|
||||
fooEndpointAddress.setHostname("localhost");
|
||||
CoreV1EndpointPort fooEndpointPort = new CoreV1EndpointPort();
|
||||
fooEndpointPort.setPort(WIRE_MOCK_SERVER.port());
|
||||
List<ServiceInstance> instances = new ArrayList<>();
|
||||
DefaultKubernetesServiceInstance fooServiceInstance = new DefaultKubernetesServiceInstance("foo", "foo",
|
||||
fooEndpointAddress.getIp(), fooEndpointPort.getPort(), new HashMap<>(), false);
|
||||
instances.add(fooServiceInstance);
|
||||
when(reactiveDiscoveryClient.getInstances(eq("foo"))).thenReturn(Flux.fromIterable(instances));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.cloud.kubernetes.configuration.watcher;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import io.kubernetes.client.openapi.models.V1ConfigMap;
|
||||
import io.kubernetes.client.openapi.models.V1ConfigMapBuilder;
|
||||
import io.kubernetes.client.openapi.models.V1ObjectMeta;
|
||||
import io.kubernetes.client.openapi.models.V1Secret;
|
||||
import io.kubernetes.client.openapi.models.V1SecretBuilder;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.springframework.cloud.kubernetes.configuration.watcher.ConfigurationWatcherConfigurationProperties.CONFIG_MAP_LABEL;
|
||||
import static org.springframework.cloud.kubernetes.configuration.watcher.ConfigurationWatcherConfigurationProperties.SECRET_APPS_ANNOTATION;
|
||||
import static org.springframework.cloud.kubernetes.configuration.watcher.ConfigurationWatcherConfigurationProperties.SECRET_LABEL;
|
||||
|
||||
class WatcherUtilTests {
|
||||
|
||||
@Test
|
||||
void isSpringCloudKubernetesConfigFalse() {
|
||||
V1ConfigMap configMap = new V1ConfigMapBuilder().withMetadata(new V1ObjectMeta().labels(Map.of())).build();
|
||||
boolean present = WatcherUtil.isSpringCloudKubernetes(configMap, CONFIG_MAP_LABEL);
|
||||
Assertions.assertFalse(present);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isSpringCloudKubernetesConfigTrue() {
|
||||
V1ConfigMap configMap = new V1ConfigMapBuilder()
|
||||
.withMetadata(new V1ObjectMeta().labels(Map.of(CONFIG_MAP_LABEL, "true"))).build();
|
||||
boolean present = WatcherUtil.isSpringCloudKubernetes(configMap, CONFIG_MAP_LABEL);
|
||||
Assertions.assertTrue(present);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isSpringCloudKubernetesSecretFalse() {
|
||||
V1Secret secret = new V1SecretBuilder().withMetadata(new V1ObjectMeta().labels(Map.of())).build();
|
||||
boolean present = WatcherUtil.isSpringCloudKubernetes(secret, SECRET_LABEL);
|
||||
Assertions.assertFalse(present);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isSpringCloudKubernetesSecretTrue() {
|
||||
V1Secret secret = new V1SecretBuilder().withMetadata(new V1ObjectMeta().labels(Map.of(SECRET_LABEL, "true")))
|
||||
.build();
|
||||
boolean present = WatcherUtil.isSpringCloudKubernetes(secret, SECRET_LABEL);
|
||||
Assertions.assertTrue(present);
|
||||
}
|
||||
|
||||
@Test
|
||||
void labelsMissing() {
|
||||
V1Secret secret = new V1SecretBuilder().withMetadata(new V1ObjectMeta()).build();
|
||||
Map<String, String> res = WatcherUtil.labels(secret);
|
||||
Assertions.assertEquals(res.size(), 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void labelsPresent() {
|
||||
V1Secret secret = new V1SecretBuilder().withMetadata(new V1ObjectMeta().labels(Map.of("a", "b"))).build();
|
||||
Map<String, String> res = WatcherUtil.labels(secret);
|
||||
Assertions.assertEquals(res.size(), 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void appsNoMetadata() {
|
||||
V1Secret secret = new V1SecretBuilder().build();
|
||||
Set<String> apps = WatcherUtil.apps(secret, SECRET_APPS_ANNOTATION);
|
||||
Assertions.assertEquals(apps.size(), 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void appsNoAnnotations() {
|
||||
V1Secret secret = new V1SecretBuilder().withMetadata(new V1ObjectMeta().annotations(Map.of())).build();
|
||||
Set<String> apps = WatcherUtil.apps(secret, SECRET_APPS_ANNOTATION);
|
||||
Assertions.assertEquals(apps.size(), 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void appsAnnotationNotFound() {
|
||||
V1Secret secret = new V1SecretBuilder().withMetadata(new V1ObjectMeta().annotations(Map.of("a", "b"))).build();
|
||||
Set<String> apps = WatcherUtil.apps(secret, SECRET_APPS_ANNOTATION);
|
||||
Assertions.assertEquals(apps.size(), 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void appsSingleResult() {
|
||||
V1Secret secret = new V1SecretBuilder()
|
||||
.withMetadata(new V1ObjectMeta().annotations(Map.of(SECRET_APPS_ANNOTATION, "one-app"))).build();
|
||||
Set<String> apps = WatcherUtil.apps(secret, SECRET_APPS_ANNOTATION);
|
||||
Assertions.assertEquals(apps.size(), 1);
|
||||
Assertions.assertEquals(apps.iterator().next(), "one-app");
|
||||
}
|
||||
|
||||
@Test
|
||||
void appsMultipleResults() {
|
||||
V1Secret secret = new V1SecretBuilder()
|
||||
.withMetadata(new V1ObjectMeta().annotations(Map.of(SECRET_APPS_ANNOTATION, "one, two, three ")))
|
||||
.build();
|
||||
Set<String> apps = WatcherUtil.apps(secret, SECRET_APPS_ANNOTATION);
|
||||
Assertions.assertEquals(apps.size(), 3);
|
||||
Assertions.assertTrue(apps.contains("one"));
|
||||
Assertions.assertTrue(apps.contains("two"));
|
||||
Assertions.assertTrue(apps.contains("three"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-kubernetes-controllers</artifactId>
|
||||
<version>3.0.4-SNAPSHOT</version>
|
||||
</parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-kubernetes-discoveryserver</artifactId>
|
||||
<version>3.0.4-SNAPSHOT</version>
|
||||
<description>Spring Cloud parent pom, managing plugins and dependencies for Spring
|
||||
Cloud projects</description>
|
||||
<url>https://cloud.spring.io/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-discoveryserver</url>
|
||||
<inceptionYear>2017</inceptionYear>
|
||||
<organization>
|
||||
<name>Pivotal Software, Inc.</name>
|
||||
<url>https://www.spring.io</url>
|
||||
</organization>
|
||||
<licenses>
|
||||
<license>
|
||||
<name>Apache License, Version 2.0</name>
|
||||
<url>https://www.apache.org/licenses/LICENSE-2.0.txt</url>
|
||||
<distribution>repo</distribution>
|
||||
</license>
|
||||
</licenses>
|
||||
<developers>
|
||||
<developer>
|
||||
<id>dsyer</id>
|
||||
<name>Dave Syer</name>
|
||||
<email>dsyer at pivotal.io</email>
|
||||
<organization>Pivotal Software, Inc.</organization>
|
||||
<organizationUrl>https://www.spring.io</organizationUrl>
|
||||
<roles>
|
||||
<role>lead</role>
|
||||
</roles>
|
||||
</developer>
|
||||
<developer>
|
||||
<id>sgibb</id>
|
||||
<name>Spencer Gibb</name>
|
||||
<email>sgibb at pivotal.io</email>
|
||||
<organization>Pivotal Software, Inc.</organization>
|
||||
<organizationUrl>https://www.spring.io</organizationUrl>
|
||||
<roles>
|
||||
<role>lead</role>
|
||||
</roles>
|
||||
</developer>
|
||||
<developer>
|
||||
<id>mgrzejszczak</id>
|
||||
<name>Marcin Grzejszczak</name>
|
||||
<email>mgrzejszczak at pivotal.io</email>
|
||||
<organization>Pivotal Software, Inc.</organization>
|
||||
<organizationUrl>https://www.spring.io</organizationUrl>
|
||||
<roles>
|
||||
<role>developer</role>
|
||||
</roles>
|
||||
</developer>
|
||||
<developer>
|
||||
<id>rbaxter</id>
|
||||
<name>Ryan Baxter</name>
|
||||
<email>rbaxter at pivotal.io</email>
|
||||
<organization>Pivotal Software, Inc.</organization>
|
||||
<organizationUrl>https://www.spring.io</organizationUrl>
|
||||
<roles>
|
||||
<role>developer</role>
|
||||
</roles>
|
||||
</developer>
|
||||
<developer>
|
||||
<id>omaciaszeksharma</id>
|
||||
<name>Olga Maciaszek-Sharma</name>
|
||||
<email>omaciaszeksharma at pivotal.io</email>
|
||||
<organization>Pivotal Software, Inc.</organization>
|
||||
<organizationUrl>https://www.spring.io</organizationUrl>
|
||||
<roles>
|
||||
<role>developer</role>
|
||||
</roles>
|
||||
</developer>
|
||||
</developers>
|
||||
<scm>
|
||||
<connection>scm:git:git://github.com/spring-cloud/spring-cloud-kubernetes.git/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-discoveryserver</connection>
|
||||
<developerConnection>scm:git:ssh://git@github.com/spring-cloud/spring-cloud-kubernetes.git/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-discoveryserver</developerConnection>
|
||||
<url>https://github.com/spring-cloud/spring-cloud-kubernetes/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-discoveryserver</url>
|
||||
</scm>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-webflux</artifactId>
|
||||
<version>3.0.7</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
<version>3.0.7</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-kubernetes-client</artifactId>
|
||||
<version>3.0.4-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -1,130 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>spring-cloud-kubernetes-controllers</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>3.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>spring-cloud-kubernetes-discoveryserver</artifactId>
|
||||
|
||||
<properties>
|
||||
<docker.registry.organization>springcloud</docker.registry.organization>
|
||||
<plexus-archiver.version>4.1.0</plexus-archiver.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-webflux</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-kubernetes-client</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.tomakehurst</groupId>
|
||||
<artifactId>wiremock-jre8-standalone</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-kubernetes-test-support</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<image>
|
||||
<name>${env.IMAGE}</name>
|
||||
</image>
|
||||
<goal>build-image</goal>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>build-image</id>
|
||||
<configuration>
|
||||
<skip>${skip.build.image}</skip>
|
||||
</configuration>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>build-image</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>repackage</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>dockerpush</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>com.spotify</groupId>
|
||||
<artifactId>dockerfile-maven-plugin</artifactId>
|
||||
<version>1.4.12</version>
|
||||
<configuration>
|
||||
<repository>${docker.registry.organization}/${artifactId}</repository>
|
||||
<tag>${project.version}</tag>
|
||||
<username>${env.DOCKER_HUB_USERNAME}</username>
|
||||
<password>${env.DOCKER_HUB_PASSWORD}</password>
|
||||
<build>
|
||||
<noCache>true</noCache>
|
||||
</build>
|
||||
</configuration>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.codehaus.plexus</groupId>
|
||||
<artifactId>plexus-archiver</artifactId>
|
||||
<version>${plexus-archiver.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>imagename</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>!env.IMAGE</name>
|
||||
</property>
|
||||
</activation>
|
||||
<properties>
|
||||
<env.IMAGE>springcloud/${project.artifactId}:${project.version}</env.IMAGE>
|
||||
</properties>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* 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.springframewok.cloud.kubernetes.discoveryserver;
|
||||
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class DiscoveryServerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
new SpringApplicationBuilder(DiscoveryServerApplication.class).run(args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* 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.springframewok.cloud.kubernetes.discoveryserver;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.kubernetes.client.discovery.reactive.KubernetesInformerReactiveDiscoveryClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@RestController
|
||||
public class DiscoveryServerController {
|
||||
|
||||
private final KubernetesInformerReactiveDiscoveryClient reactiveDiscoveryClient;
|
||||
|
||||
public DiscoveryServerController(KubernetesInformerReactiveDiscoveryClient reactiveDiscoveryClient) {
|
||||
this.reactiveDiscoveryClient = reactiveDiscoveryClient;
|
||||
}
|
||||
|
||||
@GetMapping("/apps")
|
||||
public Flux<Service> apps() {
|
||||
return reactiveDiscoveryClient.getServices().flatMap(service -> reactiveDiscoveryClient.getInstances(service)
|
||||
.collectList().flatMap(serviceInstances -> Mono.just(new Service(service, serviceInstances))));
|
||||
}
|
||||
|
||||
@GetMapping("/apps/{name}")
|
||||
public Flux<ServiceInstance> appInstances(@PathVariable String name) {
|
||||
return reactiveDiscoveryClient.getInstances(name);
|
||||
}
|
||||
|
||||
@GetMapping("/app/{name}/{instanceId}")
|
||||
public Mono<ServiceInstance> appInstance(@PathVariable String name, @PathVariable String instanceId) {
|
||||
return reactiveDiscoveryClient.getInstances(name)
|
||||
.filter(serviceInstance -> serviceInstance.getInstanceId().equals(instanceId)).singleOrEmpty();
|
||||
}
|
||||
|
||||
private record Service(String name, List<ServiceInstance> serviceInstances) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
server:
|
||||
port: 8761
|
||||
@@ -1,120 +0,0 @@
|
||||
/*
|
||||
* 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.springframewok.cloud.kubernetes.discoveryserver;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.kubernetes.client.discovery.reactive.KubernetesInformerReactiveDiscoveryClient;
|
||||
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesServiceInstance;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
class DiscoveryServerControllerTests {
|
||||
|
||||
private static final KubernetesServiceInstance serviceAInstance1 = new KubernetesServiceInstance("serviceAInstance1",
|
||||
"serviceAInstance1", "2.2.2.2", 8080, new HashMap<>(), false, "namespace1", null);
|
||||
|
||||
private static final KubernetesServiceInstance serviceAInstance2 = new KubernetesServiceInstance("serviceAInstance2",
|
||||
"serviceAInstance2", "2.2.2.2", 8080, new HashMap<>(), false, "namespace1", null);
|
||||
|
||||
private static final KubernetesServiceInstance serviceAInstance3 = new KubernetesServiceInstance("serviceAInstance3",
|
||||
"serviceAInstance3", "2.2.2.2", 8080, new HashMap<>(), false, "namespace2", null);
|
||||
|
||||
private static final KubernetesServiceInstance serviceBInstance1 = new KubernetesServiceInstance("serviceBInstance1",
|
||||
"serviceBInstance1", "2.2.2.2", 8080, new HashMap<>(), false, "namespace1", null);
|
||||
|
||||
private static final KubernetesServiceInstance serviceCInstance1 = new KubernetesServiceInstance("serviceCInstance1",
|
||||
"serviceCInstance1", "2.2.2.2", 8080, new HashMap<>(), false, "namespace2", null);
|
||||
|
||||
private static DiscoveryServerController.Service serviceA = new DiscoveryServerController.Service();
|
||||
private static DiscoveryServerController.Service serviceB = new DiscoveryServerController.Service();
|
||||
private static DiscoveryServerController.Service serviceC = new DiscoveryServerController.Service();
|
||||
|
||||
private static KubernetesInformerReactiveDiscoveryClient discoveryClient;
|
||||
|
||||
@BeforeAll
|
||||
static void beforeAll() {
|
||||
Flux<String> services = Flux.just("serviceA", "serviceB", "serviceC");
|
||||
|
||||
List<ServiceInstance> serviceAInstanceList = new ArrayList<>();
|
||||
serviceAInstanceList.add(serviceAInstance1);
|
||||
serviceAInstanceList.add(serviceAInstance2);
|
||||
serviceAInstanceList.add(serviceAInstance3);
|
||||
|
||||
Flux<ServiceInstance> serviceAInstances = Flux.fromIterable(serviceAInstanceList);
|
||||
|
||||
List<ServiceInstance> serviceBInstanceList = Collections.singletonList(serviceBInstance1);
|
||||
Flux<ServiceInstance> serviceBInstances = Flux.fromIterable(serviceBInstanceList);
|
||||
|
||||
List<ServiceInstance> serviceCInstanceList = Collections.singletonList(serviceCInstance1);
|
||||
Flux<ServiceInstance> serviceCInstances = Flux.fromIterable(serviceCInstanceList);
|
||||
|
||||
discoveryClient = mock(KubernetesInformerReactiveDiscoveryClient.class);
|
||||
when(discoveryClient.getServices()).thenReturn(services);
|
||||
when(discoveryClient.getInstances(eq("serviceA"))).thenReturn(serviceAInstances);
|
||||
when(discoveryClient.getInstances(eq("serviceB"))).thenReturn(serviceBInstances);
|
||||
when(discoveryClient.getInstances(eq("serviceC"))).thenReturn(serviceCInstances);
|
||||
when(discoveryClient.getInstances(eq("serviceD"))).thenReturn(Flux.empty());
|
||||
|
||||
serviceA.setName("serviceA");
|
||||
serviceA.setServiceInstances(serviceAInstanceList);
|
||||
|
||||
serviceB.setName("serviceB");
|
||||
serviceB.setServiceInstances(serviceBInstanceList);
|
||||
|
||||
serviceC.setName("serviceC");
|
||||
serviceC.setServiceInstances(serviceCInstanceList);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
void apps() {
|
||||
DiscoveryServerController controller = new DiscoveryServerController(discoveryClient);
|
||||
StepVerifier.create(controller.apps()).expectNext(serviceA, serviceB, serviceC).verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void appInstances() {
|
||||
DiscoveryServerController controller = new DiscoveryServerController(discoveryClient);
|
||||
StepVerifier.create(controller.appInstances("serviceA")).expectNext(serviceAInstance1, serviceAInstance2, serviceAInstance3).verifyComplete();
|
||||
StepVerifier.create(controller.appInstances("serviceB")).expectNext(serviceBInstance1).verifyComplete();
|
||||
StepVerifier.create(controller.appInstances("serviceC")).expectNext(serviceCInstance1).verifyComplete();
|
||||
StepVerifier.create(controller.appInstances("serviceD")).expectNextCount(0).verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void appInstance() {
|
||||
DiscoveryServerController controller = new DiscoveryServerController(discoveryClient);
|
||||
StepVerifier.create(controller.appInstance("serviceA", "serviceAInstance2")).expectNext(serviceAInstance2).verifyComplete();
|
||||
StepVerifier.create(controller.appInstance("serviceB", "doesnotexist")).expectNextCount(0).verifyComplete();
|
||||
}
|
||||
}
|
||||
@@ -1,208 +0,0 @@
|
||||
/*
|
||||
* 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.springframewok.cloud.kubernetes.discoveryserver;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import com.github.tomakehurst.wiremock.WireMockServer;
|
||||
import com.github.tomakehurst.wiremock.client.WireMock;
|
||||
import io.kubernetes.client.openapi.ApiClient;
|
||||
import io.kubernetes.client.openapi.JSON;
|
||||
import io.kubernetes.client.openapi.models.V1EndpointAddress;
|
||||
import io.kubernetes.client.openapi.models.V1EndpointPort;
|
||||
import io.kubernetes.client.openapi.models.V1EndpointSubset;
|
||||
import io.kubernetes.client.openapi.models.V1Endpoints;
|
||||
import io.kubernetes.client.openapi.models.V1EndpointsListBuilder;
|
||||
import io.kubernetes.client.openapi.models.V1ListMetaBuilder;
|
||||
import io.kubernetes.client.openapi.models.V1ObjectMeta;
|
||||
import io.kubernetes.client.openapi.models.V1ObjectReferenceBuilder;
|
||||
import io.kubernetes.client.openapi.models.V1Service;
|
||||
import io.kubernetes.client.openapi.models.V1ServiceListBuilder;
|
||||
import io.kubernetes.client.openapi.models.V1ServiceSpec;
|
||||
import io.kubernetes.client.openapi.models.V1ServiceStatus;
|
||||
import io.kubernetes.client.util.ClientBuilder;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider;
|
||||
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesServiceInstance;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
|
||||
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
|
||||
import static com.github.tomakehurst.wiremock.client.WireMock.get;
|
||||
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
|
||||
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = DiscoveryServerIntegrationTests.TestConfig.class,
|
||||
properties = {"debug=true"})
|
||||
public class DiscoveryServerIntegrationTests {
|
||||
|
||||
private static final V1Service testService1 = new V1Service()
|
||||
.metadata(new V1ObjectMeta().name("test-svc-1").namespace("namespace1"))
|
||||
.spec(new V1ServiceSpec().loadBalancerIP("1.1.1.1")).status(new V1ServiceStatus());
|
||||
private static final V1Endpoints testEndpoints1 = new V1Endpoints()
|
||||
.metadata(new V1ObjectMeta().name("test-svc-1").namespace("namespace1"))
|
||||
.addSubsetsItem(new V1EndpointSubset().addPortsItem(new V1EndpointPort().port(8080).name("http"))
|
||||
.addAddressesItem(new V1EndpointAddress().ip("2.2.2.2").targetRef(new V1ObjectReferenceBuilder().withUid("uid1").build())));
|
||||
|
||||
private static final V1Service testService2 = new V1Service()
|
||||
.metadata(new V1ObjectMeta().name("test-svc-1").namespace("namespace2"))
|
||||
.spec(new V1ServiceSpec().loadBalancerIP("1.1.1.1")).status(new V1ServiceStatus());
|
||||
|
||||
private static final V1Service testService3 = new V1Service()
|
||||
.metadata(new V1ObjectMeta().name("test-svc-3").namespace("namespace1").putLabelsItem("spring", "true")
|
||||
.putLabelsItem("k8s", "true"))
|
||||
.spec(new V1ServiceSpec().loadBalancerIP("1.1.1.1")).status(new V1ServiceStatus());
|
||||
private static final V1Endpoints testEndpoints3 = new V1Endpoints()
|
||||
.metadata(new V1ObjectMeta().name("test-svc-3").namespace("namespace1"))
|
||||
.addSubsetsItem(new V1EndpointSubset().addPortsItem(new V1EndpointPort().port(8080).name("http"))
|
||||
.addAddressesItem(new V1EndpointAddress().ip("2.2.2.2").targetRef(new V1ObjectReferenceBuilder().withUid("uid2").build())));
|
||||
|
||||
|
||||
private static WireMockServer wireMockServer;
|
||||
|
||||
@Autowired
|
||||
WebTestClient webTestClient;
|
||||
|
||||
@Test
|
||||
void apps(){
|
||||
Map<String, String> kubernetesServiceInstance1Metadata = new HashMap<>();
|
||||
kubernetesServiceInstance1Metadata.put(testEndpoints1.getSubsets().get(0).getPorts().get(0).getName(), testEndpoints1.getSubsets().get(0).getPorts().get(0).getPort().toString());
|
||||
|
||||
Map<String, String> kubernetesServiceInstance2Metadata = new HashMap<>();
|
||||
kubernetesServiceInstance2Metadata.put(testEndpoints3.getSubsets().get(0).getPorts().get(0).getName(), testEndpoints3.getSubsets().get(0).getPorts().get(0).getPort().toString());
|
||||
kubernetesServiceInstance2Metadata.putAll(testService3.getMetadata().getLabels());
|
||||
|
||||
KubernetesServiceInstance kubernetesServiceInstance1 = new KubernetesServiceInstance(testEndpoints1.getSubsets().get(0).getAddresses().get(0).getTargetRef().getUid(), testService1.getMetadata().getName(), testEndpoints1.getSubsets().get(0).getAddresses().get(0).getIp(), testEndpoints1.getSubsets().get(0).getPorts().get(0).getPort(), kubernetesServiceInstance1Metadata, false, testService1.getMetadata().getNamespace(), null);
|
||||
KubernetesServiceInstance kubernetesServiceInstance3 = new KubernetesServiceInstance(testEndpoints3.getSubsets().get(0).getAddresses().get(0).getTargetRef().getUid(), testService3.getMetadata().getName(), testEndpoints3.getSubsets().get(0).getAddresses().get(0).getIp(), testEndpoints3.getSubsets().get(0).getPorts().get(0).getPort(), kubernetesServiceInstance2Metadata, false, testService3.getMetadata().getNamespace(), null);
|
||||
|
||||
webTestClient.get().uri("/apps").exchange().expectBodyList(KubernetesService.class).hasSize(2).contains(new KubernetesService(testService1.getMetadata().getName(),
|
||||
Collections.singletonList(kubernetesServiceInstance1)), new KubernetesService(testService3.getMetadata().getName(), Collections.singletonList(kubernetesServiceInstance3)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void appsName() {
|
||||
Map<String, String> kubernetesServiceInstance2Metadata = new HashMap<>();
|
||||
kubernetesServiceInstance2Metadata.put(testEndpoints3.getSubsets().get(0).getPorts().get(0).getName(), testEndpoints3.getSubsets().get(0).getPorts().get(0).getPort().toString());
|
||||
kubernetesServiceInstance2Metadata.putAll(testService3.getMetadata().getLabels());
|
||||
KubernetesServiceInstance kubernetesServiceInstance3 = new KubernetesServiceInstance(testEndpoints3.getSubsets().get(0).getAddresses().get(0).getTargetRef().getUid(), testService3.getMetadata().getName(), testEndpoints3.getSubsets().get(0).getAddresses().get(0).getIp(), testEndpoints3.getSubsets().get(0).getPorts().get(0).getPort(), kubernetesServiceInstance2Metadata, false, testService3.getMetadata().getNamespace(), null);
|
||||
webTestClient.get().uri("/apps/test-svc-3").exchange().expectBodyList(KubernetesServiceInstance.class).hasSize(1).contains(kubernetesServiceInstance3);
|
||||
}
|
||||
|
||||
@Test
|
||||
void instance() {
|
||||
Map<String, String> kubernetesServiceInstance2Metadata = new HashMap<>();
|
||||
kubernetesServiceInstance2Metadata.put(testEndpoints3.getSubsets().get(0).getPorts().get(0).getName(), testEndpoints3.getSubsets().get(0).getPorts().get(0).getPort().toString());
|
||||
kubernetesServiceInstance2Metadata.putAll(testService3.getMetadata().getLabels());
|
||||
KubernetesServiceInstance kubernetesServiceInstance3 = new KubernetesServiceInstance(testEndpoints3.getSubsets().get(0).getAddresses().get(0).getTargetRef().getUid(), testService3.getMetadata().getName(), testEndpoints3.getSubsets().get(0).getAddresses().get(0).getIp(), testEndpoints3.getSubsets().get(0).getPorts().get(0).getPort(), kubernetesServiceInstance2Metadata, false, testService3.getMetadata().getNamespace(), null);
|
||||
webTestClient.get().uri("/app/test-svc-3/uid2").exchange().expectBody(KubernetesServiceInstance.class).isEqualTo(kubernetesServiceInstance3);
|
||||
}
|
||||
|
||||
@SpringBootApplication
|
||||
protected static class TestConfig {
|
||||
|
||||
@Bean
|
||||
public KubernetesNamespaceProvider kubernetesNamespaceProvider() {
|
||||
KubernetesNamespaceProvider provider = mock(KubernetesNamespaceProvider.class);
|
||||
when(provider.getNamespace()).thenReturn("namespace1");
|
||||
return provider;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ApiClient apiClient() {
|
||||
wireMockServer = new WireMockServer(options().dynamicPort());
|
||||
wireMockServer.start();
|
||||
WireMock.configureFor(wireMockServer.port());
|
||||
stubFor(get("/api/v1/namespaces/namespace1/endpoints?resourceVersion=0&watch=false")
|
||||
.willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(new V1EndpointsListBuilder()
|
||||
.withMetadata(new V1ListMetaBuilder().withNewResourceVersion("0").build()).addToItems(testEndpoints1, testEndpoints3).build()))));
|
||||
stubFor(get("/api/v1/namespaces/namespace1/services?resourceVersion=0&watch=false")
|
||||
.willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(new V1ServiceListBuilder()
|
||||
.withMetadata(new V1ListMetaBuilder().withNewResourceVersion("0").build()).addToItems(testService1, testService2, testService3).build()))));
|
||||
stubFor(get("/api/v1/namespaces/namespace1/endpoints?watch=true")
|
||||
.willReturn(aResponse().withStatus(200)));
|
||||
stubFor(get("/api/v1/namespaces/namespace1/services?watch=true")
|
||||
.willReturn(aResponse().withStatus(200)));
|
||||
ApiClient apiClient = new ClientBuilder().setBasePath(wireMockServer.baseUrl()).build();
|
||||
return apiClient;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class KubernetesService {
|
||||
|
||||
private String name;
|
||||
|
||||
private List<KubernetesServiceInstance> serviceInstances;
|
||||
|
||||
public KubernetesService() { }
|
||||
|
||||
public KubernetesService(String name, List<KubernetesServiceInstance> serviceInstances) {
|
||||
this.name = name;
|
||||
this.serviceInstances = serviceInstances;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public List<KubernetesServiceInstance> getServiceInstances() {
|
||||
return serviceInstances;
|
||||
}
|
||||
|
||||
public void setServiceInstances(List<KubernetesServiceInstance> serviceInstances) {
|
||||
this.serviceInstances = serviceInstances;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
KubernetesService service = (KubernetesService) o;
|
||||
return Objects.equals(getName(), service.getName()) && Objects.equals(getServiceInstances(), service.getServiceInstances());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(getName(), getServiceInstances());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "KubernetesService{" +
|
||||
"name='" + name + '\'' +
|
||||
", serviceInstances=" + serviceInstances +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user