@@ -27,6 +27,7 @@ import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.cloud.bootstrap.config.BootstrapPropertySource;
|
||||
import org.springframework.cloud.bootstrap.config.PropertySourceLocator;
|
||||
import org.springframework.cloud.kubernetes.commons.config.MountConfigMapPropertySource;
|
||||
import org.springframework.cloud.kubernetes.commons.config.SecretsPropertySource;
|
||||
import org.springframework.core.env.CompositePropertySource;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
@@ -110,6 +111,10 @@ public final class ConfigReloadUtil {
|
||||
// we know that the type is correct here
|
||||
managedSources.add((S) mountConfigMapPropertySource);
|
||||
}
|
||||
else if (source instanceof SecretsPropertySource secretsPropertySource) {
|
||||
// we know that the type is correct here
|
||||
managedSources.add((S) secretsPropertySource);
|
||||
}
|
||||
else if (source instanceof BootstrapPropertySource<?> bootstrapPropertySource) {
|
||||
PropertySource<?> propertySource = bootstrapPropertySource.getDelegate();
|
||||
LOG.debug(() -> "bootstrap delegate class : " + propertySource.getClass());
|
||||
@@ -120,6 +125,10 @@ public final class ConfigReloadUtil {
|
||||
// we know that the type is correct here
|
||||
managedSources.add((S) mountConfigMapPropertySource);
|
||||
}
|
||||
else if (propertySource instanceof SecretsPropertySource secretsPropertySource) {
|
||||
// we know that the type is correct here
|
||||
managedSources.add((S) secretsPropertySource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,8 @@ import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.cloud.bootstrap.config.BootstrapPropertySource;
|
||||
import org.springframework.cloud.kubernetes.commons.config.MountConfigMapPropertySource;
|
||||
import org.springframework.cloud.kubernetes.commons.config.SecretsPropertySource;
|
||||
import org.springframework.cloud.kubernetes.commons.config.SourceData;
|
||||
import org.springframework.core.env.CompositePropertySource;
|
||||
import org.springframework.core.env.EnumerablePropertySource;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
@@ -127,8 +129,8 @@ class ConfigReloadUtilTests {
|
||||
MockEnvironment environment = new MockEnvironment();
|
||||
MutablePropertySources propertySources = environment.getPropertySources();
|
||||
propertySources.addFirst(new OneComposite());
|
||||
propertySources.addFirst(new PlainPropertySource("plain"));
|
||||
propertySources.addFirst(new OneBootstrap(new EnumerablePropertySource<>("enumerable") {
|
||||
propertySources.addFirst(new PlainPropertySource<>("plain"));
|
||||
propertySources.addFirst(new OneBootstrap<>(new EnumerablePropertySource<>("enumerable") {
|
||||
@Override
|
||||
public String[] getPropertyNames() {
|
||||
return new String[0];
|
||||
@@ -143,11 +145,38 @@ class ConfigReloadUtilTests {
|
||||
|
||||
List<? extends PropertySource> result = ConfigReloadUtil.findPropertySources(PlainPropertySource.class,
|
||||
environment);
|
||||
|
||||
Assertions.assertThat(result.size()).isEqualTo(4);
|
||||
Assertions.assertThat(result.get(0).getProperty("a")).isEqualTo("b");
|
||||
Assertions.assertThat(result.get(1).getProperty("")).isEqualTo("plain");
|
||||
Assertions.assertThat(result.get(2).getProperty("")).isEqualTo("from-bootstrap");
|
||||
Assertions.assertThat(result.get(3).getProperty("")).isEqualTo("from-inner-two-composite");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSecretsPropertySource() {
|
||||
MockEnvironment environment = new MockEnvironment();
|
||||
MutablePropertySources propertySources = environment.getPropertySources();
|
||||
propertySources.addFirst(new SecretsPropertySource(new SourceData("secret", Map.of("a", "b"))));
|
||||
|
||||
List<? extends PropertySource> result = ConfigReloadUtil.findPropertySources(PlainPropertySource.class,
|
||||
environment);
|
||||
assertThat(result.size()).isEqualTo(1);
|
||||
assertThat(result.get(0).getProperty("a")).isEqualTo("b");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBootstrapSecretsPropertySource() {
|
||||
MockEnvironment environment = new MockEnvironment();
|
||||
MutablePropertySources propertySources = environment.getPropertySources();
|
||||
propertySources
|
||||
.addFirst(new OneBootstrap<>(new SecretsPropertySource(new SourceData("secret", Map.of("a", "b")))));
|
||||
|
||||
List<? extends PropertySource> result = ConfigReloadUtil.findPropertySources(PlainPropertySource.class,
|
||||
environment);
|
||||
assertThat(result.size()).isEqualTo(1);
|
||||
assertThat(result.get(0).getProperty("a")).isEqualTo("b");
|
||||
}
|
||||
|
||||
private static final class OneComposite extends CompositePropertySource {
|
||||
@@ -171,12 +200,12 @@ class ConfigReloadUtilTests {
|
||||
|
||||
@Override
|
||||
public Collection<PropertySource<?>> getPropertySources() {
|
||||
return List.of(new PlainPropertySource("from-inner-two-composite"));
|
||||
return List.of(new PlainPropertySource<>("from-inner-two-composite"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class PlainPropertySource extends PropertySource<String> {
|
||||
private static final class PlainPropertySource<T> extends PropertySource<T> {
|
||||
|
||||
private PlainPropertySource(String name) {
|
||||
super(name);
|
||||
@@ -189,15 +218,18 @@ class ConfigReloadUtilTests {
|
||||
|
||||
}
|
||||
|
||||
private static final class OneBootstrap extends BootstrapPropertySource<String> {
|
||||
private static final class OneBootstrap<T> extends BootstrapPropertySource<T> {
|
||||
|
||||
private OneBootstrap(EnumerablePropertySource<String> delegate) {
|
||||
private final EnumerablePropertySource<T> delegate;
|
||||
|
||||
private OneBootstrap(EnumerablePropertySource<T> delegate) {
|
||||
super(delegate);
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PropertySource<String> getDelegate() {
|
||||
return new PlainPropertySource("from-bootstrap");
|
||||
public PropertySource<T> getDelegate() {
|
||||
return delegate;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -51,16 +51,16 @@ class Fabric8DiscoveryFilterIT extends Fabric8DiscoveryBase {
|
||||
util.createNamespace(NAMESPACE_A_UAT);
|
||||
util.createNamespace(NAMESPACE_B_UAT);
|
||||
|
||||
util.wiremock(NAMESPACE_A_UAT, Phase.CREATE);
|
||||
util.wiremock(NAMESPACE_B_UAT, Phase.CREATE);
|
||||
util.wiremock(NAMESPACE_A_UAT, Phase.CREATE, false);
|
||||
util.wiremock(NAMESPACE_B_UAT, Phase.CREATE, false);
|
||||
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void afterEach() {
|
||||
|
||||
util.wiremock(NAMESPACE_A_UAT, Phase.DELETE);
|
||||
util.wiremock(NAMESPACE_B_UAT, Phase.DELETE);
|
||||
util.wiremock(NAMESPACE_A_UAT, Phase.DELETE, false);
|
||||
util.wiremock(NAMESPACE_B_UAT, Phase.DELETE, false);
|
||||
|
||||
util.deleteNamespace(NAMESPACE_A_UAT);
|
||||
util.deleteNamespace(NAMESPACE_B_UAT);
|
||||
|
||||
@@ -44,8 +44,6 @@ import static org.springframework.cloud.kubernetes.configuration.watcher.TestUti
|
||||
*/
|
||||
class ActuatorRefreshIT {
|
||||
|
||||
private static final String WIREMOCK_PATH = "/";
|
||||
|
||||
private static final String NAMESPACE = "default";
|
||||
|
||||
private static final K3sContainer K3S = Commons.container();
|
||||
@@ -72,12 +70,12 @@ class ActuatorRefreshIT {
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
util.wiremock(NAMESPACE, WIREMOCK_PATH, Phase.CREATE);
|
||||
util.wiremock(NAMESPACE, Phase.CREATE, true);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void after() {
|
||||
util.wiremock(NAMESPACE, WIREMOCK_PATH, Phase.DELETE);
|
||||
util.wiremock(NAMESPACE, Phase.DELETE, true);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -114,10 +112,10 @@ class ActuatorRefreshIT {
|
||||
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setEnv(envVars);
|
||||
|
||||
if (phase.equals(Phase.CREATE)) {
|
||||
util.createAndWait(NAMESPACE, null, deployment, service, null, true);
|
||||
util.createAndWait(NAMESPACE, null, deployment, service, true);
|
||||
}
|
||||
else {
|
||||
util.deleteAndWait(NAMESPACE, deployment, service, null);
|
||||
util.deleteAndWait(NAMESPACE, deployment, service);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ class ActuatorRefreshMultipleNamespacesIT {
|
||||
util = new Util(K3S);
|
||||
util.createNamespace(LEFT_NAMESPACE);
|
||||
util.createNamespace(RIGHT_NAMESPACE);
|
||||
util.wiremock(DEFAULT_NAMESPACE, "/", Phase.CREATE);
|
||||
util.wiremock(DEFAULT_NAMESPACE, Phase.CREATE, true);
|
||||
util.setUpClusterWide(DEFAULT_NAMESPACE, Set.of(DEFAULT_NAMESPACE, LEFT_NAMESPACE, RIGHT_NAMESPACE));
|
||||
configWatcher(Phase.CREATE);
|
||||
}
|
||||
@@ -68,7 +68,7 @@ class ActuatorRefreshMultipleNamespacesIT {
|
||||
@AfterAll
|
||||
static void afterAll() {
|
||||
configWatcher(Phase.DELETE);
|
||||
util.wiremock(DEFAULT_NAMESPACE, "/", Phase.DELETE);
|
||||
util.wiremock(DEFAULT_NAMESPACE, Phase.DELETE, true);
|
||||
util.deleteClusterWide(DEFAULT_NAMESPACE, Set.of(DEFAULT_NAMESPACE, LEFT_NAMESPACE, RIGHT_NAMESPACE));
|
||||
util.deleteNamespace(LEFT_NAMESPACE);
|
||||
util.deleteNamespace(RIGHT_NAMESPACE);
|
||||
@@ -128,10 +128,10 @@ class ActuatorRefreshMultipleNamespacesIT {
|
||||
.yaml("config-watcher/spring-cloud-kubernetes-configuration-watcher-service.yaml");
|
||||
|
||||
if (phase.equals(Phase.CREATE)) {
|
||||
util.createAndWait(DEFAULT_NAMESPACE, null, deployment, service, null, true);
|
||||
util.createAndWait(DEFAULT_NAMESPACE, null, deployment, service, true);
|
||||
}
|
||||
else {
|
||||
util.deleteAndWait(DEFAULT_NAMESPACE, deployment, service, null);
|
||||
util.deleteAndWait(DEFAULT_NAMESPACE, deployment, service);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ final class TestUtil {
|
||||
|
||||
private static final String WIREMOCK_HOST = "localhost";
|
||||
|
||||
private static final int WIREMOCK_PORT = 80;
|
||||
private static final int WIREMOCK_PORT = 32321;
|
||||
|
||||
static final String SPRING_CLOUD_K8S_CONFIG_WATCHER_APP_NAME = "spring-cloud-kubernetes-configuration-watcher";
|
||||
|
||||
@@ -60,7 +60,7 @@ final class TestUtil {
|
||||
// is ready to take a request via 'Wiremock::stubFor' (because sometimes it fails)
|
||||
// As such, get the existing mappings and retrySpec() makes sure we retry until
|
||||
// we get a response back.
|
||||
WebClient client = builder().baseUrl("http://localhost:80/__admin/mappings").build();
|
||||
WebClient client = builder().baseUrl("http://localhost:32321/__admin/mappings").build();
|
||||
client.method(HttpMethod.GET).retrieve().bodyToMono(String.class).retryWhen(retrySpec()).block();
|
||||
|
||||
StubMapping stubMapping = WireMock.stubFor(WireMock.post(WireMock.urlEqualTo("/actuator/refresh"))
|
||||
|
||||
@@ -93,10 +93,10 @@ abstract class DiscoveryServerClientBase {
|
||||
V1Service service = (V1Service) util.yaml("manifests/discoveryserver-service.yaml");
|
||||
|
||||
if (phase.equals(Phase.CREATE)) {
|
||||
util.createAndWait(NAMESPACE, null, deployment, service, null, true);
|
||||
util.createAndWait(NAMESPACE, null, deployment, service, true);
|
||||
}
|
||||
else {
|
||||
util.deleteAndWait(NAMESPACE, deployment, service, null);
|
||||
util.deleteAndWait(NAMESPACE, deployment, service);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -78,8 +78,8 @@ class DiscoveryServerClientIT extends DiscoveryServerClientBase {
|
||||
discoveryServer(Phase.CREATE);
|
||||
|
||||
Images.loadWiremock(K3S);
|
||||
util.wiremock(NAMESPACE_LEFT, "/wiremock-" + NAMESPACE_LEFT, Phase.CREATE, false);
|
||||
util.wiremock(NAMESPACE_RIGHT, "/wiremock-" + NAMESPACE_RIGHT, Phase.CREATE, false);
|
||||
util.wiremock(NAMESPACE_LEFT, Phase.CREATE, false);
|
||||
util.wiremock(NAMESPACE_RIGHT, Phase.CREATE, false);
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
@@ -87,8 +87,8 @@ class DiscoveryServerClientIT extends DiscoveryServerClientBase {
|
||||
serviceAccount(Phase.DELETE);
|
||||
discoveryServer(Phase.DELETE);
|
||||
|
||||
util.wiremock(NAMESPACE_LEFT, "/wiremock-" + NAMESPACE_LEFT, Phase.DELETE, false);
|
||||
util.wiremock(NAMESPACE_RIGHT, "/wiremock-" + NAMESPACE_RIGHT, Phase.DELETE, false);
|
||||
util.wiremock(NAMESPACE_LEFT, Phase.DELETE, false);
|
||||
util.wiremock(NAMESPACE_RIGHT, Phase.DELETE, false);
|
||||
|
||||
util.deleteNamespace(NAMESPACE_LEFT);
|
||||
util.deleteNamespace(NAMESPACE_RIGHT);
|
||||
|
||||
@@ -60,12 +60,12 @@ class KubernetesClientBlockingIT extends KubernetesClientDiscoveryBase {
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
Images.loadWiremock(K3S);
|
||||
util.wiremock(NAMESPACE, "/", Phase.CREATE);
|
||||
util.wiremock(NAMESPACE, Phase.CREATE, true);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void afterEach() {
|
||||
util.wiremock(NAMESPACE, "/", Phase.DELETE);
|
||||
util.wiremock(NAMESPACE, Phase.DELETE, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -63,14 +63,14 @@ class KubernetesClientDiscoveryFilterIT extends KubernetesClientDiscoveryBase {
|
||||
util.createNamespace(NAMESPACE_B_UAT);
|
||||
|
||||
Images.loadWiremock(K3S);
|
||||
util.wiremock(NAMESPACE_A_UAT, "/", Phase.CREATE);
|
||||
util.wiremock(NAMESPACE_B_UAT, "/", Phase.CREATE);
|
||||
util.wiremock(NAMESPACE_A_UAT, Phase.CREATE, false);
|
||||
util.wiremock(NAMESPACE_B_UAT, Phase.CREATE, false);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void afterEach() {
|
||||
util.wiremock(NAMESPACE_A_UAT, "/", Phase.DELETE);
|
||||
util.wiremock(NAMESPACE_B_UAT, "/", Phase.DELETE);
|
||||
util.wiremock(NAMESPACE_A_UAT, Phase.DELETE, false);
|
||||
util.wiremock(NAMESPACE_B_UAT, Phase.DELETE, false);
|
||||
|
||||
util.deleteNamespace(NAMESPACE_A_UAT);
|
||||
util.deleteNamespace(NAMESPACE_B_UAT);
|
||||
|
||||
@@ -64,13 +64,13 @@ class KubernetesClientDiscoverySimpleIT extends KubernetesClientDiscoveryBase {
|
||||
util.busybox(NAMESPACE, Phase.CREATE);
|
||||
|
||||
externalNameService = (V1Service) util.yaml("external-name-service.yaml");
|
||||
util.createAndWait(NAMESPACE, null, null, externalNameService, null, true);
|
||||
util.createAndWait(NAMESPACE, null, null, externalNameService, true);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void afterEach() {
|
||||
util.busybox(NAMESPACE, Phase.DELETE);
|
||||
util.deleteAndWait(NAMESPACE, null, externalNameService, null);
|
||||
util.deleteAndWait(NAMESPACE, null, externalNameService);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -60,12 +60,12 @@ class KubernetesClientReactiveIT extends KubernetesClientDiscoveryBase {
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
Images.loadWiremock(K3S);
|
||||
util.wiremock(NAMESPACE, "/", Phase.CREATE);
|
||||
util.wiremock(NAMESPACE, Phase.CREATE, true);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void afterEach() {
|
||||
util.wiremock(NAMESPACE, "/", Phase.DELETE);
|
||||
util.wiremock(NAMESPACE, Phase.DELETE, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -133,8 +133,8 @@ final class TestAssertions {
|
||||
assertThat(wiremockInstance.getServiceId()).isEqualTo("service-wiremock");
|
||||
assertThat(wiremockInstance.getInstanceId()).isNotNull();
|
||||
assertThat(wiremockInstance.getHost()).isNotNull();
|
||||
assertThat(wiremockInstance.getMetadata()).isEqualTo(Map.of("k8s_namespace", "default", "type", "ClusterIP",
|
||||
"port.http", "8080", "app", "service-wiremock"));
|
||||
assertThat(wiremockInstance.getMetadata()).isEqualTo(
|
||||
Map.of("k8s_namespace", "default", "type", "NodePort", "port.http", "8080", "app", "service-wiremock"));
|
||||
|
||||
}
|
||||
|
||||
@@ -147,8 +147,8 @@ final class TestAssertions {
|
||||
assertThat(wiremockInstance.getServiceId()).isEqualTo("service-wiremock");
|
||||
assertThat(wiremockInstance.getInstanceId()).isNotNull();
|
||||
assertThat(wiremockInstance.getHost()).isNotNull();
|
||||
assertThat(wiremockInstance.getMetadata()).isEqualTo(Map.of("k8s_namespace", "default", "type", "ClusterIP",
|
||||
"port.http", "8080", "app", "service-wiremock"));
|
||||
assertThat(wiremockInstance.getMetadata()).isEqualTo(
|
||||
Map.of("k8s_namespace", "default", "type", "NodePort", "port.http", "8080", "app", "service-wiremock"));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ import java.util.Objects;
|
||||
import io.kubernetes.client.openapi.models.V1ConfigMap;
|
||||
import io.kubernetes.client.openapi.models.V1ConfigMapBuilder;
|
||||
import io.kubernetes.client.openapi.models.V1Deployment;
|
||||
import io.kubernetes.client.openapi.models.V1Ingress;
|
||||
import io.kubernetes.client.openapi.models.V1Service;
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
@@ -153,7 +152,7 @@ class ConfigurationWatcherBusKafkaIT {
|
||||
util.createAndWait(NAMESPACE, configMap, null);
|
||||
|
||||
WebClient.Builder builder = builder();
|
||||
WebClient serviceClient = builder.baseUrl("http://localhost:80/app").build();
|
||||
WebClient serviceClient = builder.baseUrl("http://localhost:32321/app").build();
|
||||
|
||||
Boolean[] value = new Boolean[1];
|
||||
await().pollInterval(Duration.ofSeconds(3)).atMost(Duration.ofSeconds(240)).until(() -> {
|
||||
@@ -173,13 +172,12 @@ class ConfigurationWatcherBusKafkaIT {
|
||||
private void app(Phase phase) {
|
||||
V1Deployment deployment = (V1Deployment) util.yaml("app/app-deployment.yaml");
|
||||
V1Service service = (V1Service) util.yaml("app/app-service.yaml");
|
||||
V1Ingress ingress = (V1Ingress) util.yaml("ingress/ingress.yaml");
|
||||
|
||||
if (phase.equals(Phase.CREATE)) {
|
||||
util.createAndWait(NAMESPACE, null, deployment, service, ingress, true);
|
||||
util.createAndWait(NAMESPACE, null, deployment, service, true);
|
||||
}
|
||||
else if (phase.equals(Phase.DELETE)) {
|
||||
util.deleteAndWait(NAMESPACE, deployment, service, ingress);
|
||||
util.deleteAndWait(NAMESPACE, deployment, service);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,10 +186,10 @@ class ConfigurationWatcherBusKafkaIT {
|
||||
V1Service service = (V1Service) util.yaml("config-watcher/watcher-kus-kafka-service.yaml");
|
||||
|
||||
if (phase.equals(Phase.CREATE)) {
|
||||
util.createAndWait(NAMESPACE, null, deployment, service, null, true);
|
||||
util.createAndWait(NAMESPACE, null, deployment, service, true);
|
||||
}
|
||||
else if (phase.equals(Phase.DELETE)) {
|
||||
util.deleteAndWait(NAMESPACE, deployment, service, null);
|
||||
util.deleteAndWait(NAMESPACE, deployment, service);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ spec:
|
||||
- name: http
|
||||
port: 8081
|
||||
targetPort: 8081
|
||||
nodePort: 32321
|
||||
selector:
|
||||
app: app
|
||||
type: ClusterIP
|
||||
type: NodePort
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: it-ingress-app
|
||||
namespace: default
|
||||
spec:
|
||||
rules:
|
||||
- http:
|
||||
paths:
|
||||
- path: /app
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: app
|
||||
port:
|
||||
number: 8081
|
||||
@@ -20,7 +20,6 @@ import java.time.Duration;
|
||||
import java.util.Objects;
|
||||
|
||||
import io.kubernetes.client.openapi.models.V1Deployment;
|
||||
import io.kubernetes.client.openapi.models.V1Ingress;
|
||||
import io.kubernetes.client.openapi.models.V1Secret;
|
||||
import io.kubernetes.client.openapi.models.V1SecretBuilder;
|
||||
import io.kubernetes.client.openapi.models.V1Service;
|
||||
@@ -103,7 +102,7 @@ class ConfigurationWatcherBusAmqpIT {
|
||||
util.createAndWait(NAMESPACE, null, secret);
|
||||
|
||||
WebClient.Builder builder = builder();
|
||||
WebClient serviceClient = builder.baseUrl("http://localhost:80/app").build();
|
||||
WebClient serviceClient = builder.baseUrl("http://localhost:32321/app").build();
|
||||
|
||||
Boolean[] value = new Boolean[1];
|
||||
await().pollInterval(Duration.ofSeconds(3)).atMost(Duration.ofSeconds(240)).until(() -> {
|
||||
@@ -122,13 +121,12 @@ class ConfigurationWatcherBusAmqpIT {
|
||||
private void appA(Phase phase) {
|
||||
V1Deployment deployment = (V1Deployment) util.yaml("app/app-deployment.yaml");
|
||||
V1Service service = (V1Service) util.yaml("app/app-service.yaml");
|
||||
V1Ingress ingress = (V1Ingress) util.yaml("ingress/ingress.yaml");
|
||||
|
||||
if (phase.equals(Phase.CREATE)) {
|
||||
util.createAndWait(NAMESPACE, null, deployment, service, ingress, true);
|
||||
util.createAndWait(NAMESPACE, null, deployment, service, true);
|
||||
}
|
||||
else if (phase.equals(Phase.DELETE)) {
|
||||
util.deleteAndWait(NAMESPACE, deployment, service, ingress);
|
||||
util.deleteAndWait(NAMESPACE, deployment, service);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,10 +135,10 @@ class ConfigurationWatcherBusAmqpIT {
|
||||
V1Service service = (V1Service) util.yaml("config-watcher/watcher-service.yaml");
|
||||
|
||||
if (phase.equals(Phase.CREATE)) {
|
||||
util.createAndWait(NAMESPACE, null, deployment, service, null, true);
|
||||
util.createAndWait(NAMESPACE, null, deployment, service, true);
|
||||
}
|
||||
else if (phase.equals(Phase.DELETE)) {
|
||||
util.deleteAndWait(NAMESPACE, deployment, service, null);
|
||||
util.deleteAndWait(NAMESPACE, deployment, service);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ spec:
|
||||
- name: http
|
||||
port: 8080
|
||||
targetPort: 8080
|
||||
nodePort: 32321
|
||||
selector:
|
||||
app: app
|
||||
type: ClusterIP
|
||||
type: NodePort
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: it-ingress
|
||||
namespace: default
|
||||
spec:
|
||||
rules:
|
||||
- http:
|
||||
paths:
|
||||
- path: /app
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: app
|
||||
port:
|
||||
number: 8080
|
||||
@@ -105,11 +105,11 @@ abstract class K8sClientReloadBase {
|
||||
|
||||
if (phase.equals(Phase.CREATE)) {
|
||||
util.createAndWait(namespace, configMap, null);
|
||||
util.createAndWait(namespace, imageName, deployment, service, null, true);
|
||||
util.createAndWait(namespace, imageName, deployment, service, true);
|
||||
}
|
||||
else {
|
||||
util.deleteAndWait(namespace, configMap, null);
|
||||
util.deleteAndWait(namespace, deployment, service, null);
|
||||
util.deleteAndWait(namespace, deployment, service);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -122,11 +122,11 @@ abstract class K8sClientReloadBase {
|
||||
|
||||
if (phase.equals(Phase.CREATE)) {
|
||||
util.createAndWait(namespace, null, secret);
|
||||
util.createAndWait(namespace, imageName, deployment, service, null, true);
|
||||
util.createAndWait(namespace, imageName, deployment, service, true);
|
||||
}
|
||||
else {
|
||||
util.deleteAndWait(namespace, null, secret);
|
||||
util.deleteAndWait(namespace, deployment, service, null);
|
||||
util.deleteAndWait(namespace, deployment, service);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright 2013-2025 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.k8s.client.reload.it;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.Map;
|
||||
|
||||
import io.kubernetes.client.openapi.apis.CoreV1Api;
|
||||
import io.kubernetes.client.openapi.models.V1Secret;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.testcontainers.k3s.K3sContainer;
|
||||
|
||||
import org.springframework.cloud.kubernetes.integration.tests.commons.Commons;
|
||||
import org.springframework.cloud.kubernetes.integration.tests.commons.Phase;
|
||||
import org.springframework.cloud.kubernetes.integration.tests.commons.native_client.Util;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.awaitility.Awaitility.await;
|
||||
import static org.springframework.cloud.kubernetes.integration.tests.commons.Commons.builder;
|
||||
import static org.springframework.cloud.kubernetes.integration.tests.commons.Commons.retrySpec;
|
||||
|
||||
/**
|
||||
* @author wind57
|
||||
*/
|
||||
class K8sClientSecretMountBootstrapPollingIT extends K8sClientReloadBase {
|
||||
|
||||
private static final String IMAGE_NAME = "spring-cloud-kubernetes-k8s-client-reload";
|
||||
|
||||
private static final String NAMESPACE = "default";
|
||||
|
||||
private static final K3sContainer K3S = Commons.container();
|
||||
|
||||
private static Util util;
|
||||
|
||||
private static CoreV1Api coreV1Api;
|
||||
|
||||
@BeforeAll
|
||||
static void beforeAllLocal() throws Exception {
|
||||
K3S.start();
|
||||
Commons.validateImage(IMAGE_NAME, K3S);
|
||||
Commons.loadSpringCloudKubernetesImage(IMAGE_NAME, K3S);
|
||||
|
||||
util = new Util(K3S);
|
||||
coreV1Api = new CoreV1Api();
|
||||
util.setUp(NAMESPACE);
|
||||
manifestsSecret(Phase.CREATE, util, NAMESPACE, IMAGE_NAME);
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void afterAll() {
|
||||
manifestsSecret(Phase.DELETE, util, NAMESPACE, IMAGE_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* - we have bootstrap enabled
|
||||
* - we will 'locate' property sources from secrets.
|
||||
* - there are no explicit secrets to search for, but what we will also read,
|
||||
* is 'spring.cloud.kubernetes.secret.paths', which we have set to
|
||||
* '/tmp/application.properties'
|
||||
* in this test. That is populated by the volumeMounts (see mount/deployment-with-secret.yaml)
|
||||
* - we first assert that we are actually reading the path based source
|
||||
*
|
||||
* - we then change the secret content, wait for k8s to pick it up and replace them
|
||||
* - our polling will then detect that change, and trigger a reload.
|
||||
* </pre>
|
||||
*/
|
||||
@Test
|
||||
void test() throws Exception {
|
||||
WebClient webClient = builder().baseUrl("http://localhost:32321/secret").build();
|
||||
String result = webClient.method(HttpMethod.GET)
|
||||
.retrieve()
|
||||
.bodyToMono(String.class)
|
||||
.retryWhen(retrySpec())
|
||||
.block();
|
||||
|
||||
// we first read the initial value from the configmap
|
||||
assertThat(result).isEqualTo("initial");
|
||||
|
||||
// replace data in secret and wait for k8s to pick it up
|
||||
// our polling will detect that and restart the app
|
||||
V1Secret secret = (V1Secret) util.yaml("mount/secret.yaml");
|
||||
secret.setData(Map.of("from.properties.secret.key", "as-mount-changed".getBytes(StandardCharsets.UTF_8)));
|
||||
coreV1Api.replaceNamespacedSecret("secret-reload", NAMESPACE, secret, null, null, null, null);
|
||||
|
||||
Commons.waitForLogStatement("Detected change in config maps/secrets, reload will be triggered", K3S,
|
||||
IMAGE_NAME);
|
||||
|
||||
await().atMost(Duration.ofSeconds(120))
|
||||
.pollInterval(Duration.ofSeconds(1))
|
||||
.until(() -> webClient.method(HttpMethod.GET)
|
||||
.retrieve()
|
||||
.bodyToMono(String.class)
|
||||
.retryWhen(retrySpec())
|
||||
.block()
|
||||
.equals("as-mount-changed"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -295,7 +295,12 @@ public final class Util {
|
||||
});
|
||||
}
|
||||
|
||||
public void wiremock(String namespace, Phase phase) {
|
||||
/**
|
||||
* 'withNodePort' specifies if we add the NodePort or not. It is needed because we
|
||||
* sometimes deploy two instances of wiremock, and they can't have the same NodePort
|
||||
* exposed
|
||||
*/
|
||||
public void wiremock(String namespace, Phase phase, boolean withNodePort) {
|
||||
InputStream deploymentStream = inputStream("wiremock/wiremock-deployment.yaml");
|
||||
InputStream serviceStream = inputStream("wiremock/wiremock-service.yaml");
|
||||
|
||||
@@ -305,6 +310,11 @@ public final class Util {
|
||||
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setImage(imageWithVersion);
|
||||
|
||||
Service service = client.services().load(serviceStream).item();
|
||||
if (!withNodePort) {
|
||||
// we assume we only have one 'http' port
|
||||
service.getSpec().getPorts().get(0).setNodePort(null);
|
||||
service.getSpec().setType("ClusterIP");
|
||||
}
|
||||
|
||||
if (phase.equals(Phase.CREATE)) {
|
||||
deployment.getMetadata().setNamespace(namespace);
|
||||
|
||||
@@ -29,25 +29,19 @@ import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import io.kubernetes.client.custom.V1Patch;
|
||||
import io.kubernetes.client.openapi.ApiClient;
|
||||
import io.kubernetes.client.openapi.ApiException;
|
||||
import io.kubernetes.client.openapi.Configuration;
|
||||
import io.kubernetes.client.openapi.apis.ApiregistrationV1Api;
|
||||
import io.kubernetes.client.openapi.apis.AppsV1Api;
|
||||
import io.kubernetes.client.openapi.apis.CoreV1Api;
|
||||
import io.kubernetes.client.openapi.apis.NetworkingV1Api;
|
||||
import io.kubernetes.client.openapi.apis.RbacAuthorizationV1Api;
|
||||
import io.kubernetes.client.openapi.models.V1APIService;
|
||||
import io.kubernetes.client.openapi.models.V1ClusterRole;
|
||||
import io.kubernetes.client.openapi.models.V1ClusterRoleBinding;
|
||||
import io.kubernetes.client.openapi.models.V1ConfigMap;
|
||||
import io.kubernetes.client.openapi.models.V1Deployment;
|
||||
import io.kubernetes.client.openapi.models.V1DeploymentCondition;
|
||||
import io.kubernetes.client.openapi.models.V1DeploymentList;
|
||||
import io.kubernetes.client.openapi.models.V1Ingress;
|
||||
import io.kubernetes.client.openapi.models.V1IngressLoadBalancerIngress;
|
||||
import io.kubernetes.client.openapi.models.V1IngressLoadBalancerStatus;
|
||||
import io.kubernetes.client.openapi.models.V1NamespaceBuilder;
|
||||
import io.kubernetes.client.openapi.models.V1Role;
|
||||
import io.kubernetes.client.openapi.models.V1RoleBinding;
|
||||
@@ -55,7 +49,6 @@ import io.kubernetes.client.openapi.models.V1Secret;
|
||||
import io.kubernetes.client.openapi.models.V1Service;
|
||||
import io.kubernetes.client.openapi.models.V1ServiceAccount;
|
||||
import io.kubernetes.client.util.Config;
|
||||
import io.kubernetes.client.util.PatchUtils;
|
||||
import io.kubernetes.client.util.Yaml;
|
||||
import jakarta.annotation.Nullable;
|
||||
import org.apache.commons.logging.Log;
|
||||
@@ -82,8 +75,6 @@ public final class Util {
|
||||
|
||||
private final AppsV1Api appsV1Api;
|
||||
|
||||
private final NetworkingV1Api networkingV1Api;
|
||||
|
||||
private final RbacAuthorizationV1Api rbacApi;
|
||||
|
||||
private final K3sContainer container;
|
||||
@@ -104,7 +95,6 @@ public final class Util {
|
||||
this.container = container;
|
||||
this.coreV1Api = new CoreV1Api();
|
||||
this.appsV1Api = new AppsV1Api();
|
||||
this.networkingV1Api = new NetworkingV1Api();
|
||||
rbacApi = new RbacAuthorizationV1Api();
|
||||
}
|
||||
|
||||
@@ -116,7 +106,7 @@ public final class Util {
|
||||
*
|
||||
*/
|
||||
public void createAndWait(String namespace, String name, V1Deployment deployment, V1Service service,
|
||||
@Nullable V1Ingress ingress, boolean changeVersion) {
|
||||
boolean changeVersion) {
|
||||
try {
|
||||
|
||||
coreV1Api.createNamespacedService(namespace, service, null, null, null, null);
|
||||
@@ -146,10 +136,6 @@ public final class Util {
|
||||
waitForDeployment(namespace, deployment);
|
||||
}
|
||||
|
||||
if (ingress != null) {
|
||||
networkingV1Api.createNamespacedIngress(namespace, ingress, null, null, null, null);
|
||||
waitForIngress(namespace, ingress);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
if (e instanceof ApiException apiException) {
|
||||
@@ -207,8 +193,7 @@ public final class Util {
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteAndWait(String namespace, V1Deployment deployment, V1Service service,
|
||||
@Nullable V1Ingress ingress) {
|
||||
public void deleteAndWait(String namespace, V1Deployment deployment, V1Service service) {
|
||||
|
||||
if (deployment != null) {
|
||||
try {
|
||||
@@ -223,26 +208,16 @@ public final class Util {
|
||||
labelSelector(podLabels), null, null, null, null, null, null, null, null);
|
||||
waitForDeploymentToBeDeleted(deploymentName, namespace);
|
||||
waitForDeploymentPodsToBeDeleted(podLabels, namespace);
|
||||
|
||||
service.getMetadata().setNamespace(namespace);
|
||||
coreV1Api.deleteNamespacedService(service.getMetadata().getName(), service.getMetadata().getNamespace(),
|
||||
null, null, null, null, null, null);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
String serviceName = serviceName(service);
|
||||
try {
|
||||
coreV1Api.deleteNamespacedService(serviceName, namespace, null, null, null, null, null, null);
|
||||
if (ingress != null) {
|
||||
String ingressName = ingressName(ingress);
|
||||
networkingV1Api.deleteNamespacedIngress(ingressName, namespace, null, null, null, null, null, null);
|
||||
waitForIngressToBeDeleted(ingressName, namespace);
|
||||
}
|
||||
|
||||
}
|
||||
catch (ApiException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void busybox(String namespace, Phase phase) {
|
||||
@@ -254,10 +229,10 @@ public final class Util {
|
||||
|
||||
V1Service service = (V1Service) yaml("busybox/service.yaml");
|
||||
if (phase.equals(Phase.CREATE)) {
|
||||
createAndWait(namespace, "busybox", deployment, service, null, false);
|
||||
createAndWait(namespace, "busybox", deployment, service, false);
|
||||
}
|
||||
else if (phase.equals(Phase.DELETE)) {
|
||||
deleteAndWait(namespace, deployment, service, null);
|
||||
deleteAndWait(namespace, deployment, service);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,10 +246,10 @@ public final class Util {
|
||||
V1Service service = (V1Service) yaml("kafka/kafka-service.yaml");
|
||||
|
||||
if (phase.equals(Phase.CREATE)) {
|
||||
createAndWait(namespace, "kafka", deployment, service, null, false);
|
||||
createAndWait(namespace, "kafka", deployment, service, false);
|
||||
}
|
||||
else if (phase.equals(Phase.DELETE)) {
|
||||
deleteAndWait(namespace, deployment, service, null);
|
||||
deleteAndWait(namespace, deployment, service);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,10 +263,10 @@ public final class Util {
|
||||
V1Service service = (V1Service) yaml("rabbitmq/rabbitmq-service.yaml");
|
||||
|
||||
if (phase.equals(Phase.CREATE)) {
|
||||
createAndWait(namespace, "rabbitmq", deployment, service, null, false);
|
||||
createAndWait(namespace, "rabbitmq", deployment, service, false);
|
||||
}
|
||||
else if (phase.equals(Phase.DELETE)) {
|
||||
deleteAndWait(namespace, deployment, service, null);
|
||||
deleteAndWait(namespace, deployment, service);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -335,49 +310,6 @@ public final class Util {
|
||||
|
||||
}
|
||||
|
||||
public void setUpClusterWideClusterRoleBinding(String serviceAccountNamespace) {
|
||||
|
||||
try {
|
||||
V1ServiceAccount serviceAccount = (V1ServiceAccount) yaml("cluster/service-account.yaml");
|
||||
CheckedSupplier<V1ServiceAccount> accountSupplier = () -> coreV1Api
|
||||
.readNamespacedServiceAccount(serviceAccount.getMetadata().getName(), serviceAccountNamespace, null);
|
||||
CheckedSupplier<V1ServiceAccount> accountDefaulter = () -> coreV1Api
|
||||
.createNamespacedServiceAccount(serviceAccountNamespace, serviceAccount, null, null, null, null);
|
||||
notExistsHandler(accountSupplier, accountDefaulter);
|
||||
|
||||
V1ClusterRole clusterRole = (V1ClusterRole) yaml("cluster/cluster-role.yaml");
|
||||
notExistsHandler(() -> rbacApi.readClusterRole(clusterRole.getMetadata().getName(), null),
|
||||
() -> rbacApi.createClusterRole(clusterRole, null, null, null, null));
|
||||
|
||||
V1ClusterRoleBinding clusterRoleBinding = (V1ClusterRoleBinding) yaml("cluster/cluster-role-binding.yaml");
|
||||
notExistsHandler(() -> rbacApi.readClusterRoleBinding(clusterRoleBinding.getMetadata().getName(), null),
|
||||
() -> rbacApi.createClusterRoleBinding(clusterRoleBinding, null, null, null, null));
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void deleteClusterWideClusterRoleBinding(String serviceAccountNamespace) {
|
||||
try {
|
||||
V1ServiceAccount serviceAccount = (V1ServiceAccount) yaml("cluster/service-account.yaml");
|
||||
V1ClusterRole clusterRole = (V1ClusterRole) yaml("cluster/cluster-role.yaml");
|
||||
V1ClusterRoleBinding clusterRoleBinding = (V1ClusterRoleBinding) yaml("cluster/cluster-role-binding.yaml");
|
||||
|
||||
coreV1Api.deleteNamespacedServiceAccount(serviceAccount.getMetadata().getName(), serviceAccountNamespace,
|
||||
null, null, null, null, null, null);
|
||||
rbacApi.deleteClusterRole(clusterRole.getMetadata().getName(), null, null, null, null, null, null);
|
||||
rbacApi.deleteClusterRoleBinding(clusterRoleBinding.getMetadata().getName(), null, null, null, null, null,
|
||||
null);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setUpClusterWide(String serviceAccountNamespace, Set<String> namespaces) {
|
||||
|
||||
try {
|
||||
@@ -481,11 +413,7 @@ public final class Util {
|
||||
.noneMatch(x -> x.getMetadata().getName().equals(name)));
|
||||
}
|
||||
|
||||
public void wiremock(String namespace, String path, Phase phase) {
|
||||
wiremock(namespace, path, phase, true);
|
||||
}
|
||||
|
||||
public void wiremock(String namespace, String path, Phase phase, boolean withIngress) {
|
||||
public void wiremock(String namespace, Phase phase, boolean withNodePort) {
|
||||
V1Deployment deployment = (V1Deployment) yaml("wiremock/wiremock-deployment.yaml");
|
||||
|
||||
String imageWithoutVersion = deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getImage();
|
||||
@@ -493,77 +421,28 @@ public final class Util {
|
||||
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setImage(imageWithVersion);
|
||||
|
||||
V1Service service = (V1Service) yaml("wiremock/wiremock-service.yaml");
|
||||
|
||||
V1Ingress ingress = null;
|
||||
service.getMetadata().setNamespace(namespace);
|
||||
if (!withNodePort) {
|
||||
// we assume we only have one 'http' port
|
||||
service.getSpec().getPorts().get(0).setNodePort(null);
|
||||
service.getSpec().setType("ClusterIP");
|
||||
}
|
||||
|
||||
if (phase.equals(Phase.CREATE)) {
|
||||
|
||||
if (withIngress) {
|
||||
ingress = (V1Ingress) yaml("wiremock/wiremock-ingress.yaml");
|
||||
ingress.getMetadata().setNamespace(namespace);
|
||||
ingress.getSpec().getRules().get(0).getHttp().getPaths().get(0).setPath(path);
|
||||
}
|
||||
|
||||
deployment.getMetadata().setNamespace(namespace);
|
||||
service.getMetadata().setNamespace(namespace);
|
||||
createAndWait(namespace, "wiremock", deployment, service, ingress, false);
|
||||
createAndWait(namespace, "wiremock", deployment, service, false);
|
||||
}
|
||||
else {
|
||||
if (withIngress) {
|
||||
ingress = (V1Ingress) yaml("wiremock/wiremock-ingress.yaml");
|
||||
}
|
||||
deleteAndWait(namespace, deployment, service, ingress);
|
||||
deleteAndWait(namespace, deployment, service);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void patchWithMerge(String deploymentName, String namespace, String patchBody,
|
||||
Map<String, String> podLabels) {
|
||||
try {
|
||||
PatchUtils.patch(V1Deployment.class,
|
||||
() -> new AppsV1Api().patchNamespacedDeploymentCall(deploymentName, namespace,
|
||||
new V1Patch(patchBody), null, null, null, null, null, null),
|
||||
V1Patch.PATCH_FORMAT_STRATEGIC_MERGE_PATCH, new CoreV1Api().getApiClient());
|
||||
}
|
||||
catch (ApiException e) {
|
||||
LOG.error("error : " + e.getResponseBody());
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
waitForDeploymentAfterPatch(deploymentName, namespace, podLabels);
|
||||
}
|
||||
|
||||
public static void patchWithReplace(String imageName, String deploymentName, String namespace, String patchBody,
|
||||
Map<String, String> podLabels) {
|
||||
String body = patchBody.replace("image_name_here", imageName);
|
||||
|
||||
try {
|
||||
PatchUtils.patch(V1Deployment.class,
|
||||
() -> new AppsV1Api().patchNamespacedDeploymentCall(deploymentName, namespace, new V1Patch(body),
|
||||
null, null, null, null, null, null),
|
||||
V1Patch.PATCH_FORMAT_JSON_MERGE_PATCH, new CoreV1Api().getApiClient());
|
||||
}
|
||||
catch (ApiException e) {
|
||||
LOG.error("error : " + e.getResponseBody());
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
waitForDeploymentAfterPatch(deploymentName, namespace, podLabels);
|
||||
|
||||
}
|
||||
|
||||
private String deploymentName(V1Deployment deployment) {
|
||||
return deployment.getMetadata().getName();
|
||||
}
|
||||
|
||||
private String serviceName(V1Service service) {
|
||||
return service.getMetadata().getName();
|
||||
}
|
||||
|
||||
private String ingressName(V1Ingress ingress) {
|
||||
return ingress.getMetadata().getName();
|
||||
}
|
||||
|
||||
private String configMapName(V1ConfigMap configMap) {
|
||||
return configMap.getMetadata().getName();
|
||||
}
|
||||
@@ -613,43 +492,6 @@ public final class Util {
|
||||
});
|
||||
}
|
||||
|
||||
private void waitForIngress(String namespace, V1Ingress ingress) {
|
||||
String ingressName = ingressName(ingress);
|
||||
await().timeout(Duration.ofSeconds(90)).pollInterval(Duration.ofSeconds(3)).until(() -> {
|
||||
try {
|
||||
V1IngressLoadBalancerStatus status = networkingV1Api.readNamespacedIngress(ingressName, namespace, null)
|
||||
.getStatus()
|
||||
.getLoadBalancer();
|
||||
|
||||
if (status == null) {
|
||||
LOG.info("ingress : " + ingressName + " not ready yet (loadbalancer not yet present)");
|
||||
return false;
|
||||
}
|
||||
|
||||
List<V1IngressLoadBalancerIngress> loadBalancerIngress = status.getIngress();
|
||||
if (loadBalancerIngress == null) {
|
||||
LOG.info("ingress : " + ingressName + " not ready yet (loadbalancer ingress not yet present)");
|
||||
return false;
|
||||
}
|
||||
|
||||
String ip = loadBalancerIngress.get(0).getIp();
|
||||
if (ip == null) {
|
||||
LOG.info("ingress : " + ingressName + " not ready yet");
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG.info("ingress : " + ingressName + " ready with ip : " + ip);
|
||||
return true;
|
||||
}
|
||||
catch (ApiException e) {
|
||||
if (e.getCode() == HttpURLConnection.HTTP_NOT_FOUND) {
|
||||
return false;
|
||||
}
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void waitForDeploymentToBeDeleted(String deploymentName, String namespace) {
|
||||
await().timeout(Duration.ofSeconds(180)).until(() -> {
|
||||
try {
|
||||
@@ -684,21 +526,6 @@ public final class Util {
|
||||
});
|
||||
}
|
||||
|
||||
private void waitForIngressToBeDeleted(String ingressName, String namespace) {
|
||||
await().timeout(Duration.ofSeconds(90)).until(() -> {
|
||||
try {
|
||||
networkingV1Api.readNamespacedIngress(ingressName, namespace, null);
|
||||
return false;
|
||||
}
|
||||
catch (ApiException e) {
|
||||
if (e.getCode() == HttpURLConnection.HTTP_NOT_FOUND) {
|
||||
return true;
|
||||
}
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean isDeploymentReady(String deploymentName, String namespace) throws ApiException {
|
||||
V1DeploymentList deployments = appsV1Api.listNamespacedDeployment(namespace, null, null, null,
|
||||
"metadata.name=" + deploymentName, null, null, null, null, null, null, null);
|
||||
@@ -729,24 +556,6 @@ public final class Util {
|
||||
}
|
||||
}
|
||||
|
||||
private static void waitForDeploymentAfterPatch(String deploymentName, String namespace,
|
||||
Map<String, String> podLabels) {
|
||||
try {
|
||||
await().pollDelay(Duration.ofSeconds(4))
|
||||
.pollInterval(Duration.ofSeconds(3))
|
||||
.atMost(60, TimeUnit.SECONDS)
|
||||
.until(() -> isDeploymentReadyAfterPatch(deploymentName, namespace, podLabels));
|
||||
}
|
||||
catch (Exception e) {
|
||||
if (e instanceof ApiException apiException) {
|
||||
LOG.error("Error: ");
|
||||
LOG.error(apiException.getResponseBody());
|
||||
}
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static boolean isDeploymentReadyAfterPatch(String deploymentName, String namespace,
|
||||
Map<String, String> podLabels) throws ApiException {
|
||||
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: wiremock-ingress
|
||||
namespace: default
|
||||
spec:
|
||||
rules:
|
||||
- http:
|
||||
paths:
|
||||
- path: /wiremock/
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: service-wiremock
|
||||
port:
|
||||
number: 8080
|
||||
|
||||
@@ -9,6 +9,7 @@ spec:
|
||||
- name: http
|
||||
port: 8080
|
||||
targetPort: 8080
|
||||
nodePort: 32321
|
||||
selector:
|
||||
app: service-wiremock
|
||||
type: ClusterIP
|
||||
type: NodePort
|
||||
|
||||
Reference in New Issue
Block a user