From a47a530bc39e617aaf3dcf8083b438d702bbc16b Mon Sep 17 00:00:00 2001 From: Ryan Baxter Date: Tue, 24 Nov 2020 20:25:20 -0500 Subject: [PATCH] Refactor Config Module And Add A New Config Module Using Kubernetes Client (#664) * Deprecate KubernetesAutoServiceRegistration * Document service registry in kubernetes. Fixes #348 * Refactoring config module to extract common code Renamed config module to be specific to fabric8 * Kubernetes client config module * Fixing test to avoid 503 error * Accounting for missing invocation * Adding code to deal with potential 503 error * Fixing class used in polling change listener * Formatting changes --- .../main/asciidoc/property-source-config.adoc | 4 +- pom.xml | 3 +- .../KubernetesClientAutoConfiguration.java | 34 +-- .../client/KubernetesClientUtils.java | 58 ++++ ...ClientProfileEnvironmentPostProcessor.java | 6 +- spring-cloud-kubernetes-client-config/pom.xml | 91 +++++++ ...ubernetesClientBootstrapConfiguration.java | 65 +++++ ...bernetesClientConfigMapPropertySource.java | 68 +++++ ...sClientConfigMapPropertySourceLocator.java | 53 ++++ .../config/KubernetesClientConfigUtils.java | 52 ++++ ...KubernetesClientSecretsPropertySource.java | 109 ++++++++ ...tesClientSecretsPropertySourceLocator.java | 54 ++++ ...esClientConfigReloadAutoConfiguration.java | 153 +++++++++++ ...ientEventBasedConfigMapChangeDetector.java | 141 ++++++++++ ...ClientEventBasedSecretsChangeDetector.java | 131 +++++++++ .../main/resources/META-INF/spring.factories | 4 + ...ntConfigMapPropertySourceLocatorTests.java | 125 +++++++++ ...tesClientConfigMapPropertySourceTests.java | 127 +++++++++ ...ientSecretsPropertySourceLocatorTests.java | 138 ++++++++++ ...netesClientSecretsPropertySourceTests.java | 145 ++++++++++ ...ventBasedConfigMapChangeDetectorTests.java | 159 +++++++++++ ...tEventBasedSecretsChangeDetectorTests.java | 157 +++++++++++ .../reload/KubernetesMockEnvironment.java | 55 ++++ spring-cloud-kubernetes-commons/pom.xml | 8 + .../config/AbstractConfigProperties.java | 2 +- .../config/ConfigMapConfigProperties.java | 4 +- .../config/ConfigMapPropertySource.java | 132 +++++++++ .../ConfigMapPropertySourceLocator.java | 38 ++- .../commons}/config/ConfigUtils.java | 19 +- .../kubernetes/commons}/config/Constants.java | 21 +- .../KubernetesBootstrapConfiguration.java | 31 +++ .../commons}/config/PropertySourceUtils.java | 27 +- .../config/SecretsConfigProperties.java | 4 +- .../commons/config/SecretsPropertySource.java | 54 ++++ .../config/SecretsPropertySourceLocator.java | 31 +-- .../reload/ConfigReloadAutoConfiguration.java | 96 +++++++ .../config/reload/ConfigReloadProperties.java | 2 +- .../reload/ConfigurationChangeDetector.java | 23 +- .../reload/ConfigurationUpdateStrategy.java | 2 +- .../PollingConfigMapChangeDetector.java | 26 +- .../reload/PollingSecretsChangeDetector.java | 24 +- .../condition/EventReloadDetectionMode.java | 6 +- .../condition/PollingReloadDetectionMode.java | 6 +- .../main/resources/META-INF/spring.factories | 5 +- .../config/ConfigMapPropertySource.java | 180 ------------- .../main/resources/META-INF/spring.factories | 4 - ...ntBasedConfigMapWatcherChangeDetector.java | 10 +- ...ventBasedSecretsWatcherChangeDetector.java | 10 +- .../ConfigMapWatcherChangeDetector.java | 12 +- ...ConfigurationWatcherAutoConfiguration.java | 31 +-- ...HttpBasedConfigMapWatchChangeDetector.java | 10 +- .../HttpBasedSecretsWatchChangeDetector.java | 10 +- .../watcher/SecretsWatcherChangeDetector.java | 12 +- ...edConfigMapWatcherChangeDetectorTests.java | 10 +- ...asedSecretsWatcherChangeDetectorTests.java | 10 +- ...asedConfigMapWatchChangeDetectorTests.java | 10 +- ...pBasedSecretsWatchChangeDetectorTests.java | 10 +- spring-cloud-kubernetes-dependencies/pom.xml | 8 +- .../.jdk8 | 0 .../pom.xml | 4 +- .../config/Fabric8BootstrapConfiguration.java | 20 +- .../Fabric8ConfigMapPropertySource.java | 94 +++++++ ...Fabric8ConfigMapPropertySourceLocator.java | 55 ++++ .../fabric8/config/Fabric8ConfigUtils.java | 48 ++++ .../config/Fabric8SecretsPropertySource.java | 30 +-- .../Fabric8SecretsPropertySourceLocator.java | 55 ++++ .../reload/ConfigReloadAutoConfiguration.java | 57 ++-- .../ConfigReloadDefaultAutoConfiguration.java | 79 +++--- .../EventBasedConfigMapChangeDetector.java | 33 ++- .../EventBasedSecretsChangeDetector.java | 32 ++- ...itional-spring-configuration-metadata.json | 0 .../main/resources/META-INF/spring.factories | 4 + .../fabric8}/config/ConfigMapTestUtil.java | 2 +- .../config/ConfigMapsFromFilePathsTests.java | 14 +- .../fabric8}/config/ConfigMapsMixedTests.java | 8 +- .../fabric8}/config/ConfigMapsTest.java | 24 +- .../fabric8}/config/ConfigMapsTests.java | 4 +- ...ConfigMapsWithActiveProfilesNameTests.java | 6 +- .../ConfigMapsWithProfileExpressionTests.java | 8 +- ...gMapsWithProfilesNoActiveProfileTests.java | 6 +- .../config/ConfigMapsWithProfilesTests.java | 8 +- .../ConfigMapsWithoutProfilesTests.java | 8 +- .../kubernetes/fabric8}/config/CoreTest.java | 2 +- .../Fabric8SecretsPropertySourceTest.java | 8 +- .../fabric8}/config/HealthIndicatorTest.java | 4 +- .../KubernetesConfigConfigurationTest.java | 6 +- .../fabric8}/config/MissingActuatorTest.java | 2 +- .../config/MultipleConfigMapsTests.java | 4 +- .../fabric8}/config/MultipleSecretsTests.java | 4 +- .../fabric8}/config/TestApplication.java | 2 +- .../fabric8}/config/example/App.java | 2 +- .../config/example/GreetingController.java | 2 +- .../config/example/GreetingProperties.java | 2 +- .../config/example/ResponseMessage.java | 2 +- .../fabric8}/config/example2/ExampleApp.java | 5 +- .../config/example2/ExampleAppProps.java | 2 +- .../config/example3/MultiSecretsApp.java | 2 +- .../example3/MultiSecretsProperties.java | 2 +- .../ConfigurationChangeDetectorTest.java | 12 +- ...BasedConfigurationChangeDetectorTests.java | 18 +- .../src/test/resources/adhoc.properties | 0 .../src/test/resources/adhoc.yml | 0 .../src/test/resources/application-path.yaml | 0 .../resources/application-secrets.properties | 2 +- ...application-with-active-profiles-name.yaml | 0 .../resources/application-with-profiles.yaml | 0 .../application-without-profiles.yaml | 0 .../src/test/resources/application.properties | 0 .../src/test/resources/application.yaml | 0 .../src/test/resources/logback-test.xml | 0 .../src/test/resources/multiple-secrets.yml | 0 .../src/test/resources/multiplecms.yml | 0 .../run.sh | 3 + .../k8s/configmap.yaml | 9 + .../k8s/deployment-it.yaml | 29 ++ .../k8s/deployment-polling-it.yaml | 31 +++ .../k8s/secret.yaml | 7 + .../k8s/service-it.yaml | 18 ++ .../pom.xml | 90 +++++++ .../skaffold.yaml | 33 +++ .../KubernetesConfigClientApplicationIt.java | 57 ++++ .../config/it/MyConfigurationProperties.java | 50 ++++ .../src/main/resources/application.yaml | 11 + .../main/resources/bootstrap-kubernetes.yaml | 15 ++ .../src/main/resources/bootstrap.yaml | 7 + .../config/it/ConfigMapAndSecretIT.java | 255 ++++++++++++++++++ ...kubernetes-client-config-it-configmap.yaml | 9 + ...ubernetes-client-config-it-deployment.yaml | 31 +++ ...d-kubernetes-client-config-it-ingress.yaml | 14 + ...s-client-config-it-polling-deployment.yaml | 33 +++ ...ud-kubernetes-client-config-it-secret.yaml | 7 + ...d-kubernetes-client-config-it-service.yaml | 14 + .../core/k8s/it/ActuatorEndpointIT.java | 66 ++++- .../integration/tests/commons/K8SUtils.java | 9 + spring-cloud-starter-kubernetes-all/pom.xml | 2 +- .../pom.xml | 4 + .../pom.xml | 2 +- 137 files changed, 3691 insertions(+), 617 deletions(-) create mode 100644 spring-cloud-kubernetes-client-autoconfig/src/main/java/org/springframework/cloud/kubernetes/client/KubernetesClientUtils.java create mode 100644 spring-cloud-kubernetes-client-config/pom.xml create mode 100644 spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientBootstrapConfiguration.java create mode 100644 spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapPropertySource.java create mode 100644 spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapPropertySourceLocator.java create mode 100644 spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigUtils.java create mode 100644 spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSecretsPropertySource.java create mode 100644 spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSecretsPropertySourceLocator.java create mode 100644 spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientConfigReloadAutoConfiguration.java create mode 100644 spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientEventBasedConfigMapChangeDetector.java create mode 100644 spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientEventBasedSecretsChangeDetector.java create mode 100644 spring-cloud-kubernetes-client-config/src/main/resources/META-INF/spring.factories create mode 100644 spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapPropertySourceLocatorTests.java create mode 100644 spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapPropertySourceTests.java create mode 100644 spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSecretsPropertySourceLocatorTests.java create mode 100644 spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSecretsPropertySourceTests.java create mode 100644 spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientEventBasedConfigMapChangeDetectorTests.java create mode 100644 spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientEventBasedSecretsChangeDetectorTests.java create mode 100644 spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesMockEnvironment.java rename {spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes => spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons}/config/AbstractConfigProperties.java (95%) rename {spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes => spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons}/config/ConfigMapConfigProperties.java (97%) create mode 100644 spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapPropertySource.java rename {spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes => spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons}/config/ConfigMapPropertySourceLocator.java (72%) rename {spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes => spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons}/config/ConfigUtils.java (66%) rename {spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes => spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons}/config/Constants.java (60%) create mode 100644 spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/KubernetesBootstrapConfiguration.java rename {spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes => spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons}/config/PropertySourceUtils.java (77%) rename {spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes => spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons}/config/SecretsConfigProperties.java (97%) create mode 100644 spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsPropertySource.java rename {spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes => spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons}/config/SecretsPropertySourceLocator.java (73%) create mode 100644 spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/reload/ConfigReloadAutoConfiguration.java rename {spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes => spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons}/config/reload/ConfigReloadProperties.java (98%) rename {spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes => spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons}/config/reload/ConfigurationChangeDetector.java (88%) rename {spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes => spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons}/config/reload/ConfigurationUpdateStrategy.java (95%) rename {spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes => spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons}/config/reload/PollingConfigMapChangeDetector.java (72%) rename {spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes => spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons}/config/reload/PollingSecretsChangeDetector.java (74%) rename {spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes => spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons}/config/reload/condition/EventReloadDetectionMode.java (84%) rename {spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes => spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons}/config/reload/condition/PollingReloadDetectionMode.java (84%) delete mode 100644 spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/ConfigMapPropertySource.java delete mode 100644 spring-cloud-kubernetes-config/src/main/resources/META-INF/spring.factories rename {spring-cloud-kubernetes-config => spring-cloud-kubernetes-fabric8-config}/.jdk8 (100%) rename {spring-cloud-kubernetes-config => spring-cloud-kubernetes-fabric8-config}/pom.xml (97%) rename spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/BootstrapConfiguration.java => spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8BootstrapConfiguration.java (70%) create mode 100644 spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapPropertySource.java create mode 100644 spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapPropertySourceLocator.java create mode 100644 spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtils.java rename spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/SecretsPropertySource.java => spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretsPropertySource.java (66%) create mode 100644 spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretsPropertySourceLocator.java rename {spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes => spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8}/config/reload/ConfigReloadAutoConfiguration.java (72%) rename {spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes => spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8}/config/reload/ConfigReloadDefaultAutoConfiguration.java (66%) rename {spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes => spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8}/config/reload/EventBasedConfigMapChangeDetector.java (70%) rename {spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes => spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8}/config/reload/EventBasedSecretsChangeDetector.java (70%) rename {spring-cloud-kubernetes-config => spring-cloud-kubernetes-fabric8-config}/src/main/resources/META-INF/additional-spring-configuration-metadata.json (100%) create mode 100644 spring-cloud-kubernetes-fabric8-config/src/main/resources/META-INF/spring.factories rename {spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes => spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8}/config/ConfigMapTestUtil.java (95%) rename {spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes => spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8}/config/ConfigMapsFromFilePathsTests.java (89%) rename {spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes => spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8}/config/ConfigMapsMixedTests.java (93%) rename {spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes => spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8}/config/ConfigMapsTest.java (87%) rename {spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes => spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8}/config/ConfigMapsTests.java (96%) rename {spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes => spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8}/config/ConfigMapsWithActiveProfilesNameTests.java (94%) rename {spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes => spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8}/config/ConfigMapsWithProfileExpressionTests.java (91%) rename {spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes => spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8}/config/ConfigMapsWithProfilesNoActiveProfileTests.java (93%) rename {spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes => spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8}/config/ConfigMapsWithProfilesTests.java (92%) rename {spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes => spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8}/config/ConfigMapsWithoutProfilesTests.java (91%) rename {spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes => spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8}/config/CoreTest.java (98%) rename spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/SecretsPropertySourceTest.java => spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretsPropertySourceTest.java (92%) rename {spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes => spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8}/config/HealthIndicatorTest.java (95%) rename {spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes => spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8}/config/KubernetesConfigConfigurationTest.java (96%) rename {spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes => spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8}/config/MissingActuatorTest.java (97%) rename {spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes => spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8}/config/MultipleConfigMapsTests.java (96%) rename {spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes => spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8}/config/MultipleSecretsTests.java (96%) rename {spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes => spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8}/config/TestApplication.java (93%) rename {spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes => spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8}/config/example/App.java (93%) rename {spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes => spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8}/config/example/GreetingController.java (96%) rename {spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes => spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8}/config/example/GreetingProperties.java (95%) rename {spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes => spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8}/config/example/ResponseMessage.java (92%) rename {spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes => spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8}/config/example2/ExampleApp.java (91%) rename {spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes => spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8}/config/example2/ExampleAppProps.java (95%) rename {spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes => spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8}/config/example3/MultiSecretsApp.java (96%) rename {spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes => spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8}/config/example3/MultiSecretsProperties.java (94%) rename {spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes => spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8}/config/reload/ConfigurationChangeDetectorTest.java (89%) rename {spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes => spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8}/config/reload/EventBasedConfigurationChangeDetectorTests.java (76%) rename {spring-cloud-kubernetes-config => spring-cloud-kubernetes-fabric8-config}/src/test/resources/adhoc.properties (100%) rename {spring-cloud-kubernetes-config => spring-cloud-kubernetes-fabric8-config}/src/test/resources/adhoc.yml (100%) rename {spring-cloud-kubernetes-config => spring-cloud-kubernetes-fabric8-config}/src/test/resources/application-path.yaml (100%) rename {spring-cloud-kubernetes-config => spring-cloud-kubernetes-fabric8-config}/src/test/resources/application-secrets.properties (64%) rename {spring-cloud-kubernetes-config => spring-cloud-kubernetes-fabric8-config}/src/test/resources/application-with-active-profiles-name.yaml (100%) rename {spring-cloud-kubernetes-config => spring-cloud-kubernetes-fabric8-config}/src/test/resources/application-with-profiles.yaml (100%) rename {spring-cloud-kubernetes-config => spring-cloud-kubernetes-fabric8-config}/src/test/resources/application-without-profiles.yaml (100%) rename {spring-cloud-kubernetes-config => spring-cloud-kubernetes-fabric8-config}/src/test/resources/application.properties (100%) rename {spring-cloud-kubernetes-config => spring-cloud-kubernetes-fabric8-config}/src/test/resources/application.yaml (100%) rename {spring-cloud-kubernetes-config => spring-cloud-kubernetes-fabric8-config}/src/test/resources/logback-test.xml (100%) rename {spring-cloud-kubernetes-config => spring-cloud-kubernetes-fabric8-config}/src/test/resources/multiple-secrets.yml (100%) rename {spring-cloud-kubernetes-config => spring-cloud-kubernetes-fabric8-config}/src/test/resources/multiplecms.yml (100%) create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/k8s/configmap.yaml create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/k8s/deployment-it.yaml create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/k8s/deployment-polling-it.yaml create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/k8s/secret.yaml create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/k8s/service-it.yaml create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/pom.xml create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/skaffold.yaml create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/main/java/org/springframework/cloud/kubernetes/client/config/it/KubernetesConfigClientApplicationIt.java create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/main/java/org/springframework/cloud/kubernetes/client/config/it/MyConfigurationProperties.java create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/main/resources/application.yaml create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/main/resources/bootstrap-kubernetes.yaml create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/main/resources/bootstrap.yaml create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/test/java/org/springframework/cloud/kubernetes/client/config/it/ConfigMapAndSecretIT.java create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/test/resources/spring-cloud-kubernetes-client-config-it-configmap.yaml create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/test/resources/spring-cloud-kubernetes-client-config-it-deployment.yaml create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/test/resources/spring-cloud-kubernetes-client-config-it-ingress.yaml create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/test/resources/spring-cloud-kubernetes-client-config-it-polling-deployment.yaml create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/test/resources/spring-cloud-kubernetes-client-config-it-secret.yaml create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/test/resources/spring-cloud-kubernetes-client-config-it-service.yaml diff --git a/docs/src/main/asciidoc/property-source-config.adoc b/docs/src/main/asciidoc/property-source-config.adoc index ec0ac32e..d23d14bd 100644 --- a/docs/src/main/asciidoc/property-source-config.adoc +++ b/docs/src/main/asciidoc/property-source-config.adoc @@ -14,7 +14,7 @@ The link:./spring-cloud-kubernetes-config[Spring Cloud Kubernetes Config] projec during application bootstrapping and triggers hot reloading of beans or Spring context when changes are detected on observed `ConfigMap` instances. -The default behavior is to create a `ConfigMapPropertySource` based on a Kubernetes `ConfigMap` that has a `metadata.name` value of either the name of +The default behavior is to create a `Fabric8ConfigMapPropertySource` based on a Kubernetes `ConfigMap` that has a `metadata.name` value of either the name of your Spring application (as defined by its `spring.application.name` property) or a custom name defined within the `bootstrap.properties` file under the following key: `spring.cloud.kubernetes.config.name`. @@ -300,7 +300,7 @@ Kubernetes has the notion of https://kubernetes.io/docs/concepts/configuration/s sensitive data such as passwords, OAuth tokens, and so on. This project provides integration with `Secrets` to make secrets accessible by Spring Boot applications. You can explicitly enable or disable This feature by setting the `spring.cloud.kubernetes.secrets.enabled` property. -When enabled, the `SecretsPropertySource` looks up Kubernetes for `Secrets` from the following sources: +When enabled, the `Fabric8SecretsPropertySource` looks up Kubernetes for `Secrets` from the following sources: . Reading recursively from secrets mounts . Named after the application (as defined by `spring.application.name`) diff --git a/pom.xml b/pom.xml index fd26418c..ad2adf27 100644 --- a/pom.xml +++ b/pom.xml @@ -90,8 +90,9 @@ spring-cloud-kubernetes-commons spring-cloud-kubernetes-test-support spring-cloud-kubernetes-client-autoconfig + spring-cloud-kubernetes-client-config spring-cloud-kubernetes-fabric8-autoconfig - spring-cloud-kubernetes-config + spring-cloud-kubernetes-fabric8-config spring-cloud-kubernetes-discovery spring-cloud-starter-kubernetes spring-cloud-starter-kubernetes-config diff --git a/spring-cloud-kubernetes-client-autoconfig/src/main/java/org/springframework/cloud/kubernetes/client/KubernetesClientAutoConfiguration.java b/spring-cloud-kubernetes-client-autoconfig/src/main/java/org/springframework/cloud/kubernetes/client/KubernetesClientAutoConfiguration.java index de269f3e..732bf4b6 100644 --- a/spring-cloud-kubernetes-client-autoconfig/src/main/java/org/springframework/cloud/kubernetes/client/KubernetesClientAutoConfiguration.java +++ b/spring-cloud-kubernetes-client-autoconfig/src/main/java/org/springframework/cloud/kubernetes/client/KubernetesClientAutoConfiguration.java @@ -20,9 +20,6 @@ import java.io.IOException; import io.kubernetes.client.openapi.ApiClient; import io.kubernetes.client.openapi.apis.CoreV1Api; -import io.kubernetes.client.util.ClientBuilder; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator; import org.springframework.boot.actuate.autoconfigure.info.ConditionalOnEnabledInfoContributor; @@ -37,6 +34,8 @@ import org.springframework.cloud.kubernetes.commons.PodUtils; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import static org.springframework.cloud.kubernetes.client.KubernetesClientUtils.kubernetesApiClient; + /** * @author Ryan Baxter */ @@ -45,36 +44,11 @@ import org.springframework.context.annotation.Configuration; @AutoConfigureAfter(KubernetesCommonsAutoConfiguration.class) public class KubernetesClientAutoConfiguration { - private static final Log LOG = LogFactory.getLog(KubernetesClientAutoConfiguration.class); - - private ApiClient kubernetesApiClient() throws IOException { - try { - // Assume we are running in a cluster - ApiClient apiClient = ClientBuilder.cluster().build(); - io.kubernetes.client.openapi.Configuration.setDefaultApiClient(apiClient); - LOG.info("Created API client in the cluster."); - return apiClient; - } - catch (Exception e) { - LOG.info( - "Could not create the Kubernetes ApiClient in a cluster environment, trying to use a \"standard\" configuration instead.", - e); - try { - ApiClient apiClient = ClientBuilder.standard().build(); - io.kubernetes.client.openapi.Configuration.setDefaultApiClient(apiClient); - return apiClient; - } - catch (IOException e1) { - LOG.warn("Could not create a Kubernetes ApiClient from either a cluster or standard environment", e1); - throw e1; - } - } - } - @Bean @ConditionalOnMissingBean public CoreV1Api coreApi() throws IOException { - kubernetesApiClient(); + ApiClient apiClient = kubernetesApiClient(); + io.kubernetes.client.openapi.Configuration.setDefaultApiClient(apiClient); return new CoreV1Api(); } diff --git a/spring-cloud-kubernetes-client-autoconfig/src/main/java/org/springframework/cloud/kubernetes/client/KubernetesClientUtils.java b/spring-cloud-kubernetes-client-autoconfig/src/main/java/org/springframework/cloud/kubernetes/client/KubernetesClientUtils.java new file mode 100644 index 00000000..b9c2f232 --- /dev/null +++ b/spring-cloud-kubernetes-client-autoconfig/src/main/java/org/springframework/cloud/kubernetes/client/KubernetesClientUtils.java @@ -0,0 +1,58 @@ +/* + * 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.client; + +import java.io.IOException; + +import io.kubernetes.client.openapi.ApiClient; +import io.kubernetes.client.util.ClientBuilder; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * @author Ryan Baxter + */ +public final class KubernetesClientUtils { + + private static final Log LOG = LogFactory.getLog(KubernetesClientUtils.class); + + private KubernetesClientUtils() { + } + + public static ApiClient kubernetesApiClient() throws IOException { + try { + // Assume we are running in a cluster + ApiClient apiClient = ClientBuilder.cluster().build(); + LOG.info("Created API client in the cluster."); + return apiClient; + } + catch (Exception e) { + LOG.info( + "Could not create the Kubernetes ApiClient in a cluster environment, trying to use a \"standard\" configuration instead.", + e); + try { + ApiClient apiClient = ClientBuilder.standard().build(); + return apiClient; + } + catch (IOException e1) { + LOG.warn("Could not create a Kubernetes ApiClient from either a cluster or standard environment", e1); + throw e1; + } + } + } + +} diff --git a/spring-cloud-kubernetes-client-autoconfig/src/main/java/org/springframework/cloud/kubernetes/client/profile/KubernetesClientProfileEnvironmentPostProcessor.java b/spring-cloud-kubernetes-client-autoconfig/src/main/java/org/springframework/cloud/kubernetes/client/profile/KubernetesClientProfileEnvironmentPostProcessor.java index d419acd3..767d0823 100644 --- a/spring-cloud-kubernetes-client-autoconfig/src/main/java/org/springframework/cloud/kubernetes/client/profile/KubernetesClientProfileEnvironmentPostProcessor.java +++ b/spring-cloud-kubernetes-client-autoconfig/src/main/java/org/springframework/cloud/kubernetes/client/profile/KubernetesClientProfileEnvironmentPostProcessor.java @@ -22,19 +22,19 @@ import org.springframework.cloud.kubernetes.client.KubernetesClientPodUtils; import org.springframework.cloud.kubernetes.commons.profile.AbstractKubernetesProfileEnvironmentPostProcessor; import org.springframework.core.env.Environment; +import static io.kubernetes.client.util.Config.ENV_SERVICE_HOST; + /** * @author Ryan Baxter * @author Thomas Vitale */ public class KubernetesClientProfileEnvironmentPostProcessor extends AbstractKubernetesProfileEnvironmentPostProcessor { - protected static final String KUBERNETES_SERVICE_ENV_VAR = "KUBERNETES_SERVICE_HOST"; - @Override protected boolean isInsideKubernetes(Environment environment) { CoreV1Api api = new CoreV1Api(); KubernetesClientPodUtils utils = new KubernetesClientPodUtils(api, environment.getProperty(NAMESPACE_PROPERTY)); - return environment.containsProperty(KUBERNETES_SERVICE_ENV_VAR) || utils.isInsideKubernetes(); + return environment.containsProperty(ENV_SERVICE_HOST) || utils.isInsideKubernetes(); } } diff --git a/spring-cloud-kubernetes-client-config/pom.xml b/spring-cloud-kubernetes-client-config/pom.xml new file mode 100644 index 00000000..09ca59cf --- /dev/null +++ b/spring-cloud-kubernetes-client-config/pom.xml @@ -0,0 +1,91 @@ + + + + spring-cloud-kubernetes + org.springframework.cloud + 2.0.0-SNAPSHOT + + 4.0.0 + + spring-cloud-kubernetes-client-config + + + 2.26.3 + + + + + + org.springframework.cloud + spring-cloud-kubernetes-client-autoconfig + + + org.springframework.cloud + spring-cloud-kubernetes-commons + + + io.kubernetes + client-java + + + io.kubernetes + client-java-extended + + + org.springframework.boot + spring-boot-starter-logging + true + + + org.springframework.boot + spring-boot-actuator + true + + + org.springframework.boot + spring-boot-actuator-autoconfigure + true + + + org.springframework.boot + spring-boot-autoconfigure + + + + org.springframework.cloud + spring-cloud-starter + + + + org.springframework.cloud + spring-cloud-starter-bootstrap + + + + org.springframework.security + spring-security-rsa + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.cloud + spring-cloud-kubernetes-test-support + + + + com.github.tomakehurst + wiremock-jre8 + ${wiremock.version} + test + + + + + + diff --git a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientBootstrapConfiguration.java b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientBootstrapConfiguration.java new file mode 100644 index 00000000..dcffc012 --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientBootstrapConfiguration.java @@ -0,0 +1,65 @@ +/* + * 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.client.config; + +import io.kubernetes.client.openapi.apis.CoreV1Api; + +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.kubernetes.client.KubernetesClientAutoConfiguration; +import org.springframework.cloud.kubernetes.commons.ConditionalOnKubernetesEnabled; +import org.springframework.cloud.kubernetes.commons.KubernetesClientProperties; +import org.springframework.cloud.kubernetes.commons.KubernetesCommonsAutoConfiguration; +import org.springframework.cloud.kubernetes.commons.config.ConfigMapConfigProperties; +import org.springframework.cloud.kubernetes.commons.config.KubernetesBootstrapConfiguration; +import org.springframework.cloud.kubernetes.commons.config.SecretsConfigProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +/** + * @author Ryan Baxter + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnKubernetesEnabled +@AutoConfigureAfter(KubernetesBootstrapConfiguration.class) +public class KubernetesClientBootstrapConfiguration { + + @Configuration(proxyBeanMethods = false) + @Import({ KubernetesCommonsAutoConfiguration.class, KubernetesClientAutoConfiguration.class }) + protected static class KubernetesPropertySourceConfiguration { + + @Bean + @ConditionalOnProperty(name = "spring.cloud.kubernetes.config.enabled", matchIfMissing = true) + public KubernetesClientConfigMapPropertySourceLocator configMapPropertySourceLocator( + ConfigMapConfigProperties properties, CoreV1Api coreV1Api, + KubernetesClientProperties kubernetesClientProperties) { + return new KubernetesClientConfigMapPropertySourceLocator(coreV1Api, properties, + kubernetesClientProperties); + } + + @Bean + @ConditionalOnProperty(name = "spring.cloud.kubernetes.secrets.enabled", matchIfMissing = true) + public KubernetesClientSecretsPropertySourceLocator secretsPropertySourceLocator( + SecretsConfigProperties properties, CoreV1Api coreV1Api, + KubernetesClientProperties kubernetesClientProperties) { + return new KubernetesClientSecretsPropertySourceLocator(coreV1Api, kubernetesClientProperties, properties); + } + + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapPropertySource.java b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapPropertySource.java new file mode 100644 index 00000000..8c0901ce --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapPropertySource.java @@ -0,0 +1,68 @@ +/* + * 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.client.config; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import io.kubernetes.client.openapi.ApiException; +import io.kubernetes.client.openapi.apis.CoreV1Api; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.cloud.kubernetes.commons.config.ConfigMapPropertySource; +import org.springframework.core.env.Environment; + +/** + * @author Ryan Baxter + */ +public class KubernetesClientConfigMapPropertySource extends ConfigMapPropertySource { + + private static final Log LOG = LogFactory.getLog(KubernetesClientConfigMapPropertySource.class); + + public KubernetesClientConfigMapPropertySource(CoreV1Api coreV1Api, String name, String namespace, + Environment environment) { + super(getName(name, namespace), asObjectMap(getData(coreV1Api, name, namespace, environment))); + } + + private static Map getData(CoreV1Api coreV1Api, String name, String namespace, + Environment environment) { + + try { + List names = new ArrayList<>(); + names.add(name); + if (environment != null) { + for (String activeProfile : environment.getActiveProfiles()) { + names.add(name + "-" + activeProfile); + } + } + Map result = new LinkedHashMap<>(); + coreV1Api.listNamespacedConfigMap(namespace, null, null, null, null, null, null, null, null, null) + .getItems().stream().filter(cm -> names.contains(cm.getMetadata().getName())) + .forEach(map -> result.putAll(processAllEntries(map.getData(), environment))); + + return result; + } + catch (ApiException e) { + LOG.warn("Unable to get ConfigMap " + name + " in namespace " + namespace, e); + } + return new LinkedHashMap<>(); + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapPropertySourceLocator.java b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapPropertySourceLocator.java new file mode 100644 index 00000000..21ea5414 --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapPropertySourceLocator.java @@ -0,0 +1,53 @@ +/* + * 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.client.config; + +import io.kubernetes.client.openapi.apis.CoreV1Api; + +import org.springframework.cloud.kubernetes.commons.KubernetesClientProperties; +import org.springframework.cloud.kubernetes.commons.config.ConfigMapConfigProperties; +import org.springframework.cloud.kubernetes.commons.config.ConfigMapPropertySourceLocator; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.MapPropertySource; + +import static org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigUtils.getNamespace; + +/** + * @author Ryan Baxter + */ +public class KubernetesClientConfigMapPropertySourceLocator extends ConfigMapPropertySourceLocator { + + private CoreV1Api coreV1Api; + + private KubernetesClientProperties kubernetesClientProperties; + + public KubernetesClientConfigMapPropertySourceLocator(CoreV1Api coreV1Api, ConfigMapConfigProperties properties, + KubernetesClientProperties kubernetesClientProperties) { + super(properties); + this.coreV1Api = coreV1Api; + this.kubernetesClientProperties = kubernetesClientProperties; + } + + @Override + protected MapPropertySource getMapPropertySource(String name, + ConfigMapConfigProperties.NormalizedSource normalizedSource, String configurationTarget, + ConfigurableEnvironment environment) { + return new KubernetesClientConfigMapPropertySource(coreV1Api, name, + getNamespace(normalizedSource, kubernetesClientProperties), environment); + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigUtils.java b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigUtils.java new file mode 100644 index 00000000..fd02e726 --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigUtils.java @@ -0,0 +1,52 @@ +/* + * 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.client.config; + +import org.springframework.cloud.kubernetes.commons.KubernetesClientProperties; +import org.springframework.cloud.kubernetes.commons.config.ConfigMapConfigProperties; +import org.springframework.cloud.kubernetes.commons.config.SecretsConfigProperties; +import org.springframework.util.StringUtils; + +/** + * @author Ryan Baxter + */ +public final class KubernetesClientConfigUtils { + + private KubernetesClientConfigUtils() { + } + + public static String getNamespace(ConfigMapConfigProperties.NormalizedSource normalizedSource, + KubernetesClientProperties kubernetesClientProperties) { + if (!StringUtils.hasText(normalizedSource.getNamespace())) { + return kubernetesClientProperties.getNamespace(); + } + else { + return normalizedSource.getNamespace(); + } + } + + public static String getNamespace(SecretsConfigProperties.NormalizedSource normalizedSource, + KubernetesClientProperties kubernetesClientProperties) { + if (!StringUtils.hasText(normalizedSource.getNamespace())) { + return kubernetesClientProperties.getNamespace(); + } + else { + return normalizedSource.getNamespace(); + } + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSecretsPropertySource.java b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSecretsPropertySource.java new file mode 100644 index 00000000..64a88c3d --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSecretsPropertySource.java @@ -0,0 +1,109 @@ +/* + * 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.client.config; + +import java.util.Base64; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +import io.kubernetes.client.openapi.apis.CoreV1Api; +import io.kubernetes.client.openapi.models.V1Secret; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.cloud.kubernetes.commons.config.SecretsPropertySource; +import org.springframework.core.env.Environment; +import org.springframework.util.StringUtils; + +/** + * @author Ryan Baxter + */ +public class KubernetesClientSecretsPropertySource extends SecretsPropertySource { + + private static final Log LOG = LogFactory.getLog(KubernetesClientSecretsPropertySource.class); + + private CoreV1Api coreV1Api; + + public KubernetesClientSecretsPropertySource(CoreV1Api coreV1Api, String name, String namespace, + Environment environment, Map labels) { + super(getSourceName(name, namespace), getSourceData(coreV1Api, environment, name, namespace, labels)); + + } + + private static Map getSourceData(CoreV1Api api, Environment env, String name, String namespace, + Map labels) { + Map result = new HashMap<>(); + + try { + // Read for secrets api (named) + if (StringUtils.hasText(name)) { + Optional secret; + if (!StringUtils.hasText(namespace)) { + + // There could technically be more than one, just return the first + secret = api.listSecretForAllNamespaces(null, null, null, null, null, null, null, null, null) + .getItems().stream().filter(s -> name.equals(s.getMetadata().getName())).findFirst(); + } + else { + secret = api.listNamespacedSecret(namespace, null, null, null, null, null, null, null, null, null) + .getItems().stream().filter(s -> name.equals(s.getMetadata().getName())).findFirst(); + } + + secret.ifPresent(s -> putAll(s, result)); + } + + // Read for secrets api (label) + if (labels != null && !labels.isEmpty()) { + if (!StringUtils.hasText(namespace)) { + api.listSecretForAllNamespaces(null, null, null, createLabelsSelector(labels), null, null, null, + null, null).getItems().forEach(s -> putAll(s, result)); + } + else { + api.listNamespacedSecret(namespace, null, null, null, null, createLabelsSelector(labels), null, + null, null, null).getItems().forEach(s -> putAll(s, result)); + } + } + } + catch (Exception e) { + LOG.warn("Can't read secret with name: [" + name + "] or labels [" + labels + "] in namespace:[" + namespace + + "] (cause: " + e.getMessage() + "). Ignoring", e); + } + + return result; + } + + private static String createLabelsSelector(Map labels) { + StringBuilder selectorString = new StringBuilder(); + for (String key : labels.keySet()) { + if (selectorString.length() != 0) { + selectorString.append(","); + } + selectorString.append(key + "=" + labels.get(key)); + } + return selectorString.toString(); + } + + private static void putAll(V1Secret secret, Map result) { + Map secretData = new HashMap<>(); + secret.getData().forEach((key, value) -> secretData.put(key, Base64.getEncoder().encodeToString(value))); + if (secret != null) { + putAll(secretData, result); + } + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSecretsPropertySourceLocator.java b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSecretsPropertySourceLocator.java new file mode 100644 index 00000000..eb36d31b --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSecretsPropertySourceLocator.java @@ -0,0 +1,54 @@ +/* + * 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.client.config; + +import io.kubernetes.client.openapi.apis.CoreV1Api; + +import org.springframework.cloud.kubernetes.commons.KubernetesClientProperties; +import org.springframework.cloud.kubernetes.commons.config.SecretsConfigProperties; +import org.springframework.cloud.kubernetes.commons.config.SecretsPropertySourceLocator; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.MapPropertySource; + +import static org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigUtils.getNamespace; +import static org.springframework.cloud.kubernetes.commons.config.ConfigUtils.getApplicationName; + +/** + * @author Ryan Baxter + */ +public class KubernetesClientSecretsPropertySourceLocator extends SecretsPropertySourceLocator { + + private CoreV1Api coreV1Api; + + private KubernetesClientProperties kubernetesClientProperties; + + public KubernetesClientSecretsPropertySourceLocator(CoreV1Api coreV1Api, + KubernetesClientProperties kubernetesClientProperties, SecretsConfigProperties secretsConfigProperties) { + super(secretsConfigProperties); + this.coreV1Api = coreV1Api; + this.kubernetesClientProperties = kubernetesClientProperties; + } + + @Override + protected MapPropertySource getPropertySource(ConfigurableEnvironment environment, + SecretsConfigProperties.NormalizedSource normalizedSource, String configurationTarget) { + return new KubernetesClientSecretsPropertySource(coreV1Api, + getApplicationName(environment, normalizedSource.getName(), configurationTarget), + getNamespace(normalizedSource, kubernetesClientProperties), environment, normalizedSource.getLabels()); + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientConfigReloadAutoConfiguration.java b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientConfigReloadAutoConfiguration.java new file mode 100644 index 00000000..2c0a38be --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientConfigReloadAutoConfiguration.java @@ -0,0 +1,153 @@ +/* + * 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.client.config.reload; + +import io.kubernetes.client.openapi.apis.CoreV1Api; + +import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration; +import org.springframework.boot.actuate.autoconfigure.info.InfoEndpointAutoConfiguration; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.autoconfigure.RefreshAutoConfiguration; +import org.springframework.cloud.autoconfigure.RefreshEndpointAutoConfiguration; +import org.springframework.cloud.context.refresh.ContextRefresher; +import org.springframework.cloud.context.restart.RestartEndpoint; +import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigMapPropertySource; +import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigMapPropertySourceLocator; +import org.springframework.cloud.kubernetes.client.config.KubernetesClientSecretsPropertySource; +import org.springframework.cloud.kubernetes.client.config.KubernetesClientSecretsPropertySourceLocator; +import org.springframework.cloud.kubernetes.commons.ConditionalOnKubernetesEnabled; +import org.springframework.cloud.kubernetes.commons.KubernetesClientProperties; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadAutoConfiguration; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationChangeDetector; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy; +import org.springframework.cloud.kubernetes.commons.config.reload.PollingConfigMapChangeDetector; +import org.springframework.cloud.kubernetes.commons.config.reload.PollingSecretsChangeDetector; +import org.springframework.cloud.kubernetes.commons.config.reload.condition.EventReloadDetectionMode; +import org.springframework.cloud.kubernetes.commons.config.reload.condition.PollingReloadDetectionMode; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Conditional; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.AbstractEnvironment; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.annotation.EnableScheduling; + +/** + * @author Ryan Baxter + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnKubernetesEnabled +@ConditionalOnClass(EndpointAutoConfiguration.class) +@AutoConfigureAfter({ InfoEndpointAutoConfiguration.class, RefreshEndpointAutoConfiguration.class, + RefreshAutoConfiguration.class, ConfigReloadAutoConfiguration.class }) +@EnableConfigurationProperties(ConfigReloadProperties.class) +public class KubernetesClientConfigReloadAutoConfiguration { + + /** + * Configuration reload must be enabled explicitly. + */ + @ConditionalOnProperty("spring.cloud.kubernetes.reload.enabled") + @ConditionalOnClass({ RestartEndpoint.class, ContextRefresher.class }) + @EnableScheduling + @EnableAsync + protected static class ConfigReloadAutoConfigurationBeans { + + /** + * Polling configMap ConfigurationChangeDetector. + * @param properties config reload properties + * @param strategy configuration update strategy + * @param configMapPropertySourceLocator configMap property source locator + * @return a bean that listen to configuration changes and fire a reload. + */ + @Bean + @ConditionalOnBean(KubernetesClientConfigMapPropertySourceLocator.class) + @Conditional(PollingReloadDetectionMode.class) + public ConfigurationChangeDetector configMapPropertyChangePollingWatcher(ConfigReloadProperties properties, + ConfigurationUpdateStrategy strategy, + KubernetesClientConfigMapPropertySourceLocator configMapPropertySourceLocator, + AbstractEnvironment environment) { + + return new PollingConfigMapChangeDetector(environment, properties, strategy, + KubernetesClientConfigMapPropertySource.class, configMapPropertySourceLocator); + } + + /** + * Polling secrets ConfigurationChangeDetector. + * @param properties config reload properties + * @param strategy configuration update strategy + * @param secretsPropertySourceLocator secrets property source locator + * @return a bean that listen to configuration changes and fire a reload. + */ + @Bean + @ConditionalOnBean(KubernetesClientSecretsPropertySourceLocator.class) + @Conditional(PollingReloadDetectionMode.class) + public ConfigurationChangeDetector secretsPropertyChangePollingWatcher(ConfigReloadProperties properties, + ConfigurationUpdateStrategy strategy, + KubernetesClientSecretsPropertySourceLocator secretsPropertySourceLocator, + AbstractEnvironment environment) { + + return new PollingSecretsChangeDetector(environment, properties, strategy, + KubernetesClientSecretsPropertySource.class, secretsPropertySourceLocator); + } + + /** + * Event Based configMap ConfigurationChangeDetector. + * @param properties config reload properties + * @param strategy configuration update strategy + * @param configMapPropertySourceLocator configMap property source locator + * @return a bean that listen to configMap change events and fire a reload. + */ + @Bean + @ConditionalOnBean(KubernetesClientConfigMapPropertySourceLocator.class) + @Conditional(EventReloadDetectionMode.class) + public ConfigurationChangeDetector configMapPropertyChangeEventWatcher(ConfigReloadProperties properties, + ConfigurationUpdateStrategy strategy, + KubernetesClientConfigMapPropertySourceLocator configMapPropertySourceLocator, + AbstractEnvironment environment, CoreV1Api coreV1Api, + KubernetesClientProperties kubernetesClientProperties) { + + return new KubernetesClientEventBasedConfigMapChangeDetector(coreV1Api, environment, properties, strategy, + configMapPropertySourceLocator, kubernetesClientProperties); + } + + /** + * Event Based secrets ConfigurationChangeDetector. + * @param properties config reload properties + * @param strategy configuration update strategy + * @param secretsPropertySourceLocator secrets property source locator + * @return a bean that listen to secrets change events and fire a reload. + */ + @Bean + @ConditionalOnBean(KubernetesClientSecretsPropertySourceLocator.class) + @Conditional(EventReloadDetectionMode.class) + public ConfigurationChangeDetector secretsPropertyChangeEventWatcher(ConfigReloadProperties properties, + ConfigurationUpdateStrategy strategy, + KubernetesClientSecretsPropertySourceLocator secretsPropertySourceLocator, + AbstractEnvironment environment, CoreV1Api coreV1Api, + KubernetesClientProperties kubernetesClientProperties) { + + return new KubernetesClientEventBasedSecretsChangeDetector(coreV1Api, environment, properties, strategy, + secretsPropertySourceLocator, kubernetesClientProperties); + } + + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientEventBasedConfigMapChangeDetector.java b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientEventBasedConfigMapChangeDetector.java new file mode 100644 index 00000000..5b340484 --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientEventBasedConfigMapChangeDetector.java @@ -0,0 +1,141 @@ +/* + * 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.client.config.reload; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; + +import io.kubernetes.client.informer.ResourceEventHandler; +import io.kubernetes.client.informer.SharedIndexInformer; +import io.kubernetes.client.informer.SharedInformerFactory; +import io.kubernetes.client.openapi.ApiClient; +import io.kubernetes.client.openapi.apis.CoreV1Api; +import io.kubernetes.client.openapi.models.V1ConfigMap; +import io.kubernetes.client.openapi.models.V1ConfigMapList; +import io.kubernetes.client.util.CallGeneratorParams; +import okhttp3.OkHttpClient; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigMapPropertySource; +import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigMapPropertySourceLocator; +import org.springframework.cloud.kubernetes.commons.KubernetesClientProperties; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationChangeDetector; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy; +import org.springframework.core.env.ConfigurableEnvironment; + +import static org.springframework.cloud.kubernetes.client.KubernetesClientUtils.kubernetesApiClient; + +/** + * @author Ryan Baxter + */ +public class KubernetesClientEventBasedConfigMapChangeDetector extends ConfigurationChangeDetector { + + private static final Log LOG = LogFactory.getLog(KubernetesClientEventBasedConfigMapChangeDetector.class); + + private CoreV1Api coreV1Api = null; + + private KubernetesClientConfigMapPropertySourceLocator propertySourceLocator; + + private SharedInformerFactory factory; + + private KubernetesClientProperties kubernetesClientProperties; + + public KubernetesClientEventBasedConfigMapChangeDetector(CoreV1Api coreV1Api, ConfigurableEnvironment environment, + ConfigReloadProperties properties, ConfigurationUpdateStrategy strategy, + KubernetesClientConfigMapPropertySourceLocator propertySourceLocator, + KubernetesClientProperties kubernetesClientProperties) { + super(environment, properties, strategy); + this.propertySourceLocator = propertySourceLocator; + this.coreV1Api = coreV1Api; + this.factory = new SharedInformerFactory(); + this.kubernetesClientProperties = kubernetesClientProperties; + } + + public KubernetesClientEventBasedConfigMapChangeDetector(ConfigurableEnvironment environment, + ConfigReloadProperties properties, ConfigurationUpdateStrategy strategy, + KubernetesClientConfigMapPropertySourceLocator propertySourceLocator, + KubernetesClientProperties kubernetesClientProperties) { + super(environment, properties, strategy); + this.propertySourceLocator = propertySourceLocator; + this.kubernetesClientProperties = kubernetesClientProperties; + try { + ApiClient apiClient = kubernetesApiClient(); + OkHttpClient httpClient = apiClient.getHttpClient().newBuilder().readTimeout(0, TimeUnit.SECONDS).build(); + apiClient.setHttpClient(httpClient); + this.coreV1Api = new CoreV1Api(apiClient); + } + catch (IOException e) { + LOG.error("Failed to create Kubernetes API client. Event based ConfigMap monitoring will not work", e); + } + this.factory = new SharedInformerFactory(); + } + + @PostConstruct + public void watch() { + if (coreV1Api != null && this.properties.isMonitoringConfigMaps()) { + SharedIndexInformer configMapInformer = factory.sharedIndexInformerFor( + (CallGeneratorParams params) -> coreV1Api.listNamespacedConfigMapCall( + kubernetesClientProperties.getNamespace(), null, null, null, null, null, null, + params.resourceVersion, params.timeoutSeconds, params.watch, null), + V1ConfigMap.class, V1ConfigMapList.class); + configMapInformer.addEventHandler(new ResourceEventHandler() { + @Override + public void onAdd(V1ConfigMap obj) { + LOG.info("CongifMap " + obj.getMetadata().getName() + " was added."); + onEvent(obj); + } + + @Override + public void onUpdate(V1ConfigMap oldObj, V1ConfigMap newObj) { + LOG.info("ConfigMap " + newObj.getMetadata().getName() + " was added."); + onEvent(newObj); + } + + @Override + public void onDelete(V1ConfigMap obj, boolean deletedFinalStateUnknown) { + LOG.info("ConfigMap " + obj.getMetadata() + " was deleted."); + onEvent(obj); + } + }); + factory.startAllRegisteredInformers(); + } + } + + @PreDestroy + public void unwatch() { + factory.stopAllRegisteredInformers(); + } + + private void onEvent(V1ConfigMap configMap) { + boolean changed = changed(locateMapPropertySources(this.propertySourceLocator, this.environment), + findPropertySources(KubernetesClientConfigMapPropertySource.class)); + if (changed) { + LOG.info("Configuration change detected, reloading properties."); + reloadProperties(); + } + else { + LOG.warn("Configuration change was not detected."); + } + + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientEventBasedSecretsChangeDetector.java b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientEventBasedSecretsChangeDetector.java new file mode 100644 index 00000000..be295b94 --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientEventBasedSecretsChangeDetector.java @@ -0,0 +1,131 @@ +/* + * 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.client.config.reload; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import javax.annotation.PostConstruct; + +import io.kubernetes.client.informer.ResourceEventHandler; +import io.kubernetes.client.informer.SharedIndexInformer; +import io.kubernetes.client.informer.SharedInformerFactory; +import io.kubernetes.client.openapi.ApiClient; +import io.kubernetes.client.openapi.apis.CoreV1Api; +import io.kubernetes.client.openapi.models.V1Secret; +import io.kubernetes.client.openapi.models.V1SecretList; +import io.kubernetes.client.util.CallGeneratorParams; +import okhttp3.OkHttpClient; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.cloud.kubernetes.client.config.KubernetesClientSecretsPropertySource; +import org.springframework.cloud.kubernetes.client.config.KubernetesClientSecretsPropertySourceLocator; +import org.springframework.cloud.kubernetes.commons.KubernetesClientProperties; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationChangeDetector; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy; +import org.springframework.core.env.ConfigurableEnvironment; + +import static org.springframework.cloud.kubernetes.client.KubernetesClientUtils.kubernetesApiClient; + +/** + * @author Ryan Baxter + */ +public class KubernetesClientEventBasedSecretsChangeDetector extends ConfigurationChangeDetector { + + private static final Log LOG = LogFactory.getLog(KubernetesClientEventBasedSecretsChangeDetector.class); + + private CoreV1Api coreV1Api; + + private KubernetesClientSecretsPropertySourceLocator propertySourceLocator; + + private SharedInformerFactory factory; + + private KubernetesClientProperties kubernetesClientProperties; + + public KubernetesClientEventBasedSecretsChangeDetector(CoreV1Api coreV1Api, ConfigurableEnvironment environment, + ConfigReloadProperties properties, ConfigurationUpdateStrategy strategy, + KubernetesClientSecretsPropertySourceLocator propertySourceLocator, + KubernetesClientProperties kubernetesClientProperties) { + super(environment, properties, strategy); + this.propertySourceLocator = propertySourceLocator; + this.factory = new SharedInformerFactory(); + this.coreV1Api = coreV1Api; + this.kubernetesClientProperties = kubernetesClientProperties; + } + + public KubernetesClientEventBasedSecretsChangeDetector(ConfigurableEnvironment environment, + ConfigReloadProperties properties, ConfigurationUpdateStrategy strategy, + KubernetesClientSecretsPropertySourceLocator propertySourceLocator, + KubernetesClientProperties kubernetesClientProperties) { + super(environment, properties, strategy); + this.propertySourceLocator = propertySourceLocator; + this.factory = new SharedInformerFactory(); + this.kubernetesClientProperties = kubernetesClientProperties; + try { + ApiClient apiClient = kubernetesApiClient(); + OkHttpClient httpClient = apiClient.getHttpClient().newBuilder().readTimeout(0, TimeUnit.SECONDS).build(); + apiClient.setHttpClient(httpClient); + this.coreV1Api = new CoreV1Api(apiClient); + } + catch (IOException e) { + LOG.error("Failed to create Kubernetes API client. Event based ConfigMap monitoring will not work", e); + } + } + + @PostConstruct + public void watch() { + if (coreV1Api != null && this.properties.isMonitoringSecrets()) { + SharedIndexInformer configMapInformer = factory.sharedIndexInformerFor( + (CallGeneratorParams params) -> coreV1Api.listNamespacedSecretCall( + kubernetesClientProperties.getNamespace(), null, null, null, null, null, null, + params.resourceVersion, params.timeoutSeconds, params.watch, null), + V1Secret.class, V1SecretList.class); + configMapInformer.addEventHandler(new ResourceEventHandler() { + @Override + public void onAdd(V1Secret obj) { + LOG.info("Secret " + obj.getMetadata().getName() + " was added."); + onEvent(obj); + } + + @Override + public void onUpdate(V1Secret oldObj, V1Secret newObj) { + LOG.info("Secret " + newObj.getMetadata().getName() + " was added."); + onEvent(newObj); + } + + @Override + public void onDelete(V1Secret obj, boolean deletedFinalStateUnknown) { + LOG.info("Secret " + obj.getMetadata() + " was deleted."); + onEvent(obj); + } + }); + factory.startAllRegisteredInformers(); + } + } + + private void onEvent(V1Secret secret) { + boolean changed = changed(locateMapPropertySources(this.propertySourceLocator, this.environment), + findPropertySources(KubernetesClientSecretsPropertySource.class)); + if (changed) { + this.log.info("Detected change in secrets"); + reloadProperties(); + } + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/main/resources/META-INF/spring.factories b/spring-cloud-kubernetes-client-config/src/main/resources/META-INF/spring.factories new file mode 100644 index 00000000..067e1fb4 --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/main/resources/META-INF/spring.factories @@ -0,0 +1,4 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +org.springframework.cloud.kubernetes.client.config.reload.KubernetesClientConfigReloadAutoConfiguration +org.springframework.cloud.bootstrap.BootstrapConfiguration=\ +org.springframework.cloud.kubernetes.client.config.KubernetesClientBootstrapConfiguration diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapPropertySourceLocatorTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapPropertySourceLocatorTests.java new file mode 100644 index 00000000..c668480e --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapPropertySourceLocatorTests.java @@ -0,0 +1,125 @@ +/* + * 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.client.config; + +import java.util.ArrayList; +import java.util.List; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.WireMock; +import io.kubernetes.client.openapi.ApiClient; +import io.kubernetes.client.openapi.Configuration; +import io.kubernetes.client.openapi.JSON; +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.util.ClientBuilder; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import org.springframework.cloud.kubernetes.commons.KubernetesClientProperties; +import org.springframework.cloud.kubernetes.commons.config.ConfigMapConfigProperties; +import org.springframework.core.env.PropertySource; +import org.springframework.mock.env.MockEnvironment; + +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.assertj.core.api.Assertions.assertThat; + +/** + * @author Ryan Baxter + */ +class KubernetesClientConfigMapPropertySourceLocatorTests { + + private static final V1ConfigMapList PROPERTIES_CONFIGMAP_LIST = new V1ConfigMapList() + .addItemsItem( + new V1ConfigMapBuilder() + .withMetadata(new V1ObjectMetaBuilder().withName("bootstrap-640").withNamespace("default") + .withResourceVersion("1").build()) + .addToData("application.properties", + "spring.cloud.kubernetes.configuration.watcher.refreshDelay=0\n" + + "logging.level.org.springframework.cloud.kubernetes=TRACE") + .build()); + + private static final String API = "/api/v1/namespaces/default/configmaps"; + + private static WireMockServer wireMockServer; + + @BeforeAll + public static void setup() { + wireMockServer = new WireMockServer(options().dynamicPort()); + + wireMockServer.start(); + WireMock.configureFor("localhost", wireMockServer.port()); + + ApiClient client = new ClientBuilder().setBasePath("http://localhost:" + wireMockServer.port()).build(); + client.setDebugging(true); + Configuration.setDefaultApiClient(client); + } + + @AfterAll + public static void after() { + wireMockServer.stop(); + } + + @AfterEach + public void afterEach() { + WireMock.reset(); + } + + @Test + void locateWithoutSources() { + CoreV1Api api = new CoreV1Api(); + stubFor(get(API) + .willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(PROPERTIES_CONFIGMAP_LIST)))); + ConfigMapConfigProperties configMapConfigProperties = new ConfigMapConfigProperties(); + configMapConfigProperties.setName("bootstrap-640"); + KubernetesClientProperties kubernetesClientProperties = new KubernetesClientProperties(); + kubernetesClientProperties.setNamespace("default"); + PropertySource propertySource = new KubernetesClientConfigMapPropertySourceLocator(api, + configMapConfigProperties, kubernetesClientProperties).locate(new MockEnvironment()); + assertThat(propertySource.containsProperty("spring.cloud.kubernetes.configuration.watcher.refreshDelay")) + .isTrue(); + } + + @Test + void locateWithSources() { + CoreV1Api api = new CoreV1Api(); + stubFor(get(API) + .willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(PROPERTIES_CONFIGMAP_LIST)))); + ConfigMapConfigProperties configMapConfigProperties = new ConfigMapConfigProperties(); + configMapConfigProperties.setName("fake-name"); + ConfigMapConfigProperties.Source source1 = new ConfigMapConfigProperties.Source(); + source1.setName("bootstrap-640"); + source1.setNamespace("default"); + List sources = new ArrayList<>(); + sources.add(source1); + configMapConfigProperties.setSources(sources); + KubernetesClientProperties kubernetesClientProperties = new KubernetesClientProperties(); + kubernetesClientProperties.setNamespace("dev"); + PropertySource propertySource = new KubernetesClientConfigMapPropertySourceLocator(api, + configMapConfigProperties, kubernetesClientProperties).locate(new MockEnvironment()); + assertThat(propertySource.containsProperty("spring.cloud.kubernetes.configuration.watcher.refreshDelay")) + .isTrue(); + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapPropertySourceTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapPropertySourceTests.java new file mode 100644 index 00000000..f4def1fe --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapPropertySourceTests.java @@ -0,0 +1,127 @@ +/* + * 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.client.config; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.WireMock; +import io.kubernetes.client.openapi.ApiClient; +import io.kubernetes.client.openapi.Configuration; +import io.kubernetes.client.openapi.JSON; +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.util.ClientBuilder; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import org.springframework.mock.env.MockEnvironment; + +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.getRequestedFor; +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; +import static com.github.tomakehurst.wiremock.client.WireMock.verify; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Ryan Baxter + */ +class KubernetesClientConfigMapPropertySourceTests { + + private static final V1ConfigMapList PROPERTIES_CONFIGMAP_LIST = new V1ConfigMapList() + .addItemsItem( + new V1ConfigMapBuilder() + .withMetadata(new V1ObjectMetaBuilder().withName("bootstrap-640").withNamespace("default") + .withResourceVersion("1").build()) + .addToData("application.properties", + "spring.cloud.kubernetes.configuration.watcher.refreshDelay=0\n" + + "logging.level.org.springframework.cloud.kubernetes=TRACE") + .build()); + + private static final V1ConfigMapList YAML_CONFIGMAP_LIST = new V1ConfigMapList() + .addItemsItem(new V1ConfigMapBuilder() + .withMetadata(new V1ObjectMetaBuilder().withName("bootstrap-641").withNamespace("default") + .withResourceVersion("1").build()) + .addToData("application.yaml", + "dummy:\n property:\n string2: \"a\"\n int2: 1\n bool2: true\n") + .build()); + + private static final String API = "/api/v1/namespaces/default/configmaps"; + + private static WireMockServer wireMockServer; + + @BeforeAll + public static void setup() { + wireMockServer = new WireMockServer(options().dynamicPort()); + + wireMockServer.start(); + WireMock.configureFor("localhost", wireMockServer.port()); + + ApiClient client = new ClientBuilder().setBasePath("http://localhost:" + wireMockServer.port()).build(); + client.setDebugging(true); + Configuration.setDefaultApiClient(client); + } + + @AfterAll + public static void after() { + wireMockServer.stop(); + } + + @AfterEach + public void afterEach() { + WireMock.reset(); + } + + @Test + public void propertiesFile() { + CoreV1Api api = new CoreV1Api(); + stubFor(get(API) + .willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(PROPERTIES_CONFIGMAP_LIST)))); + KubernetesClientConfigMapPropertySource propertySource = new KubernetesClientConfigMapPropertySource(api, + "bootstrap-640", "default", new MockEnvironment()); + verify(getRequestedFor(urlEqualTo(API))); + assertThat(propertySource.containsProperty("spring.cloud.kubernetes.configuration.watcher.refreshDelay")) + .isTrue(); + assertThat(propertySource.getProperty("spring.cloud.kubernetes.configuration.watcher.refreshDelay")) + .isEqualTo("0"); + assertThat(propertySource.containsProperty("logging.level.org.springframework.cloud.kubernetes")).isTrue(); + assertThat(propertySource.getProperty("logging.level.org.springframework.cloud.kubernetes")).isEqualTo("TRACE"); + + } + + @Test + public void yamlFile() { + CoreV1Api api = new CoreV1Api(); + stubFor(get(API).willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(YAML_CONFIGMAP_LIST)))); + KubernetesClientConfigMapPropertySource propertySource = new KubernetesClientConfigMapPropertySource(api, + "bootstrap-641", "default", new MockEnvironment()); + verify(getRequestedFor(urlEqualTo(API))); + assertThat(propertySource.containsProperty("dummy.property.string2")).isTrue(); + assertThat(propertySource.getProperty("dummy.property.string2")).isEqualTo("a"); + assertThat(propertySource.containsProperty("dummy.property.int2")).isTrue(); + assertThat(propertySource.getProperty("dummy.property.int2")).isEqualTo(1); + assertThat(propertySource.containsProperty("dummy.property.bool2")).isTrue(); + assertThat(propertySource.getProperty("dummy.property.bool2")).isEqualTo(true); + + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSecretsPropertySourceLocatorTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSecretsPropertySourceLocatorTests.java new file mode 100644 index 00000000..4c7eb5b8 --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSecretsPropertySourceLocatorTests.java @@ -0,0 +1,138 @@ +/* + * 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.client.config; + +import java.util.ArrayList; +import java.util.List; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.WireMock; +import io.kubernetes.client.openapi.ApiClient; +import io.kubernetes.client.openapi.Configuration; +import io.kubernetes.client.openapi.apis.CoreV1Api; +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.junit.jupiter.api.Test; + +import org.springframework.cloud.kubernetes.commons.KubernetesClientProperties; +import org.springframework.cloud.kubernetes.commons.config.SecretsConfigProperties; +import org.springframework.core.env.PropertySource; +import org.springframework.mock.env.MockEnvironment; + +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.assertj.core.api.Assertions.assertThat; + +/** + * @author Ryan Baxter + */ +class KubernetesClientSecretsPropertySourceLocatorTests { + + private static final String LIST_API = "/api/v1/secrets"; + + private static final String LIST_API_WITH_LABEL = "/api/v1/secrets?labelSelector=spring.cloud.kubernetes.secret%3Dtrue"; + + private static final String LIST_BODY = "{\n" + "\t\"kind\": \"SecretList\",\n" + "\t\"apiVersion\": \"v1\",\n" + + "\t\"metadata\": {\n" + "\t\t\"selfLink\": \"/api/v1/secrets\",\n" + + "\t\t\"resourceVersion\": \"163035\"\n" + "\t},\n" + "\t\"items\": [{\n" + "\t\t\t\"metadata\": {\n" + + "\t\t\t\t\"name\": \"db-secret\",\n" + "\t\t\t\t\"namespace\": \"default\",\n" + + "\t\t\t\t\"selfLink\": \"/api/v1/namespaces/default/secrets/db-secret\",\n" + + "\t\t\t\t\"uid\": \"59ba8e6a-a2d4-416c-b016-22597c193f23\",\n" + + "\t\t\t\t\"resourceVersion\": \"1462\",\n" + "\t\t\t\t\"creationTimestamp\": \"2020-10-28T14:45:02Z\",\n" + + "\t\t\t\t\"labels\": {\n" + "\t\t\t\t\t\"spring.cloud.kubernetes.secret\": \"true\"\n" + "\t\t\t\t}\n" + + "\t\t\t},\n" + "\t\t\t\"data\": {\n" + "\t\t\t\t\"password\": \"cDQ1NXcwcmQ=\",\n" + + "\t\t\t\t\"username\": \"dXNlcg==\"\n" + "\t\t\t},\n" + "\t\t\t\"type\": \"Opaque\"\n" + "\t\t},\n" + + "\t\t{\n" + "\t\t\t\"metadata\": {\n" + "\t\t\t\t\"name\": \"rabbit-password\",\n" + + "\t\t\t\t\"namespace\": \"default\",\n" + + "\t\t\t\t\"selfLink\": \"/api/v1/namespaces/default/secrets/rabbit-password\",\n" + + "\t\t\t\t\"uid\": \"bc211cb4-e7ff-4556-b26e-c54911301740\",\n" + + "\t\t\t\t\"resourceVersion\": \"162708\",\n" + + "\t\t\t\t\"creationTimestamp\": \"2020-10-29T19:47:36Z\",\n" + "\t\t\t\t\"labels\": {\n" + + "\t\t\t\t\t\"spring.cloud.kubernetes.secret\": \"true\"\n" + "\t\t\t\t},\n" + + "\t\t\t\t\"annotations\": {\n" + + "\t\t\t\t\t\"kubectl.kubernetes.io/last-applied-configuration\": \"{\\\"apiVersion\\\":\\\"v1\\\",\\\"data\\\":{\\\"spring.rabbitmq.password\\\":\\\"password\\\"},\\\"kind\\\":\\\"Secret\\\",\\\"metadata\\\":{\\\"annotations\\\":{},\\\"labels\\\":{\\\"spring.cloud.kubernetes.secret\\\":\\\"true\\\"},\\\"name\\\":\\\"rabbit-password\\\",\\\"namespace\\\":\\\"default\\\"},\\\"type\\\":\\\"Opaque\\\"}\\n\"\n" + + "\t\t\t\t}\n" + "\t\t\t},\n" + "\t\t\t\"data\": {\n" + + "\t\t\t\t\"spring.rabbitmq.password\": \"cGFzc3dvcmQ=\"\n" + "\t\t\t},\n" + "\t\t\t\"type\": \"Opaque\"\n" + + "\t\t}\n" + "\t]\n" + "}"; + + private static WireMockServer wireMockServer; + + @BeforeAll + public static void setup() { + wireMockServer = new WireMockServer(options().dynamicPort()); + + wireMockServer.start(); + WireMock.configureFor("localhost", wireMockServer.port()); + + ApiClient client = new ClientBuilder().setBasePath("http://localhost:" + wireMockServer.port()).build(); + client.setDebugging(true); + Configuration.setDefaultApiClient(client); + } + + @AfterAll + public static void after() { + wireMockServer.stop(); + } + + @AfterEach + public void afterEach() { + WireMock.reset(); + } + + @Test + void getLocateWithSources() { + CoreV1Api api = new CoreV1Api(); + stubFor(get(LIST_API).willReturn(aResponse().withStatus(200).withBody(LIST_BODY))); + SecretsConfigProperties secretsConfigProperties = new SecretsConfigProperties(); + SecretsConfigProperties.Source source1 = new SecretsConfigProperties.Source(); + source1.setName("db-secret"); + source1.setNamespace(""); + SecretsConfigProperties.Source source2 = new SecretsConfigProperties.Source(); + source2.setName("rabbit-password"); + source2.setNamespace(""); + List sources = new ArrayList<>(); + sources.add(source1); + sources.add(source2); + secretsConfigProperties.setName("app"); + secretsConfigProperties.setNamespace(""); + secretsConfigProperties.setSources(sources); + secretsConfigProperties.setEnableApi(true); + PropertySource propertySource = new KubernetesClientSecretsPropertySourceLocator(api, + new KubernetesClientProperties(), secretsConfigProperties).locate(new MockEnvironment()); + assertThat(propertySource.containsProperty("password")).isTrue(); + assertThat(propertySource.getProperty("password")).isEqualTo("p455w0rd"); + } + + @Test + void getLocateWithOutSources() { + CoreV1Api api = new CoreV1Api(); + stubFor(get(LIST_API).willReturn(aResponse().withStatus(200).withBody(LIST_BODY))); + SecretsConfigProperties secretsConfigProperties = new SecretsConfigProperties(); + secretsConfigProperties.setName("db-secret"); + secretsConfigProperties.setNamespace(""); + secretsConfigProperties.setEnableApi(true); + PropertySource propertySource = new KubernetesClientSecretsPropertySourceLocator(api, + new KubernetesClientProperties(), secretsConfigProperties).locate(new MockEnvironment()); + assertThat(propertySource.containsProperty("password")).isTrue(); + assertThat(propertySource.getProperty("password")).isEqualTo("p455w0rd"); + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSecretsPropertySourceTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSecretsPropertySourceTests.java new file mode 100644 index 00000000..d94ce47c --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSecretsPropertySourceTests.java @@ -0,0 +1,145 @@ +/* + * 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.client.config; + +import java.util.HashMap; +import java.util.Map; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.WireMock; +import io.kubernetes.client.openapi.ApiClient; +import io.kubernetes.client.openapi.Configuration; +import io.kubernetes.client.openapi.JSON; +import io.kubernetes.client.openapi.apis.CoreV1Api; +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 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.junit.jupiter.api.Test; + +import org.springframework.mock.env.MockEnvironment; + +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.assertj.core.api.Assertions.assertThat; + +/** + * @author Ryan Baxter + */ +class KubernetesClientSecretsPropertySourceTests { + + private static final String API = "/api/v1/namespaces/default/secrets"; + + private static final V1SecretList SECRET_LIST = new V1SecretListBuilder().addToItems(new V1SecretBuilder() + .withMetadata(new V1ObjectMetaBuilder().withName("db-secret").withResourceVersion("0") + .withNamespace("default").build()) + .addToData("password", "p455w0rd".getBytes()).addToData("username", "user".getBytes()).build()).build(); + + private static final String LIST_API = "/api/v1/secrets"; + + private static final String LIST_API_WITH_LABEL = "/api/v1/secrets?labelSelector=spring.cloud.kubernetes.secret%3Dtrue"; + + private static final String LIST_BODY = "{\n" + "\t\"kind\": \"SecretList\",\n" + "\t\"apiVersion\": \"v1\",\n" + + "\t\"metadata\": {\n" + "\t\t\"selfLink\": \"/api/v1/secrets\",\n" + + "\t\t\"resourceVersion\": \"163035\"\n" + "\t},\n" + "\t\"items\": [{\n" + "\t\t\t\"metadata\": {\n" + + "\t\t\t\t\"name\": \"db-secret\",\n" + "\t\t\t\t\"namespace\": \"default\",\n" + + "\t\t\t\t\"selfLink\": \"/api/v1/namespaces/default/secrets/db-secret\",\n" + + "\t\t\t\t\"uid\": \"59ba8e6a-a2d4-416c-b016-22597c193f23\",\n" + + "\t\t\t\t\"resourceVersion\": \"1462\",\n" + "\t\t\t\t\"creationTimestamp\": \"2020-10-28T14:45:02Z\",\n" + + "\t\t\t\t\"labels\": {\n" + "\t\t\t\t\t\"spring.cloud.kubernetes.secret\": \"true\"\n" + "\t\t\t\t}\n" + + "\t\t\t},\n" + "\t\t\t\"data\": {\n" + "\t\t\t\t\"password\": \"cDQ1NXcwcmQ=\",\n" + + "\t\t\t\t\"username\": \"dXNlcg==\"\n" + "\t\t\t},\n" + "\t\t\t\"type\": \"Opaque\"\n" + "\t\t},\n" + + "\t\t{\n" + "\t\t\t\"metadata\": {\n" + "\t\t\t\t\"name\": \"rabbit-password\",\n" + + "\t\t\t\t\"namespace\": \"default\",\n" + + "\t\t\t\t\"selfLink\": \"/api/v1/namespaces/default/secrets/rabbit-password\",\n" + + "\t\t\t\t\"uid\": \"bc211cb4-e7ff-4556-b26e-c54911301740\",\n" + + "\t\t\t\t\"resourceVersion\": \"162708\",\n" + + "\t\t\t\t\"creationTimestamp\": \"2020-10-29T19:47:36Z\",\n" + "\t\t\t\t\"labels\": {\n" + + "\t\t\t\t\t\"spring.cloud.kubernetes.secret\": \"true\"\n" + "\t\t\t\t},\n" + + "\t\t\t\t\"annotations\": {\n" + + "\t\t\t\t\t\"kubectl.kubernetes.io/last-applied-configuration\": \"{\\\"apiVersion\\\":\\\"v1\\\",\\\"data\\\":{\\\"spring.rabbitmq.password\\\":\\\"password\\\"},\\\"kind\\\":\\\"Secret\\\",\\\"metadata\\\":{\\\"annotations\\\":{},\\\"labels\\\":{\\\"spring.cloud.kubernetes.secret\\\":\\\"true\\\"},\\\"name\\\":\\\"rabbit-password\\\",\\\"namespace\\\":\\\"default\\\"},\\\"type\\\":\\\"Opaque\\\"}\\n\"\n" + + "\t\t\t\t}\n" + "\t\t\t},\n" + "\t\t\t\"data\": {\n" + + "\t\t\t\t\"spring.rabbitmq.password\": \"cGFzc3dvcmQ=\"\n" + "\t\t\t},\n" + "\t\t\t\"type\": \"Opaque\"\n" + + "\t\t}\n" + "\t]\n" + "}"; + + private static WireMockServer wireMockServer; + + @BeforeAll + public static void setup() { + wireMockServer = new WireMockServer(options().dynamicPort()); + + wireMockServer.start(); + WireMock.configureFor("localhost", wireMockServer.port()); + + ApiClient client = new ClientBuilder().setBasePath("http://localhost:" + wireMockServer.port()).build(); + client.setDebugging(true); + Configuration.setDefaultApiClient(client); + } + + @AfterAll + public static void after() { + wireMockServer.stop(); + } + + @AfterEach + public void afterEach() { + WireMock.reset(); + } + + @Test + public void secretsTest() { + CoreV1Api api = new CoreV1Api(); + stubFor(get(API).willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(SECRET_LIST)))); + KubernetesClientSecretsPropertySource propertySource = new KubernetesClientSecretsPropertySource(api, + "db-secret", "default", new MockEnvironment(), new HashMap<>()); + assertThat(propertySource.containsProperty("password")).isTrue(); + assertThat(propertySource.getProperty("password")).isEqualTo("p455w0rd"); + assertThat(propertySource.containsProperty("username")).isTrue(); + assertThat(propertySource.getProperty("username")).isEqualTo("user"); + } + + @Test + public void secretsNullNamespaceTest() { + CoreV1Api api = new CoreV1Api(); + stubFor(get(LIST_API).willReturn(aResponse().withStatus(200).withBody(LIST_BODY))); + KubernetesClientSecretsPropertySource propertySource = new KubernetesClientSecretsPropertySource(api, + "db-secret", null, new MockEnvironment(), new HashMap<>()); + assertThat(propertySource.containsProperty("password")).isTrue(); + assertThat(propertySource.getProperty("password")).isEqualTo("p455w0rd"); + assertThat(propertySource.containsProperty("username")).isTrue(); + assertThat(propertySource.getProperty("username")).isEqualTo("user"); + } + + @Test + public void secretLabelsTest() { + CoreV1Api api = new CoreV1Api(); + stubFor(get(LIST_API_WITH_LABEL).willReturn(aResponse().withStatus(200).withBody(LIST_BODY))); + Map labels = new HashMap<>(); + labels.put("spring.cloud.kubernetes.secret", "true"); + KubernetesClientSecretsPropertySource propertySource = new KubernetesClientSecretsPropertySource(api, null, + null, new MockEnvironment(), labels); + assertThat(propertySource.containsProperty("spring.rabbitmq.password")).isTrue(); + assertThat(propertySource.getProperty("spring.rabbitmq.password")).isEqualTo("password"); + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientEventBasedConfigMapChangeDetectorTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientEventBasedConfigMapChangeDetectorTests.java new file mode 100644 index 00000000..b6ce3cb5 --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientEventBasedConfigMapChangeDetectorTests.java @@ -0,0 +1,159 @@ +/* + * 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.client.config.reload; + +import java.time.Duration; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.WireMock; +import com.google.gson.Gson; +import io.kubernetes.client.informer.EventType; +import io.kubernetes.client.openapi.ApiClient; +import io.kubernetes.client.openapi.JSON; +import io.kubernetes.client.openapi.apis.CoreV1Api; +import io.kubernetes.client.openapi.models.V1ConfigMap; +import io.kubernetes.client.openapi.models.V1ConfigMapList; +import io.kubernetes.client.openapi.models.V1ListMeta; +import io.kubernetes.client.openapi.models.V1ObjectMeta; +import io.kubernetes.client.util.ClientBuilder; +import io.kubernetes.client.util.Watch; +import okhttp3.OkHttpClient; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigMapPropertySource; +import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigMapPropertySourceLocator; +import org.springframework.cloud.kubernetes.commons.KubernetesClientProperties; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy; +import org.springframework.mock.env.MockPropertySource; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; +import static com.github.tomakehurst.wiremock.client.WireMock.urlMatching; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; +import static com.github.tomakehurst.wiremock.stubbing.Scenario.STARTED; +import static org.awaitility.Awaitility.await; +import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * @author Ryan Baxter + */ +class KubernetesClientEventBasedConfigMapChangeDetectorTests { + + private static WireMockServer wireMockServer; + + @BeforeAll + public static void setup() { + wireMockServer = new WireMockServer(options().dynamicPort()); + wireMockServer.start(); + WireMock.configureFor("localhost", wireMockServer.port()); + + } + + @AfterAll + public static void after() { + wireMockServer.stop(); + } + + @AfterEach + public void afterEach() { + WireMock.reset(); + } + + @Test + void watch() throws Exception { + Map data = new HashMap<>(); + data.put("application.properties", "spring.cloud.kubernetes.configuration.watcher.refreshDelay=0\n" + + "logging.level.org.springframework.cloud.kubernetes=TRACE"); + Map updateData = new HashMap<>(); + updateData.put("application.properties", "spring.cloud.kubernetes.configuration.watcher.refreshDelay=1\n" + + "logging.level.org.springframework.cloud.kubernetes=TRACE"); + V1ConfigMap applicationConfig = new V1ConfigMap().kind("ConfigMap") + .metadata(new V1ObjectMeta().namespace("default").name("bar1")).data(data); + V1ConfigMapList configMapList = new V1ConfigMapList().metadata(new V1ListMeta().resourceVersion("0")) + .items(Arrays.asList(applicationConfig)); + stubFor(get(urlMatching("^/api/v1/namespaces/default/configmaps.*")).inScenario("watch") + .whenScenarioStateIs(STARTED).withQueryParam("watch", equalTo("false")) + .willReturn(aResponse().withStatus(200).withBody(new Gson().toJson(configMapList))) + .willSetStateTo("update")); + + Watch.Response watchResponse = new Watch.Response<>(EventType.MODIFIED.name(), new V1ConfigMap() + .kind("ConfigMap").metadata(new V1ObjectMeta().namespace("default").name("bar1")).data(updateData)); + stubFor(get(urlMatching("^/api/v1/namespaces/default/configmaps.*")).inScenario("watch") + .whenScenarioStateIs("update").withQueryParam("watch", equalTo("true")) + .willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(watchResponse))) + .willSetStateTo("add")); + + stubFor(get(urlMatching("^/api/v1/namespaces/default/configmaps.*")).inScenario("watch") + .whenScenarioStateIs("add").withQueryParam("watch", equalTo("true")) + .willReturn(aResponse().withStatus(200) + .withBody(new JSON().serialize(new Watch.Response<>(EventType.ADDED.name(), + new V1ConfigMap().kind("ConfigMap") + .metadata(new V1ObjectMeta().namespace("default").name("bar3")) + .putDataItem("application.properties", "debug=true"))))) + .willSetStateTo("delete")); + + stubFor(get(urlMatching("^/api/v1/namespaces/default/configmaps.*")).inScenario("watch") + .whenScenarioStateIs("delete").withQueryParam("watch", equalTo("true")) + .willReturn(aResponse().withStatus(200) + .withBody(new JSON().serialize(new Watch.Response<>(EventType.DELETED.name(), + new V1ConfigMap().kind("ConfigMap") + .metadata(new V1ObjectMeta().namespace("default").name("bar1")) + .putDataItem("application.properties", "debug=true"))))) + .willSetStateTo("done")); + + stubFor(get(urlMatching("^/api/v1/namespaces/default/configmaps.*")).inScenario("watch") + .whenScenarioStateIs("done").withQueryParam("watch", equalTo("true")) + .willReturn(aResponse().withStatus(200))); + ApiClient apiClient = new ClientBuilder().setBasePath("http://localhost:" + wireMockServer.port()).build(); + OkHttpClient httpClient = apiClient.getHttpClient().newBuilder().readTimeout(0, TimeUnit.SECONDS).build(); + apiClient.setHttpClient(httpClient); + CoreV1Api coreV1Api = new CoreV1Api(apiClient); + ConfigurationUpdateStrategy strategy = mock(ConfigurationUpdateStrategy.class); + when(strategy.getName()).thenReturn("strategy"); + KubernetesMockEnvironment environment = new KubernetesMockEnvironment( + mock(KubernetesClientConfigMapPropertySource.class)).withProperty("debug", "true"); + KubernetesClientConfigMapPropertySourceLocator locator = mock( + KubernetesClientConfigMapPropertySourceLocator.class); + when(locator.locate(environment)).thenReturn(new MockPropertySource().withProperty("debug", "false")); + KubernetesClientProperties kubernetesClientProperties = new KubernetesClientProperties(); + kubernetesClientProperties.setNamespace("default"); + KubernetesClientEventBasedConfigMapChangeDetector changeDetector = new KubernetesClientEventBasedConfigMapChangeDetector( + coreV1Api, environment, new ConfigReloadProperties(), strategy, locator, kubernetesClientProperties); + + Thread controllerThread = new Thread(changeDetector::watch); + controllerThread.setDaemon(true); + controllerThread.start(); + await().timeout(Duration.ofSeconds(5)) + .until(() -> Mockito.mockingDetails(strategy).getInvocations().size() > 4); + verify(strategy, atLeast(3)).reload(); + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientEventBasedSecretsChangeDetectorTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientEventBasedSecretsChangeDetectorTests.java new file mode 100644 index 00000000..2ccd60c2 --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientEventBasedSecretsChangeDetectorTests.java @@ -0,0 +1,157 @@ +/* + * 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.client.config.reload; + +import java.time.Duration; +import java.util.Arrays; +import java.util.Base64; +import java.util.concurrent.TimeUnit; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.WireMock; +import com.google.gson.Gson; +import io.kubernetes.client.informer.EventType; +import io.kubernetes.client.openapi.ApiClient; +import io.kubernetes.client.openapi.JSON; +import io.kubernetes.client.openapi.apis.CoreV1Api; +import io.kubernetes.client.openapi.models.V1ListMeta; +import io.kubernetes.client.openapi.models.V1ObjectMeta; +import io.kubernetes.client.openapi.models.V1Secret; +import io.kubernetes.client.openapi.models.V1SecretList; +import io.kubernetes.client.util.ClientBuilder; +import io.kubernetes.client.util.Watch; +import okhttp3.OkHttpClient; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import org.springframework.cloud.kubernetes.client.config.KubernetesClientSecretsPropertySource; +import org.springframework.cloud.kubernetes.client.config.KubernetesClientSecretsPropertySourceLocator; +import org.springframework.cloud.kubernetes.commons.KubernetesClientProperties; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy; +import org.springframework.mock.env.MockPropertySource; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; +import static com.github.tomakehurst.wiremock.client.WireMock.urlMatching; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; +import static com.github.tomakehurst.wiremock.stubbing.Scenario.STARTED; +import static org.awaitility.Awaitility.await; +import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * @author Ryan Baxter + */ +class KubernetesClientEventBasedSecretsChangeDetectorTests { + + private static WireMockServer wireMockServer; + + @BeforeAll + public static void setup() { + wireMockServer = new WireMockServer(options().dynamicPort()); + wireMockServer.start(); + WireMock.configureFor("localhost", wireMockServer.port()); + + } + + @AfterAll + public static void after() { + wireMockServer.stop(); + } + + @AfterEach + public void afterEach() { + WireMock.reset(); + } + + @Test + void watch() { + + V1Secret dbPassword = new V1Secret().kind("Secret").metadata(new V1ObjectMeta().name("db-password")) + .putStringDataItem("password", Base64.getEncoder().encodeToString("p455w0rd".getBytes())) + .putStringDataItem("username", Base64.getEncoder().encodeToString("user".getBytes())); + V1Secret dbPasswordUpdated = new V1Secret().kind("Secret").metadata(new V1ObjectMeta().name("db-password")) + .putStringDataItem("password", Base64.getEncoder().encodeToString("p455w0rd2".getBytes())) + .putStringDataItem("username", Base64.getEncoder().encodeToString("user".getBytes())); + V1SecretList secretList = new V1SecretList().kind("SecretList").metadata(new V1ListMeta().resourceVersion("0")) + .items(Arrays.asList(dbPassword)); + + stubFor(get(urlMatching("^/api/v1/namespaces/default/secrets.*")).inScenario("watch") + .whenScenarioStateIs(STARTED).withQueryParam("watch", equalTo("false")) + .willReturn(aResponse().withStatus(200).withBody(new Gson().toJson(secretList))) + .willSetStateTo("update")); + + Watch.Response watchResponse = new Watch.Response<>(EventType.MODIFIED.name(), dbPasswordUpdated); + stubFor(get(urlMatching("/api/v1/namespaces/default/secrets.*")).inScenario("watch") + .whenScenarioStateIs("update").withQueryParam("watch", equalTo("true")) + .willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(watchResponse))) + .willSetStateTo("add")); + + stubFor(get(urlMatching("/api/v1/namespaces/default/secrets.*")).inScenario("watch").whenScenarioStateIs("add") + .withQueryParam("watch", equalTo("true")) + .willReturn(aResponse().withStatus(200) + .withBody(new JSON().serialize(new Watch.Response<>(EventType.ADDED.name(), + new V1Secret().kind("Secret").metadata(new V1ObjectMeta().name("rabbit-password")) + .putDataItem("rabbit-pw", Base64.getEncoder().encode("password".getBytes())))))) + .willSetStateTo("delete")); + + stubFor(get(urlMatching("/api/v1/namespaces/default/secrets.*")).inScenario("watch") + .whenScenarioStateIs("delete").withQueryParam("watch", equalTo("true")) + .willReturn(aResponse().withStatus(200) + .withBody(new JSON().serialize(new Watch.Response<>(EventType.DELETED.name(), + new V1Secret().kind("Secret").metadata(new V1ObjectMeta().name("rabbit-password")) + .putDataItem("rabbit-pw", Base64.getEncoder().encode("password".getBytes())))))) + .willSetStateTo("done")); + + stubFor(get(urlMatching("/api/v1/namespaces/default/secrets.*")).inScenario("watch").whenScenarioStateIs("done") + .withQueryParam("watch", equalTo("true")).willReturn(aResponse().withStatus(200))); + + ApiClient apiClient = new ClientBuilder().setBasePath("http://localhost:" + wireMockServer.port()).build(); + OkHttpClient httpClient = apiClient.getHttpClient().newBuilder().readTimeout(0, TimeUnit.SECONDS).build(); + apiClient.setHttpClient(httpClient); + CoreV1Api coreV1Api = new CoreV1Api(apiClient); + ConfigurationUpdateStrategy strategy = mock(ConfigurationUpdateStrategy.class); + when(strategy.getName()).thenReturn("strategy"); + KubernetesMockEnvironment environment = new KubernetesMockEnvironment( + mock(KubernetesClientSecretsPropertySource.class)).withProperty("db-password", "p455w0rd"); + KubernetesClientSecretsPropertySourceLocator locator = mock(KubernetesClientSecretsPropertySourceLocator.class); + when(locator.locate(environment)).thenReturn(new MockPropertySource().withProperty("db-password", "p455w0rd2")); + ConfigReloadProperties properties = new ConfigReloadProperties(); + properties.setMonitoringSecrets(true); + KubernetesClientProperties kubernetesClientProperties = new KubernetesClientProperties(); + kubernetesClientProperties.setNamespace("default"); + KubernetesClientEventBasedSecretsChangeDetector changeDetector = new KubernetesClientEventBasedSecretsChangeDetector( + coreV1Api, environment, properties, strategy, locator, kubernetesClientProperties); + + Thread controllerThread = new Thread(changeDetector::watch); + controllerThread.setDaemon(true); + controllerThread.start(); + + await().timeout(Duration.ofSeconds(300)) + .until(() -> Mockito.mockingDetails(strategy).getInvocations().size() > 4); + verify(strategy, atLeast(3)).reload(); + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesMockEnvironment.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesMockEnvironment.java new file mode 100644 index 00000000..f951f627 --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesMockEnvironment.java @@ -0,0 +1,55 @@ +/* + * 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.client.config.reload; + +import java.util.HashMap; +import java.util.Map; + +import org.springframework.cloud.kubernetes.client.config.KubernetesClientSecretsPropertySource; +import org.springframework.core.env.AbstractEnvironment; +import org.springframework.core.env.PropertySource; + +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * @author Ryan Baxter + */ +public class KubernetesMockEnvironment extends AbstractEnvironment { + + private PropertySource propertySource = mock(KubernetesClientSecretsPropertySource.class); + + private Map map = new HashMap<>(); + + public KubernetesMockEnvironment(PropertySource mockPropertySource) { + this.propertySource = mockPropertySource; + this.getPropertySources().addLast(this.propertySource); + when(propertySource.getSource()).thenReturn(map); + } + + public void setProperty(String key, String value) { + map.put(key, value); + when(propertySource.getProperty(eq(key))).thenReturn(value); + } + + public KubernetesMockEnvironment withProperty(String key, String value) { + this.setProperty(key, value); + return this; + } + +} diff --git a/spring-cloud-kubernetes-commons/pom.xml b/spring-cloud-kubernetes-commons/pom.xml index b67c8c5f..6db6bab1 100644 --- a/spring-cloud-kubernetes-commons/pom.xml +++ b/spring-cloud-kubernetes-commons/pom.xml @@ -26,6 +26,14 @@ spring-boot-configuration-processor true + + org.springframework.cloud + spring-cloud-context + + + javax.annotation + javax.annotation-api + org.springframework.boot spring-boot-starter-test diff --git a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/AbstractConfigProperties.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/AbstractConfigProperties.java similarity index 95% rename from spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/AbstractConfigProperties.java rename to spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/AbstractConfigProperties.java index c1c83ead..a3f9cbbe 100644 --- a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/AbstractConfigProperties.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/AbstractConfigProperties.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.config; +package org.springframework.cloud.kubernetes.commons.config; /** * Abstraction over configuration properties. diff --git a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/ConfigMapConfigProperties.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapConfigProperties.java similarity index 97% rename from spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/ConfigMapConfigProperties.java rename to spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapConfigProperties.java index cb287f40..443a401a 100644 --- a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/ConfigMapConfigProperties.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapConfigProperties.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.config; +package org.springframework.cloud.kubernetes.commons.config; import java.util.Collections; import java.util.List; @@ -137,7 +137,7 @@ public class ConfigMapConfigProperties extends AbstractConfigProperties { } - static class NormalizedSource { + public static class NormalizedSource { private final String name; diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapPropertySource.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapPropertySource.java new file mode 100644 index 00000000..591c8b7c --- /dev/null +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapPropertySource.java @@ -0,0 +1,132 @@ +/* + * 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.commons.config; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.stream.Collectors; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.core.env.Environment; +import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.StandardEnvironment; + +import static org.springframework.cloud.kubernetes.commons.config.PropertySourceUtils.KEY_VALUE_TO_PROPERTIES; +import static org.springframework.cloud.kubernetes.commons.config.PropertySourceUtils.PROPERTIES_TO_MAP; +import static org.springframework.cloud.kubernetes.commons.config.PropertySourceUtils.throwingMerger; +import static org.springframework.cloud.kubernetes.commons.config.PropertySourceUtils.yamlParserGenerator; + +/** + * A {@link MapPropertySource} that uses Kubernetes config maps. + * + * @author Ioannis Canellos + * @author Ali Shahbour + * @author Michael Moudatsos + */ +public abstract class ConfigMapPropertySource extends MapPropertySource { + + private static final Log LOG = LogFactory.getLog(ConfigMapPropertySource.class); + + protected static final String APPLICATION_YML = "application.yml"; + + protected static final String APPLICATION_YAML = "application.yaml"; + + protected static final String APPLICATION_PROPERTIES = "application.properties"; + + protected static final String PREFIX = "configmap"; + + public ConfigMapPropertySource(String name, Map source) { + super(name, source); + } + + protected static Environment createEnvironmentWithActiveProfiles(String[] activeProfiles) { + StandardEnvironment environment = new StandardEnvironment(); + environment.setActiveProfiles(activeProfiles); + return environment; + } + + protected static String getName(String name, String namespace) { + return new StringBuilder().append(PREFIX).append(Constants.PROPERTY_SOURCE_NAME_SEPARATOR).append(name) + .append(Constants.PROPERTY_SOURCE_NAME_SEPARATOR).append(namespace).toString(); + } + + protected static Map processAllEntries(Map input, Environment environment) { + + Set> entrySet = input.entrySet(); + if (entrySet.size() == 1) { + // we handle the case where the configmap contains a single "file" + // in this case we don't care what the name of t he file is + Entry singleEntry = entrySet.iterator().next(); + String propertyName = singleEntry.getKey(); + String propertyValue = singleEntry.getValue(); + if (propertyName.endsWith(".yml") || propertyName.endsWith(".yaml")) { + if (LOG.isDebugEnabled()) { + LOG.debug("The single property with name: [" + propertyName + "] will be treated as a yaml file"); + } + + return yamlParserGenerator(environment).andThen(PROPERTIES_TO_MAP).apply(propertyValue); + } + else if (propertyName.endsWith(".properties")) { + if (LOG.isDebugEnabled()) { + LOG.debug("The single property with name: [" + propertyName + + "] will be treated as a properties file"); + } + + return KEY_VALUE_TO_PROPERTIES.andThen(PROPERTIES_TO_MAP).apply(propertyValue); + } + else { + return defaultProcessAllEntries(input, environment); + } + } + + return defaultProcessAllEntries(input, environment); + } + + protected static Map defaultProcessAllEntries(Map input, Environment environment) { + + return input.entrySet().stream().map(e -> extractProperties(e.getKey(), e.getValue(), environment)) + .filter(m -> !m.isEmpty()).flatMap(m -> m.entrySet().stream()) + .collect(Collectors.toMap(Entry::getKey, Entry::getValue, throwingMerger(), LinkedHashMap::new)); + } + + protected static Map extractProperties(String resourceName, String content, + Environment environment) { + + if (resourceName.equals(APPLICATION_YAML) || resourceName.equals(APPLICATION_YML)) { + return yamlParserGenerator(environment).andThen(PROPERTIES_TO_MAP).apply(content); + } + else if (resourceName.equals(APPLICATION_PROPERTIES)) { + return KEY_VALUE_TO_PROPERTIES.andThen(PROPERTIES_TO_MAP).apply(content); + } + + return new LinkedHashMap() { + { + put(resourceName, content); + } + }; + } + + protected static Map asObjectMap(Map source) { + return source.entrySet().stream() + .collect(Collectors.toMap(Entry::getKey, Entry::getValue, throwingMerger(), LinkedHashMap::new)); + } + +} diff --git a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/ConfigMapPropertySourceLocator.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapPropertySourceLocator.java similarity index 72% rename from spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/ConfigMapPropertySourceLocator.java rename to spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapPropertySourceLocator.java index a1212ad2..a450f63a 100644 --- a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/ConfigMapPropertySourceLocator.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapPropertySourceLocator.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.config; +package org.springframework.cloud.kubernetes.commons.config; import java.io.IOException; import java.nio.file.Files; @@ -22,26 +22,23 @@ import java.nio.file.Paths; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.function.Function; -import io.fabric8.kubernetes.api.builder.Function; -import io.fabric8.kubernetes.client.KubernetesClient; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.cloud.bootstrap.config.PropertySourceLocator; -import org.springframework.cloud.kubernetes.config.ConfigMapConfigProperties.NormalizedSource; -import org.springframework.core.annotation.Order; +import org.springframework.cloud.kubernetes.commons.config.ConfigMapConfigProperties.NormalizedSource; import org.springframework.core.env.CompositePropertySource; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.Environment; import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.PropertySource; -import static org.springframework.cloud.kubernetes.config.ConfigUtils.getApplicationName; -import static org.springframework.cloud.kubernetes.config.ConfigUtils.getApplicationNamespace; -import static org.springframework.cloud.kubernetes.config.PropertySourceUtils.KEY_VALUE_TO_PROPERTIES; -import static org.springframework.cloud.kubernetes.config.PropertySourceUtils.PROPERTIES_TO_MAP; -import static org.springframework.cloud.kubernetes.config.PropertySourceUtils.yamlParserGenerator; +import static org.springframework.cloud.kubernetes.commons.config.ConfigUtils.getApplicationName; +import static org.springframework.cloud.kubernetes.commons.config.PropertySourceUtils.KEY_VALUE_TO_PROPERTIES; +import static org.springframework.cloud.kubernetes.commons.config.PropertySourceUtils.PROPERTIES_TO_MAP; +import static org.springframework.cloud.kubernetes.commons.config.PropertySourceUtils.yamlParserGenerator; /** * A {@link PropertySourceLocator} that uses config maps. @@ -49,17 +46,13 @@ import static org.springframework.cloud.kubernetes.config.PropertySourceUtils.ya * @author Ioannis Canellos * @author Michael Moudatsos */ -@Order(0) -public class ConfigMapPropertySourceLocator implements PropertySourceLocator { +public abstract class ConfigMapPropertySourceLocator implements PropertySourceLocator { private static final Log LOG = LogFactory.getLog(ConfigMapPropertySourceLocator.class); - private final KubernetesClient client; + protected final ConfigMapConfigProperties properties; - private final ConfigMapConfigProperties properties; - - public ConfigMapPropertySourceLocator(KubernetesClient client, ConfigMapConfigProperties properties) { - this.client = client; + public ConfigMapPropertySourceLocator(ConfigMapConfigProperties properties) { this.properties = properties; } @@ -68,7 +61,7 @@ public class ConfigMapPropertySourceLocator implements PropertySourceLocator { if (environment instanceof ConfigurableEnvironment) { ConfigurableEnvironment env = (ConfigurableEnvironment) environment; - List sources = this.properties.determineSources(); + List sources = this.properties.determineSources(); CompositePropertySource composite = new CompositePropertySource("composite-configmap"); if (this.properties.isEnableApi()) { sources.forEach(s -> composite.addFirstPropertySource(getMapPropertySourceForSingleConfigMap(env, s))); @@ -85,12 +78,13 @@ public class ConfigMapPropertySourceLocator implements PropertySourceLocator { NormalizedSource normalizedSource) { String configurationTarget = this.properties.getConfigurationTarget(); - return new ConfigMapPropertySource(this.client, - getApplicationName(environment, normalizedSource.getName(), configurationTarget), - getApplicationNamespace(this.client, normalizedSource.getNamespace(), configurationTarget), - environment); + return getMapPropertySource(getApplicationName(environment, normalizedSource.getName(), configurationTarget), + normalizedSource, configurationTarget, environment); } + protected abstract MapPropertySource getMapPropertySource(String name, NormalizedSource normalizedSource, + String configurationTarget, ConfigurableEnvironment environment); + private void addPropertySourcesFromPaths(Environment environment, CompositePropertySource composite) { this.properties.getPaths().stream().map(Paths::get).peek(p -> { if (!Files.exists(p)) { diff --git a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/ConfigUtils.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigUtils.java similarity index 66% rename from spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/ConfigUtils.java rename to spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigUtils.java index d06e33ab..6ebf5c61 100644 --- a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/ConfigUtils.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigUtils.java @@ -14,17 +14,16 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.config; +package org.springframework.cloud.kubernetes.commons.config; -import io.fabric8.kubernetes.client.KubernetesClient; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.core.env.Environment; import org.springframework.util.StringUtils; -import static org.springframework.cloud.kubernetes.config.Constants.FALLBACK_APPLICATION_NAME; -import static org.springframework.cloud.kubernetes.config.Constants.SPRING_APPLICATION_NAME; +import static org.springframework.cloud.kubernetes.commons.config.Constants.FALLBACK_APPLICATION_NAME; +import static org.springframework.cloud.kubernetes.commons.config.Constants.SPRING_APPLICATION_NAME; /** * Utility class that works with configuration properties. @@ -36,7 +35,6 @@ public final class ConfigUtils { private static final Log LOG = LogFactory.getLog(ConfigUtils.class); private ConfigUtils() { - throw new IllegalStateException("Can't instantiate a utility class"); } public static String getApplicationName(Environment env, String configName, String configurationTarget) { @@ -50,15 +48,4 @@ public final class ConfigUtils { return configName; } - public static String getApplicationNamespace(KubernetesClient client, String configNamespace, - String configurationTarget) { - if (StringUtils.isEmpty(configNamespace)) { - LOG.debug(configurationTarget + " namespace has not been set, taking it from client (ns=" - + client.getNamespace() + ")"); - configNamespace = client.getNamespace(); - } - - return configNamespace; - } - } diff --git a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/Constants.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/Constants.java similarity index 60% rename from spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/Constants.java rename to spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/Constants.java index 8fdd8a3f..bd98ab3e 100644 --- a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/Constants.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/Constants.java @@ -14,13 +14,24 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.config; +package org.springframework.cloud.kubernetes.commons.config; -final class Constants { +public final class Constants { - static final String SPRING_APPLICATION_NAME = "spring.application.name"; - static final String FALLBACK_APPLICATION_NAME = "application"; - static final String PROPERTY_SOURCE_NAME_SEPARATOR = "."; + /** + * Spring application name property. + */ + public static final String SPRING_APPLICATION_NAME = "spring.application.name"; + + /** + * Default application name. + */ + public static final String FALLBACK_APPLICATION_NAME = "application"; + + /** + * Property separator. + */ + public static final String PROPERTY_SOURCE_NAME_SEPARATOR = "."; private Constants() { } diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/KubernetesBootstrapConfiguration.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/KubernetesBootstrapConfiguration.java new file mode 100644 index 00000000..71bb65c0 --- /dev/null +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/KubernetesBootstrapConfiguration.java @@ -0,0 +1,31 @@ +/* + * 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.commons.config; + +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.kubernetes.commons.ConditionalOnKubernetesEnabled; +import org.springframework.context.annotation.Configuration; + +/** + * @author Ryan Baxter + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnKubernetesEnabled +@EnableConfigurationProperties({ ConfigMapConfigProperties.class, SecretsConfigProperties.class }) +public class KubernetesBootstrapConfiguration { + +} diff --git a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/PropertySourceUtils.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/PropertySourceUtils.java similarity index 77% rename from spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/PropertySourceUtils.java rename to spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/PropertySourceUtils.java index ad9641fd..5c757813 100644 --- a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/PropertySourceUtils.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/PropertySourceUtils.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.config; +package org.springframework.cloud.kubernetes.commons.config; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -42,7 +42,10 @@ import static org.springframework.beans.factory.config.YamlProcessor.MatchStatus */ public final class PropertySourceUtils { - static final Function KEY_VALUE_TO_PROPERTIES = s -> { + /** + * Function to convert a String to Properties. + */ + public static final Function KEY_VALUE_TO_PROPERTIES = s -> { Properties properties = new Properties(); try { properties.load(new ByteArrayInputStream(s.getBytes())); @@ -52,7 +55,11 @@ public final class PropertySourceUtils { throw new IllegalArgumentException(); } }; - static final Function> PROPERTIES_TO_MAP = p -> p.entrySet().stream() + + /** + * Function to convert Properties to a Map. + */ + public static final Function> PROPERTIES_TO_MAP = p -> p.entrySet().stream() .collect(Collectors.toMap(e -> String.valueOf(e.getKey()), Map.Entry::getValue, throwingMerger(), java.util.LinkedHashMap::new)); @@ -60,7 +67,12 @@ public final class PropertySourceUtils { throw new IllegalStateException("Can't instantiate a utility class"); } - static Function yamlParserGenerator(Environment environment) { + /** + * Function to convert String into Properties with an environment. + * @param environment Environment. + * @return properties. + */ + public static Function yamlParserGenerator(Environment environment) { return s -> { YamlPropertiesFactoryBean yamlFactory = new YamlPropertiesFactoryBean(); yamlFactory.setDocumentMatchers(properties -> { @@ -77,7 +89,12 @@ public final class PropertySourceUtils { }; } - static BinaryOperator throwingMerger() { + /** + * Throws IllegalStateException. + * @param Throwable. + * @return IllegalStateException. + */ + public static BinaryOperator throwingMerger() { return (u, v) -> { throw new IllegalStateException(String.format("Duplicate key %s", u)); }; diff --git a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/SecretsConfigProperties.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsConfigProperties.java similarity index 97% rename from spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/SecretsConfigProperties.java rename to spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsConfigProperties.java index 2a1c23c3..79cad9dd 100644 --- a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/SecretsConfigProperties.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsConfigProperties.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.config; +package org.springframework.cloud.kubernetes.commons.config; import java.util.ArrayList; import java.util.HashMap; @@ -169,7 +169,7 @@ public class SecretsConfigProperties extends AbstractConfigProperties { } - static class NormalizedSource { + public static class NormalizedSource { private final String name; diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsPropertySource.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsPropertySource.java new file mode 100644 index 00000000..88cfe3e6 --- /dev/null +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsPropertySource.java @@ -0,0 +1,54 @@ +/* + * 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.commons.config; + +import java.util.Base64; +import java.util.Map; + +import org.springframework.core.env.MapPropertySource; + +/** + * Kubernetes property source for secrets. + * + * @author l burgazzoli + * @author Haytham Mohamed + */ +public class SecretsPropertySource extends MapPropertySource { + + private static final String PREFIX = "secrets"; + + public SecretsPropertySource(String name, Map source) { + super(name, source); + } + + protected static String getSourceName(String name, String namespace) { + return new StringBuilder().append(PREFIX).append(Constants.PROPERTY_SOURCE_NAME_SEPARATOR).append(name) + .append(Constants.PROPERTY_SOURCE_NAME_SEPARATOR).append(namespace).toString(); + } + + protected static void putAll(Map data, Map result) { + if (data != null) { + data.forEach((k, v) -> result.put(k, new String(Base64.getDecoder().decode(v)).trim())); + } + } + + @Override + public String toString() { + return getClass().getSimpleName() + " {name='" + this.name + "'}"; + } + +} diff --git a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/SecretsPropertySourceLocator.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsPropertySourceLocator.java similarity index 73% rename from spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/SecretsPropertySourceLocator.java rename to spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsPropertySourceLocator.java index 862712f4..4b09389e 100644 --- a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/SecretsPropertySourceLocator.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsPropertySourceLocator.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.config; +package org.springframework.cloud.kubernetes.commons.config; import java.io.IOException; import java.nio.file.Files; @@ -24,38 +24,29 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import io.fabric8.kubernetes.client.KubernetesClient; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.cloud.bootstrap.config.PropertySourceLocator; -import org.springframework.core.annotation.Order; import org.springframework.core.env.CompositePropertySource; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.Environment; import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.PropertySource; -import static org.springframework.cloud.kubernetes.config.ConfigUtils.getApplicationName; -import static org.springframework.cloud.kubernetes.config.ConfigUtils.getApplicationNamespace; - /** * Kubernetes {@link PropertySourceLocator} for secrets. * * @author l burgazzoli * @author Haytham Mohamed */ -@Order(1) -public class SecretsPropertySourceLocator implements PropertySourceLocator { +public abstract class SecretsPropertySourceLocator implements PropertySourceLocator { private static final Log LOG = LogFactory.getLog(SecretsPropertySourceLocator.class); - private final KubernetesClient client; + protected final SecretsConfigProperties properties; - private final SecretsConfigProperties properties; - - public SecretsPropertySourceLocator(KubernetesClient client, SecretsConfigProperties properties) { - this.client = client; + public SecretsPropertySourceLocator(SecretsConfigProperties properties) { this.properties = properties; } @@ -83,17 +74,17 @@ public class SecretsPropertySourceLocator implements PropertySourceLocator { SecretsConfigProperties.NormalizedSource normalizedSource) { String configurationTarget = this.properties.getConfigurationTarget(); - return new SecretsPropertySource(this.client, environment, - getApplicationName(environment, normalizedSource.getName(), configurationTarget), - getApplicationNamespace(this.client, normalizedSource.getNamespace(), configurationTarget), - normalizedSource.getLabels()); + return getPropertySource(environment, normalizedSource, configurationTarget); } - private void putPathConfig(CompositePropertySource composite) { + protected abstract MapPropertySource getPropertySource(ConfigurableEnvironment environment, + SecretsConfigProperties.NormalizedSource normalizedSource, String configurationTarget); + + protected void putPathConfig(CompositePropertySource composite) { this.properties.getPaths().stream().map(Paths::get).filter(Files::exists).forEach(p -> putAll(p, composite)); } - private void putAll(Path path, CompositePropertySource composite) { + protected void putAll(Path path, CompositePropertySource composite) { try { Files.walk(path).filter(Files::isRegularFile).forEach(p -> readFile(p, composite)); @@ -103,7 +94,7 @@ public class SecretsPropertySourceLocator implements PropertySourceLocator { } } - private void readFile(Path path, CompositePropertySource composite) { + protected void readFile(Path path, CompositePropertySource composite) { try { Map result = new HashMap<>(); result.put(path.getFileName().toString(), new String(Files.readAllBytes(path)).trim()); diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/reload/ConfigReloadAutoConfiguration.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/reload/ConfigReloadAutoConfiguration.java new file mode 100644 index 00000000..b508bde7 --- /dev/null +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/reload/ConfigReloadAutoConfiguration.java @@ -0,0 +1,96 @@ +/* + * 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.commons.config.reload; + +import java.util.concurrent.ThreadLocalRandom; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration; +import org.springframework.boot.actuate.autoconfigure.info.InfoEndpointAutoConfiguration; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.autoconfigure.RefreshAutoConfiguration; +import org.springframework.cloud.autoconfigure.RefreshEndpointAutoConfiguration; +import org.springframework.cloud.context.refresh.ContextRefresher; +import org.springframework.cloud.context.restart.RestartEndpoint; +import org.springframework.cloud.kubernetes.commons.ConditionalOnKubernetesEnabled; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.util.Assert; + +/** + * @author Ryan Baxter + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnKubernetesEnabled +@ConditionalOnClass(EndpointAutoConfiguration.class) +@AutoConfigureAfter({ InfoEndpointAutoConfiguration.class, RefreshEndpointAutoConfiguration.class, + RefreshAutoConfiguration.class }) +public class ConfigReloadAutoConfiguration { + + /** + * Configuration reload must be enabled explicitly. + */ + @ConditionalOnProperty("spring.cloud.kubernetes.reload.enabled") + @ConditionalOnClass({ RestartEndpoint.class, ContextRefresher.class }) + protected static class ConfigReloadAutoConfigurationBeans { + + /** + * @param properties config reload properties + * @param ctx application context + * @param restarter restart endpoint + * @param refresher context refresher + * @return provides the action to execute when the configuration changes. + */ + @Bean + @ConditionalOnMissingBean + public ConfigurationUpdateStrategy configurationUpdateStrategy(ConfigReloadProperties properties, + ConfigurableApplicationContext ctx, @Autowired(required = false) RestartEndpoint restarter, + ContextRefresher refresher) { + switch (properties.getStrategy()) { + case RESTART_CONTEXT: + Assert.notNull(restarter, "Restart endpoint is not enabled"); + return new ConfigurationUpdateStrategy(properties.getStrategy().name(), () -> { + wait(properties); + restarter.restart(); + }); + case REFRESH: + return new ConfigurationUpdateStrategy(properties.getStrategy().name(), refresher::refresh); + case SHUTDOWN: + return new ConfigurationUpdateStrategy(properties.getStrategy().name(), () -> { + wait(properties); + ctx.close(); + }); + } + throw new IllegalStateException("Unsupported configuration update strategy: " + properties.getStrategy()); + } + + private static void wait(ConfigReloadProperties properties) { + final long waitMillis = ThreadLocalRandom.current().nextLong(properties.getMaxWaitForRestart().toMillis()); + try { + Thread.sleep(waitMillis); + } + catch (InterruptedException ignored) { + } + } + + } + +} diff --git a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/reload/ConfigReloadProperties.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/reload/ConfigReloadProperties.java similarity index 98% rename from spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/reload/ConfigReloadProperties.java rename to spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/reload/ConfigReloadProperties.java index b89c4ad5..1d5c8076 100644 --- a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/reload/ConfigReloadProperties.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/reload/ConfigReloadProperties.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.config.reload; +package org.springframework.cloud.kubernetes.commons.config.reload; import java.time.Duration; diff --git a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/reload/ConfigurationChangeDetector.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/reload/ConfigurationChangeDetector.java similarity index 88% rename from spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/reload/ConfigurationChangeDetector.java rename to spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/reload/ConfigurationChangeDetector.java index 4df0a4ea..182e86d1 100644 --- a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/reload/ConfigurationChangeDetector.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/reload/ConfigurationChangeDetector.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.config.reload; +package org.springframework.cloud.kubernetes.commons.config.reload; import java.util.ArrayList; import java.util.LinkedList; @@ -23,9 +23,6 @@ import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; -import javax.annotation.PreDestroy; - -import io.fabric8.kubernetes.client.KubernetesClient; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -51,25 +48,15 @@ public abstract class ConfigurationChangeDetector { protected ConfigReloadProperties properties; - protected KubernetesClient kubernetesClient; - protected ConfigurationUpdateStrategy strategy; public ConfigurationChangeDetector(ConfigurableEnvironment environment, ConfigReloadProperties properties, - KubernetesClient kubernetesClient, ConfigurationUpdateStrategy strategy) { + ConfigurationUpdateStrategy strategy) { this.environment = environment; this.properties = properties; - this.kubernetesClient = kubernetesClient; this.strategy = strategy; } - @PreDestroy - public void shutdown() { - // Ensure the kubernetes client is cleaned up from spare threads when shutting - // down - this.kubernetesClient.close(); - } - public void reloadProperties() { this.log.info("Reloading using strategy: " + this.strategy.getName()); this.strategy.reload(); @@ -81,7 +68,7 @@ public abstract class ConfigurationChangeDetector { * @param right right map property sources * @return {@code true} if source has changed */ - protected boolean changed(MapPropertySource left, MapPropertySource right) { + public boolean changed(MapPropertySource left, MapPropertySource right) { if (left == right) { return false; } @@ -93,7 +80,7 @@ public abstract class ConfigurationChangeDetector { return !Objects.equals(leftMap, rightMap); } - protected boolean changed(List left, List right) { + public boolean changed(List left, List right) { if (left.size() != right.size()) { this.log.warn("The current number of ConfigMap PropertySources does not match " @@ -132,7 +119,7 @@ public abstract class ConfigurationChangeDetector { * @param sourceClass class for which property sources will be found * @return finds all registered property sources of the given type */ - protected > List findPropertySources(Class sourceClass) { + public > List findPropertySources(Class sourceClass) { List managedSources = new LinkedList<>(); LinkedList> sources = toLinkedList(this.environment.getPropertySources()); diff --git a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/reload/ConfigurationUpdateStrategy.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/reload/ConfigurationUpdateStrategy.java similarity index 95% rename from spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/reload/ConfigurationUpdateStrategy.java rename to spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/reload/ConfigurationUpdateStrategy.java index 1b6b7506..3a5df326 100644 --- a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/reload/ConfigurationUpdateStrategy.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/reload/ConfigurationUpdateStrategy.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.config.reload; +package org.springframework.cloud.kubernetes.commons.config.reload; import java.util.Objects; diff --git a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/reload/PollingConfigMapChangeDetector.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/reload/PollingConfigMapChangeDetector.java similarity index 72% rename from spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/reload/PollingConfigMapChangeDetector.java rename to spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/reload/PollingConfigMapChangeDetector.java index 28b859aa..e8b9701d 100644 --- a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/reload/PollingConfigMapChangeDetector.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/reload/PollingConfigMapChangeDetector.java @@ -14,18 +14,16 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.config.reload; +package org.springframework.cloud.kubernetes.commons.config.reload; import java.util.List; import javax.annotation.PostConstruct; -import io.fabric8.kubernetes.client.KubernetesClient; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.cloud.kubernetes.config.ConfigMapPropertySource; -import org.springframework.cloud.kubernetes.config.ConfigMapPropertySourceLocator; +import org.springframework.cloud.bootstrap.config.PropertySourceLocator; import org.springframework.core.env.AbstractEnvironment; import org.springframework.core.env.MapPropertySource; import org.springframework.scheduling.annotation.Scheduled; @@ -42,14 +40,16 @@ public class PollingConfigMapChangeDetector extends ConfigurationChangeDetector protected Log log = LogFactory.getLog(getClass()); - private ConfigMapPropertySourceLocator configMapPropertySourceLocator; + private PropertySourceLocator propertySourceLocator; + + private Class propertySourceClass; public PollingConfigMapChangeDetector(AbstractEnvironment environment, ConfigReloadProperties properties, - KubernetesClient kubernetesClient, ConfigurationUpdateStrategy strategy, - ConfigMapPropertySourceLocator configMapPropertySourceLocator) { - super(environment, properties, kubernetesClient, strategy); - - this.configMapPropertySourceLocator = configMapPropertySourceLocator; + ConfigurationUpdateStrategy strategy, Class propertySourceClass, + PropertySourceLocator propertySourceLocator) { + super(environment, properties, strategy); + this.propertySourceLocator = propertySourceLocator; + this.propertySourceClass = propertySourceClass; } @PostConstruct @@ -66,12 +66,10 @@ public class PollingConfigMapChangeDetector extends ConfigurationChangeDetector if (log.isDebugEnabled()) { log.debug("Polling for changes in config maps"); } - List currentConfigMapSources = findPropertySources( - ConfigMapPropertySource.class); + List currentConfigMapSources = findPropertySources(propertySourceClass); if (!currentConfigMapSources.isEmpty()) { - changedConfigMap = changed( - locateMapPropertySources(this.configMapPropertySourceLocator, this.environment), + changedConfigMap = changed(locateMapPropertySources(this.propertySourceLocator, this.environment), currentConfigMapSources); } } diff --git a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/reload/PollingSecretsChangeDetector.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/reload/PollingSecretsChangeDetector.java similarity index 74% rename from spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/reload/PollingSecretsChangeDetector.java rename to spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/reload/PollingSecretsChangeDetector.java index d1e1ee97..4d99c082 100644 --- a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/reload/PollingSecretsChangeDetector.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/reload/PollingSecretsChangeDetector.java @@ -14,18 +14,16 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.config.reload; +package org.springframework.cloud.kubernetes.commons.config.reload; import java.util.List; import javax.annotation.PostConstruct; -import io.fabric8.kubernetes.client.KubernetesClient; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.cloud.kubernetes.config.SecretsPropertySource; -import org.springframework.cloud.kubernetes.config.SecretsPropertySourceLocator; +import org.springframework.cloud.bootstrap.config.PropertySourceLocator; import org.springframework.core.env.AbstractEnvironment; import org.springframework.core.env.MapPropertySource; import org.springframework.scheduling.annotation.Scheduled; @@ -42,14 +40,16 @@ public class PollingSecretsChangeDetector extends ConfigurationChangeDetector { protected Log log = LogFactory.getLog(getClass()); - private final SecretsPropertySourceLocator secretsPropertySourceLocator; + private final PropertySourceLocator propertySourceLocator; + + private Class propertySourceClass; public PollingSecretsChangeDetector(AbstractEnvironment environment, ConfigReloadProperties properties, - KubernetesClient kubernetesClient, ConfigurationUpdateStrategy strategy, - SecretsPropertySourceLocator secretsPropertySourceLocator) { - super(environment, properties, kubernetesClient, strategy); - - this.secretsPropertySourceLocator = secretsPropertySourceLocator; + ConfigurationUpdateStrategy strategy, Class propertySourceClass, + PropertySourceLocator propertySourceLocator) { + super(environment, properties, strategy); + this.propertySourceClass = propertySourceClass; + this.propertySourceLocator = propertySourceLocator; } @PostConstruct @@ -66,10 +66,10 @@ public class PollingSecretsChangeDetector extends ConfigurationChangeDetector { if (log.isDebugEnabled()) { log.debug("Polling for changes in secrets"); } - List currentSecretSources = locateMapPropertySources(this.secretsPropertySourceLocator, + List currentSecretSources = locateMapPropertySources(this.propertySourceLocator, this.environment); if (currentSecretSources != null && !currentSecretSources.isEmpty()) { - List propertySources = findPropertySources(SecretsPropertySource.class); + List propertySources = findPropertySources(this.propertySourceClass); changedSecrets = changed(currentSecretSources, propertySources); } } diff --git a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/reload/condition/EventReloadDetectionMode.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/reload/condition/EventReloadDetectionMode.java similarity index 84% rename from spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/reload/condition/EventReloadDetectionMode.java rename to spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/reload/condition/EventReloadDetectionMode.java index 04702a87..972c4e9e 100644 --- a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/reload/condition/EventReloadDetectionMode.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/reload/condition/EventReloadDetectionMode.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.config.reload.condition; +package org.springframework.cloud.kubernetes.commons.config.reload.condition; -import org.springframework.cloud.kubernetes.config.reload.ConfigReloadProperties.ReloadDetectionMode; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.env.Environment; @@ -38,7 +38,7 @@ public class EventReloadDetectionMode implements Condition { } else { if (environment.getProperty("spring.cloud.kubernetes.reload.mode") - .equalsIgnoreCase(ReloadDetectionMode.EVENT.name())) { + .equalsIgnoreCase(ConfigReloadProperties.ReloadDetectionMode.EVENT.name())) { return true; } } diff --git a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/reload/condition/PollingReloadDetectionMode.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/reload/condition/PollingReloadDetectionMode.java similarity index 84% rename from spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/reload/condition/PollingReloadDetectionMode.java rename to spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/reload/condition/PollingReloadDetectionMode.java index 39e20910..f7696d2f 100644 --- a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/reload/condition/PollingReloadDetectionMode.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/reload/condition/PollingReloadDetectionMode.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.config.reload.condition; +package org.springframework.cloud.kubernetes.commons.config.reload.condition; -import org.springframework.cloud.kubernetes.config.reload.ConfigReloadProperties.ReloadDetectionMode; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.env.Environment; @@ -38,7 +38,7 @@ public class PollingReloadDetectionMode implements Condition { } else { if (environment.getProperty("spring.cloud.kubernetes.reload.mode") - .equalsIgnoreCase(ReloadDetectionMode.POLLING.name())) { + .equalsIgnoreCase(ConfigReloadProperties.ReloadDetectionMode.POLLING.name())) { return true; } } diff --git a/spring-cloud-kubernetes-commons/src/main/resources/META-INF/spring.factories b/spring-cloud-kubernetes-commons/src/main/resources/META-INF/spring.factories index 85a298fb..ddd9639e 100644 --- a/spring-cloud-kubernetes-commons/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-kubernetes-commons/src/main/resources/META-INF/spring.factories @@ -1,2 +1,5 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ -org.springframework.cloud.kubernetes.commons.KubernetesCommonsAutoConfiguration +org.springframework.cloud.kubernetes.commons.KubernetesCommonsAutoConfiguration,\ +org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadAutoConfiguration +org.springframework.cloud.bootstrap.BootstrapConfiguration=\ +org.springframework.cloud.kubernetes.commons.config.KubernetesBootstrapConfiguration diff --git a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/ConfigMapPropertySource.java b/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/ConfigMapPropertySource.java deleted file mode 100644 index 41cac15f..00000000 --- a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/ConfigMapPropertySource.java +++ /dev/null @@ -1,180 +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.config; - -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.stream.Collectors; - -import io.fabric8.kubernetes.api.model.ConfigMap; -import io.fabric8.kubernetes.client.KubernetesClient; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.core.env.Environment; -import org.springframework.core.env.MapPropertySource; -import org.springframework.core.env.StandardEnvironment; -import org.springframework.util.StringUtils; - -import static org.springframework.cloud.kubernetes.config.PropertySourceUtils.KEY_VALUE_TO_PROPERTIES; -import static org.springframework.cloud.kubernetes.config.PropertySourceUtils.PROPERTIES_TO_MAP; -import static org.springframework.cloud.kubernetes.config.PropertySourceUtils.throwingMerger; -import static org.springframework.cloud.kubernetes.config.PropertySourceUtils.yamlParserGenerator; - -/** - * A {@link MapPropertySource} that uses Kubernetes config maps. - * - * @author Ioannis Canellos - * @author Ali Shahbour - * @author Michael Moudatsos - */ -public class ConfigMapPropertySource extends MapPropertySource { - - private static final Log LOG = LogFactory.getLog(ConfigMapPropertySource.class); - - private static final String APPLICATION_YML = "application.yml"; - - private static final String APPLICATION_YAML = "application.yaml"; - - private static final String APPLICATION_PROPERTIES = "application.properties"; - - private static final String PREFIX = "configmap"; - - public ConfigMapPropertySource(KubernetesClient client, String name) { - this(client, name, null, (Environment) null); - } - - public ConfigMapPropertySource(KubernetesClient client, String name, String namespace, String[] profiles) { - this(client, name, namespace, createEnvironmentWithActiveProfiles(profiles)); - } - - private static Environment createEnvironmentWithActiveProfiles(String[] activeProfiles) { - StandardEnvironment environment = new StandardEnvironment(); - environment.setActiveProfiles(activeProfiles); - return environment; - } - - public ConfigMapPropertySource(KubernetesClient client, String name, String namespace, Environment environment) { - super(getName(client, name, namespace), asObjectMap(getData(client, name, namespace, environment))); - } - - private static String getName(KubernetesClient client, String name, String namespace) { - return new StringBuilder().append(PREFIX).append(Constants.PROPERTY_SOURCE_NAME_SEPARATOR).append(name) - .append(Constants.PROPERTY_SOURCE_NAME_SEPARATOR) - .append(namespace == null || namespace.isEmpty() ? client.getNamespace() : namespace).toString(); - } - - private static Map getData(KubernetesClient client, String name, String namespace, - Environment environment) { - try { - Map result = new LinkedHashMap<>(); - ConfigMap map = StringUtils.isEmpty(namespace) ? client.configMaps().withName(name).get() - : client.configMaps().inNamespace(namespace).withName(name).get(); - - if (map != null) { - result.putAll(processAllEntries(map.getData(), environment)); - } - - if (environment != null) { - for (String activeProfile : environment.getActiveProfiles()) { - - String mapNameWithProfile = name + "-" + activeProfile; - - ConfigMap mapWithProfile = StringUtils.isEmpty(namespace) - ? client.configMaps().withName(mapNameWithProfile).get() - : client.configMaps().inNamespace(namespace).withName(mapNameWithProfile).get(); - - if (mapWithProfile != null) { - result.putAll(processAllEntries(mapWithProfile.getData(), environment)); - } - - } - } - - return result; - - } - catch (Exception e) { - LOG.warn("Can't read configMap with name: [" + name + "] in namespace:[" + namespace + "]. Ignoring.", e); - } - - return new LinkedHashMap<>(); - } - - private static Map processAllEntries(Map input, Environment environment) { - - Set> entrySet = input.entrySet(); - if (entrySet.size() == 1) { - // we handle the case where the configmap contains a single "file" - // in this case we don't care what the name of t he file is - Entry singleEntry = entrySet.iterator().next(); - String propertyName = singleEntry.getKey(); - String propertyValue = singleEntry.getValue(); - if (propertyName.endsWith(".yml") || propertyName.endsWith(".yaml")) { - if (LOG.isDebugEnabled()) { - LOG.debug("The single property with name: [" + propertyName + "] will be treated as a yaml file"); - } - - return yamlParserGenerator(environment).andThen(PROPERTIES_TO_MAP).apply(propertyValue); - } - else if (propertyName.endsWith(".properties")) { - if (LOG.isDebugEnabled()) { - LOG.debug("The single property with name: [" + propertyName - + "] will be treated as a properties file"); - } - - return KEY_VALUE_TO_PROPERTIES.andThen(PROPERTIES_TO_MAP).apply(propertyValue); - } - else { - return defaultProcessAllEntries(input, environment); - } - } - - return defaultProcessAllEntries(input, environment); - } - - private static Map defaultProcessAllEntries(Map input, Environment environment) { - - return input.entrySet().stream().map(e -> extractProperties(e.getKey(), e.getValue(), environment)) - .filter(m -> !m.isEmpty()).flatMap(m -> m.entrySet().stream()) - .collect(Collectors.toMap(Entry::getKey, Entry::getValue, throwingMerger(), LinkedHashMap::new)); - } - - private static Map extractProperties(String resourceName, String content, Environment environment) { - - if (resourceName.equals(APPLICATION_YAML) || resourceName.equals(APPLICATION_YML)) { - return yamlParserGenerator(environment).andThen(PROPERTIES_TO_MAP).apply(content); - } - else if (resourceName.equals(APPLICATION_PROPERTIES)) { - return KEY_VALUE_TO_PROPERTIES.andThen(PROPERTIES_TO_MAP).apply(content); - } - - return new LinkedHashMap() { - { - put(resourceName, content); - } - }; - } - - private static Map asObjectMap(Map source) { - return source.entrySet().stream().collect( - Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, throwingMerger(), LinkedHashMap::new)); - } - -} diff --git a/spring-cloud-kubernetes-config/src/main/resources/META-INF/spring.factories b/spring-cloud-kubernetes-config/src/main/resources/META-INF/spring.factories deleted file mode 100644 index 90e6af77..00000000 --- a/spring-cloud-kubernetes-config/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1,4 +0,0 @@ -org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ -org.springframework.cloud.kubernetes.config.reload.ConfigReloadAutoConfiguration -org.springframework.cloud.bootstrap.BootstrapConfiguration=\ -org.springframework.cloud.kubernetes.config.BootstrapConfiguration diff --git a/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/main/java/org/springframework/cloud/kubernetes/configuration/watcher/BusEventBasedConfigMapWatcherChangeDetector.java b/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/main/java/org/springframework/cloud/kubernetes/configuration/watcher/BusEventBasedConfigMapWatcherChangeDetector.java index 956a7d9a..4bc967ef 100644 --- a/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/main/java/org/springframework/cloud/kubernetes/configuration/watcher/BusEventBasedConfigMapWatcherChangeDetector.java +++ b/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/main/java/org/springframework/cloud/kubernetes/configuration/watcher/BusEventBasedConfigMapWatcherChangeDetector.java @@ -22,9 +22,9 @@ import reactor.core.publisher.Mono; import org.springframework.cloud.bus.BusProperties; import org.springframework.cloud.bus.event.RefreshRemoteApplicationEvent; -import org.springframework.cloud.kubernetes.config.ConfigMapPropertySourceLocator; -import org.springframework.cloud.kubernetes.config.reload.ConfigReloadProperties; -import org.springframework.cloud.kubernetes.config.reload.ConfigurationUpdateStrategy; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy; +import org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigMapPropertySourceLocator; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.core.env.AbstractEnvironment; @@ -43,10 +43,10 @@ public class BusEventBasedConfigMapWatcherChangeDetector extends ConfigMapWatche public BusEventBasedConfigMapWatcherChangeDetector(AbstractEnvironment environment, ConfigReloadProperties properties, KubernetesClient kubernetesClient, ConfigurationUpdateStrategy strategy, - ConfigMapPropertySourceLocator configMapPropertySourceLocator, BusProperties busProperties, + Fabric8ConfigMapPropertySourceLocator fabric8ConfigMapPropertySourceLocator, BusProperties busProperties, ConfigurationWatcherConfigurationProperties k8SConfigurationProperties, ThreadPoolTaskExecutor threadPoolTaskExecutor) { - super(environment, properties, kubernetesClient, strategy, configMapPropertySourceLocator, + super(environment, properties, kubernetesClient, strategy, fabric8ConfigMapPropertySourceLocator, k8SConfigurationProperties, threadPoolTaskExecutor); this.busProperties = busProperties; diff --git a/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/main/java/org/springframework/cloud/kubernetes/configuration/watcher/BusEventBasedSecretsWatcherChangeDetector.java b/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/main/java/org/springframework/cloud/kubernetes/configuration/watcher/BusEventBasedSecretsWatcherChangeDetector.java index 74bce19d..44677032 100644 --- a/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/main/java/org/springframework/cloud/kubernetes/configuration/watcher/BusEventBasedSecretsWatcherChangeDetector.java +++ b/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/main/java/org/springframework/cloud/kubernetes/configuration/watcher/BusEventBasedSecretsWatcherChangeDetector.java @@ -22,9 +22,9 @@ import reactor.core.publisher.Mono; import org.springframework.cloud.bus.BusProperties; import org.springframework.cloud.bus.event.RefreshRemoteApplicationEvent; -import org.springframework.cloud.kubernetes.config.SecretsPropertySourceLocator; -import org.springframework.cloud.kubernetes.config.reload.ConfigReloadProperties; -import org.springframework.cloud.kubernetes.config.reload.ConfigurationUpdateStrategy; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy; +import org.springframework.cloud.kubernetes.fabric8.config.Fabric8SecretsPropertySourceLocator; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.core.env.AbstractEnvironment; @@ -43,10 +43,10 @@ public class BusEventBasedSecretsWatcherChangeDetector extends SecretsWatcherCha public BusEventBasedSecretsWatcherChangeDetector(AbstractEnvironment environment, ConfigReloadProperties properties, KubernetesClient kubernetesClient, ConfigurationUpdateStrategy strategy, - SecretsPropertySourceLocator secretsPropertySourceLocator, BusProperties busProperties, + Fabric8SecretsPropertySourceLocator fabric8SecretsPropertySourceLocator, BusProperties busProperties, ConfigurationWatcherConfigurationProperties k8SConfigurationProperties, ThreadPoolTaskExecutor threadPoolTaskExecutor) { - super(environment, properties, kubernetesClient, strategy, secretsPropertySourceLocator, + super(environment, properties, kubernetesClient, strategy, fabric8SecretsPropertySourceLocator, k8SConfigurationProperties, threadPoolTaskExecutor); this.busProperties = busProperties; diff --git a/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/main/java/org/springframework/cloud/kubernetes/configuration/watcher/ConfigMapWatcherChangeDetector.java b/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/main/java/org/springframework/cloud/kubernetes/configuration/watcher/ConfigMapWatcherChangeDetector.java index 8266b603..02d6ffb0 100644 --- a/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/main/java/org/springframework/cloud/kubernetes/configuration/watcher/ConfigMapWatcherChangeDetector.java +++ b/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/main/java/org/springframework/cloud/kubernetes/configuration/watcher/ConfigMapWatcherChangeDetector.java @@ -26,10 +26,10 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import reactor.core.publisher.Mono; -import org.springframework.cloud.kubernetes.config.ConfigMapPropertySourceLocator; -import org.springframework.cloud.kubernetes.config.reload.ConfigReloadProperties; -import org.springframework.cloud.kubernetes.config.reload.ConfigurationUpdateStrategy; -import org.springframework.cloud.kubernetes.config.reload.EventBasedConfigMapChangeDetector; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy; +import org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigMapPropertySourceLocator; +import org.springframework.cloud.kubernetes.fabric8.config.reload.EventBasedConfigMapChangeDetector; import org.springframework.core.env.AbstractEnvironment; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; @@ -47,10 +47,10 @@ public abstract class ConfigMapWatcherChangeDetector extends EventBasedConfigMap public ConfigMapWatcherChangeDetector(AbstractEnvironment environment, ConfigReloadProperties properties, KubernetesClient kubernetesClient, ConfigurationUpdateStrategy strategy, - ConfigMapPropertySourceLocator configMapPropertySourceLocator, + Fabric8ConfigMapPropertySourceLocator fabric8ConfigMapPropertySourceLocator, ConfigurationWatcherConfigurationProperties k8SConfigurationProperties, ThreadPoolTaskExecutor threadPoolTaskExecutor) { - super(environment, properties, kubernetesClient, strategy, configMapPropertySourceLocator); + super(environment, properties, kubernetesClient, strategy, fabric8ConfigMapPropertySourceLocator); this.executorService = Executors.newScheduledThreadPool(k8SConfigurationProperties.getThreadPoolSize(), threadPoolTaskExecutor); this.k8SConfigurationProperties = k8SConfigurationProperties; diff --git a/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/main/java/org/springframework/cloud/kubernetes/configuration/watcher/ConfigurationWatcherAutoConfiguration.java b/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/main/java/org/springframework/cloud/kubernetes/configuration/watcher/ConfigurationWatcherAutoConfiguration.java index 058c52d8..8678023b 100644 --- a/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/main/java/org/springframework/cloud/kubernetes/configuration/watcher/ConfigurationWatcherAutoConfiguration.java +++ b/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/main/java/org/springframework/cloud/kubernetes/configuration/watcher/ConfigurationWatcherAutoConfiguration.java @@ -23,11 +23,11 @@ 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.config.ConfigMapPropertySourceLocator; -import org.springframework.cloud.kubernetes.config.SecretsPropertySourceLocator; -import org.springframework.cloud.kubernetes.config.reload.ConfigReloadProperties; -import org.springframework.cloud.kubernetes.config.reload.ConfigurationUpdateStrategy; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy; import org.springframework.cloud.kubernetes.discovery.reactive.KubernetesReactiveDiscoveryClient; +import org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigMapPropertySourceLocator; +import org.springframework.cloud.kubernetes.fabric8.config.Fabric8SecretsPropertySourceLocator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @@ -53,27 +53,28 @@ public class ConfigurationWatcherAutoConfiguration { @Bean @ConditionalOnMissingBean(ConfigMapWatcherChangeDetector.class) public ConfigMapWatcherChangeDetector httpBasedConfigMapWatchChangeDetector(AbstractEnvironment environment, - KubernetesClient kubernetesClient, ConfigMapPropertySourceLocator configMapPropertySourceLocator, - SecretsPropertySourceLocator secretsPropertySourceLocator, ConfigReloadProperties properties, + KubernetesClient kubernetesClient, + Fabric8ConfigMapPropertySourceLocator fabric8ConfigMapPropertySourceLocator, + Fabric8SecretsPropertySourceLocator fabric8SecretsPropertySourceLocator, ConfigReloadProperties properties, ConfigurationUpdateStrategy strategy, ConfigurationWatcherConfigurationProperties k8SConfigurationProperties, ThreadPoolTaskExecutor threadFactory, WebClient webClient, KubernetesReactiveDiscoveryClient kubernetesReactiveDiscoveryClient) { return new HttpBasedConfigMapWatchChangeDetector(environment, properties, kubernetesClient, strategy, - configMapPropertySourceLocator, k8SConfigurationProperties, threadFactory, webClient, + fabric8ConfigMapPropertySourceLocator, k8SConfigurationProperties, threadFactory, webClient, kubernetesReactiveDiscoveryClient); } @Bean @ConditionalOnMissingBean(SecretsWatcherChangeDetector.class) public SecretsWatcherChangeDetector httpBasedSecretsWatchChangeDetector(AbstractEnvironment environment, - KubernetesClient kubernetesClient, SecretsPropertySourceLocator secretsPropertySourceLocator, + KubernetesClient kubernetesClient, Fabric8SecretsPropertySourceLocator fabric8SecretsPropertySourceLocator, ConfigReloadProperties properties, ConfigurationUpdateStrategy strategy, ConfigurationWatcherConfigurationProperties k8SConfigurationProperties, ThreadPoolTaskExecutor threadFactory, WebClient webClient, KubernetesReactiveDiscoveryClient kubernetesReactiveDiscoveryClient) { return new HttpBasedSecretsWatchChangeDetector(environment, properties, kubernetesClient, strategy, - secretsPropertySourceLocator, k8SConfigurationProperties, threadFactory, webClient, + fabric8SecretsPropertySourceLocator, k8SConfigurationProperties, threadFactory, webClient, kubernetesReactiveDiscoveryClient); } @@ -86,24 +87,24 @@ public class ConfigurationWatcherAutoConfiguration { @ConditionalOnMissingBean(ConfigMapWatcherChangeDetector.class) public ConfigMapWatcherChangeDetector busConfigMapChangeWatcher(BusProperties busProperties, AbstractEnvironment environment, KubernetesClient kubernetesClient, - ConfigMapPropertySourceLocator configMapPropertySourceLocator, ConfigReloadProperties properties, - ConfigurationUpdateStrategy strategy, + Fabric8ConfigMapPropertySourceLocator fabric8ConfigMapPropertySourceLocator, + ConfigReloadProperties properties, ConfigurationUpdateStrategy strategy, ConfigurationWatcherConfigurationProperties k8SConfigurationProperties, ThreadPoolTaskExecutor threadFactory) { return new BusEventBasedConfigMapWatcherChangeDetector(environment, properties, kubernetesClient, strategy, - configMapPropertySourceLocator, busProperties, k8SConfigurationProperties, threadFactory); + fabric8ConfigMapPropertySourceLocator, busProperties, k8SConfigurationProperties, threadFactory); } @Bean @ConditionalOnMissingBean(SecretsWatcherChangeDetector.class) public SecretsWatcherChangeDetector busSecretsChangeWatcher(BusProperties busProperties, AbstractEnvironment environment, KubernetesClient kubernetesClient, - SecretsPropertySourceLocator secretsPropertySourceLocator, ConfigReloadProperties properties, - ConfigurationUpdateStrategy strategy, + Fabric8SecretsPropertySourceLocator fabric8SecretsPropertySourceLocator, + ConfigReloadProperties properties, ConfigurationUpdateStrategy strategy, ConfigurationWatcherConfigurationProperties k8SConfigurationProperties, ThreadPoolTaskExecutor threadFactory) { return new BusEventBasedSecretsWatcherChangeDetector(environment, properties, kubernetesClient, strategy, - secretsPropertySourceLocator, busProperties, k8SConfigurationProperties, threadFactory); + fabric8SecretsPropertySourceLocator, busProperties, k8SConfigurationProperties, threadFactory); } } diff --git a/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/main/java/org/springframework/cloud/kubernetes/configuration/watcher/HttpBasedConfigMapWatchChangeDetector.java b/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/main/java/org/springframework/cloud/kubernetes/configuration/watcher/HttpBasedConfigMapWatchChangeDetector.java index 7d910db7..ea6c8eea 100644 --- a/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/main/java/org/springframework/cloud/kubernetes/configuration/watcher/HttpBasedConfigMapWatchChangeDetector.java +++ b/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/main/java/org/springframework/cloud/kubernetes/configuration/watcher/HttpBasedConfigMapWatchChangeDetector.java @@ -27,10 +27,10 @@ import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import org.springframework.cloud.client.ServiceInstance; -import org.springframework.cloud.kubernetes.config.ConfigMapPropertySourceLocator; -import org.springframework.cloud.kubernetes.config.reload.ConfigReloadProperties; -import org.springframework.cloud.kubernetes.config.reload.ConfigurationUpdateStrategy; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy; import org.springframework.cloud.kubernetes.discovery.reactive.KubernetesReactiveDiscoveryClient; +import org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigMapPropertySourceLocator; import org.springframework.core.env.AbstractEnvironment; import org.springframework.http.ResponseEntity; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; @@ -57,11 +57,11 @@ public class HttpBasedConfigMapWatchChangeDetector extends ConfigMapWatcherChang public HttpBasedConfigMapWatchChangeDetector(AbstractEnvironment environment, ConfigReloadProperties properties, KubernetesClient kubernetesClient, ConfigurationUpdateStrategy strategy, - ConfigMapPropertySourceLocator configMapPropertySourceLocator, + Fabric8ConfigMapPropertySourceLocator fabric8ConfigMapPropertySourceLocator, ConfigurationWatcherConfigurationProperties k8SConfigurationProperties, ThreadPoolTaskExecutor threadPoolTaskExecutor, WebClient webClient, KubernetesReactiveDiscoveryClient k8sReactiveDiscoveryClient) { - super(environment, properties, kubernetesClient, strategy, configMapPropertySourceLocator, + super(environment, properties, kubernetesClient, strategy, fabric8ConfigMapPropertySourceLocator, k8SConfigurationProperties, threadPoolTaskExecutor); this.webClient = webClient; this.kubernetesReactiveDiscoveryClient = k8sReactiveDiscoveryClient; diff --git a/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/main/java/org/springframework/cloud/kubernetes/configuration/watcher/HttpBasedSecretsWatchChangeDetector.java b/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/main/java/org/springframework/cloud/kubernetes/configuration/watcher/HttpBasedSecretsWatchChangeDetector.java index c9192508..29b13dcd 100644 --- a/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/main/java/org/springframework/cloud/kubernetes/configuration/watcher/HttpBasedSecretsWatchChangeDetector.java +++ b/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/main/java/org/springframework/cloud/kubernetes/configuration/watcher/HttpBasedSecretsWatchChangeDetector.java @@ -25,10 +25,10 @@ import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import org.springframework.cloud.client.ServiceInstance; -import org.springframework.cloud.kubernetes.config.SecretsPropertySourceLocator; -import org.springframework.cloud.kubernetes.config.reload.ConfigReloadProperties; -import org.springframework.cloud.kubernetes.config.reload.ConfigurationUpdateStrategy; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy; import org.springframework.cloud.kubernetes.discovery.reactive.KubernetesReactiveDiscoveryClient; +import org.springframework.cloud.kubernetes.fabric8.config.Fabric8SecretsPropertySourceLocator; import org.springframework.core.env.AbstractEnvironment; import org.springframework.http.ResponseEntity; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; @@ -53,11 +53,11 @@ public class HttpBasedSecretsWatchChangeDetector extends SecretsWatcherChangeDet public HttpBasedSecretsWatchChangeDetector(AbstractEnvironment environment, ConfigReloadProperties properties, KubernetesClient kubernetesClient, ConfigurationUpdateStrategy strategy, - SecretsPropertySourceLocator secretsPropertySourceLocator, + Fabric8SecretsPropertySourceLocator fabric8SecretsPropertySourceLocator, ConfigurationWatcherConfigurationProperties k8SConfigurationProperties, ThreadPoolTaskExecutor threadPoolTaskExecutor, WebClient webClient, KubernetesReactiveDiscoveryClient k8sReactiveDiscoveryClient) { - super(environment, properties, kubernetesClient, strategy, secretsPropertySourceLocator, + super(environment, properties, kubernetesClient, strategy, fabric8SecretsPropertySourceLocator, k8SConfigurationProperties, threadPoolTaskExecutor); this.webClient = webClient; this.kubernetesReactiveDiscoveryClient = k8sReactiveDiscoveryClient; diff --git a/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/main/java/org/springframework/cloud/kubernetes/configuration/watcher/SecretsWatcherChangeDetector.java b/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/main/java/org/springframework/cloud/kubernetes/configuration/watcher/SecretsWatcherChangeDetector.java index 01b714d0..e81adba5 100644 --- a/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/main/java/org/springframework/cloud/kubernetes/configuration/watcher/SecretsWatcherChangeDetector.java +++ b/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/main/java/org/springframework/cloud/kubernetes/configuration/watcher/SecretsWatcherChangeDetector.java @@ -26,10 +26,10 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import reactor.core.publisher.Mono; -import org.springframework.cloud.kubernetes.config.SecretsPropertySourceLocator; -import org.springframework.cloud.kubernetes.config.reload.ConfigReloadProperties; -import org.springframework.cloud.kubernetes.config.reload.ConfigurationUpdateStrategy; -import org.springframework.cloud.kubernetes.config.reload.EventBasedSecretsChangeDetector; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy; +import org.springframework.cloud.kubernetes.fabric8.config.Fabric8SecretsPropertySourceLocator; +import org.springframework.cloud.kubernetes.fabric8.config.reload.EventBasedSecretsChangeDetector; import org.springframework.core.env.AbstractEnvironment; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; @@ -47,10 +47,10 @@ public abstract class SecretsWatcherChangeDetector extends EventBasedSecretsChan public SecretsWatcherChangeDetector(AbstractEnvironment environment, ConfigReloadProperties properties, KubernetesClient kubernetesClient, ConfigurationUpdateStrategy strategy, - SecretsPropertySourceLocator secretsPropertySourceLocator, + Fabric8SecretsPropertySourceLocator fabric8SecretsPropertySourceLocator, ConfigurationWatcherConfigurationProperties k8SConfigurationProperties, ThreadPoolTaskExecutor threadPoolTaskExecutor) { - super(environment, properties, kubernetesClient, strategy, secretsPropertySourceLocator); + super(environment, properties, kubernetesClient, strategy, fabric8SecretsPropertySourceLocator); this.executorService = Executors.newScheduledThreadPool(k8SConfigurationProperties.getThreadPoolSize(), threadPoolTaskExecutor); this.k8SConfigurationProperties = k8SConfigurationProperties; diff --git a/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/test/java/org/springframework/cloud/kubernetes/configuration/watcher/BusEventBasedConfigMapWatcherChangeDetectorTests.java b/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/test/java/org/springframework/cloud/kubernetes/configuration/watcher/BusEventBasedConfigMapWatcherChangeDetectorTests.java index 6c8f62d2..ea7effb1 100644 --- a/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/test/java/org/springframework/cloud/kubernetes/configuration/watcher/BusEventBasedConfigMapWatcherChangeDetectorTests.java +++ b/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/test/java/org/springframework/cloud/kubernetes/configuration/watcher/BusEventBasedConfigMapWatcherChangeDetectorTests.java @@ -28,9 +28,9 @@ import org.mockito.junit.MockitoJUnitRunner; import org.springframework.cloud.bus.BusProperties; import org.springframework.cloud.bus.event.RefreshRemoteApplicationEvent; -import org.springframework.cloud.kubernetes.config.ConfigMapPropertySourceLocator; -import org.springframework.cloud.kubernetes.config.reload.ConfigReloadProperties; -import org.springframework.cloud.kubernetes.config.reload.ConfigurationUpdateStrategy; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy; +import org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigMapPropertySourceLocator; import org.springframework.context.ApplicationEventPublisher; import org.springframework.mock.env.MockEnvironment; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; @@ -52,7 +52,7 @@ public class BusEventBasedConfigMapWatcherChangeDetectorTests { private ConfigurationUpdateStrategy updateStrategy; @Mock - private ConfigMapPropertySourceLocator configMapPropertySourceLocator; + private Fabric8ConfigMapPropertySourceLocator fabric8ConfigMapPropertySourceLocator; @Mock private ThreadPoolTaskExecutor threadPoolTaskExecutor; @@ -73,7 +73,7 @@ public class BusEventBasedConfigMapWatcherChangeDetectorTests { configurationWatcherConfigurationProperties = new ConfigurationWatcherConfigurationProperties(); busProperties = new BusProperties(); changeDetector = new BusEventBasedConfigMapWatcherChangeDetector(mockEnvironment, configReloadProperties, - client, updateStrategy, configMapPropertySourceLocator, busProperties, + client, updateStrategy, fabric8ConfigMapPropertySourceLocator, busProperties, configurationWatcherConfigurationProperties, threadPoolTaskExecutor); changeDetector.setApplicationEventPublisher(applicationEventPublisher); } diff --git a/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/test/java/org/springframework/cloud/kubernetes/configuration/watcher/BusEventBasedSecretsWatcherChangeDetectorTests.java b/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/test/java/org/springframework/cloud/kubernetes/configuration/watcher/BusEventBasedSecretsWatcherChangeDetectorTests.java index 0266f069..109ecb30 100644 --- a/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/test/java/org/springframework/cloud/kubernetes/configuration/watcher/BusEventBasedSecretsWatcherChangeDetectorTests.java +++ b/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/test/java/org/springframework/cloud/kubernetes/configuration/watcher/BusEventBasedSecretsWatcherChangeDetectorTests.java @@ -28,9 +28,9 @@ import org.mockito.junit.MockitoJUnitRunner; import org.springframework.cloud.bus.BusProperties; import org.springframework.cloud.bus.event.RefreshRemoteApplicationEvent; -import org.springframework.cloud.kubernetes.config.SecretsPropertySourceLocator; -import org.springframework.cloud.kubernetes.config.reload.ConfigReloadProperties; -import org.springframework.cloud.kubernetes.config.reload.ConfigurationUpdateStrategy; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy; +import org.springframework.cloud.kubernetes.fabric8.config.Fabric8SecretsPropertySourceLocator; import org.springframework.context.ApplicationEventPublisher; import org.springframework.mock.env.MockEnvironment; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; @@ -52,7 +52,7 @@ public class BusEventBasedSecretsWatcherChangeDetectorTests { private ConfigurationUpdateStrategy updateStrategy; @Mock - private SecretsPropertySourceLocator secretsPropertySourceLocator; + private Fabric8SecretsPropertySourceLocator fabric8SecretsPropertySourceLocator; @Mock private ThreadPoolTaskExecutor threadPoolTaskExecutor; @@ -73,7 +73,7 @@ public class BusEventBasedSecretsWatcherChangeDetectorTests { configurationWatcherConfigurationProperties = new ConfigurationWatcherConfigurationProperties(); busProperties = new BusProperties(); changeDetector = new BusEventBasedSecretsWatcherChangeDetector(mockEnvironment, configReloadProperties, client, - updateStrategy, secretsPropertySourceLocator, busProperties, + updateStrategy, fabric8SecretsPropertySourceLocator, busProperties, configurationWatcherConfigurationProperties, threadPoolTaskExecutor); changeDetector.setApplicationEventPublisher(applicationEventPublisher); } diff --git a/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/test/java/org/springframework/cloud/kubernetes/configuration/watcher/HttpBasedConfigMapWatchChangeDetectorTests.java b/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/test/java/org/springframework/cloud/kubernetes/configuration/watcher/HttpBasedConfigMapWatchChangeDetectorTests.java index e77fc279..ad37e3bf 100644 --- a/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/test/java/org/springframework/cloud/kubernetes/configuration/watcher/HttpBasedConfigMapWatchChangeDetectorTests.java +++ b/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/test/java/org/springframework/cloud/kubernetes/configuration/watcher/HttpBasedConfigMapWatchChangeDetectorTests.java @@ -38,11 +38,11 @@ import reactor.core.publisher.Flux; import reactor.test.StepVerifier; import org.springframework.cloud.client.ServiceInstance; -import org.springframework.cloud.kubernetes.config.ConfigMapPropertySourceLocator; -import org.springframework.cloud.kubernetes.config.reload.ConfigReloadProperties; -import org.springframework.cloud.kubernetes.config.reload.ConfigurationUpdateStrategy; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy; import org.springframework.cloud.kubernetes.discovery.KubernetesServiceInstance; import org.springframework.cloud.kubernetes.discovery.reactive.KubernetesReactiveDiscoveryClient; +import org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigMapPropertySourceLocator; import org.springframework.mock.env.MockEnvironment; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.web.reactive.function.client.WebClient; @@ -74,7 +74,7 @@ public class HttpBasedConfigMapWatchChangeDetectorTests { private ConfigurationUpdateStrategy updateStrategy; @Mock - private ConfigMapPropertySourceLocator configMapPropertySourceLocator; + private Fabric8ConfigMapPropertySourceLocator fabric8ConfigMapPropertySourceLocator; @Mock private ThreadPoolTaskExecutor threadPoolTaskExecutor; @@ -103,7 +103,7 @@ public class HttpBasedConfigMapWatchChangeDetectorTests { configurationWatcherConfigurationProperties = new ConfigurationWatcherConfigurationProperties(); WebClient webClient = WebClient.builder().build(); changeDetector = new HttpBasedConfigMapWatchChangeDetector(mockEnvironment, configReloadProperties, client, - updateStrategy, configMapPropertySourceLocator, configurationWatcherConfigurationProperties, + updateStrategy, fabric8ConfigMapPropertySourceLocator, configurationWatcherConfigurationProperties, threadPoolTaskExecutor, webClient, reactiveDiscoveryClient); } diff --git a/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/test/java/org/springframework/cloud/kubernetes/configuration/watcher/HttpBasedSecretsWatchChangeDetectorTests.java b/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/test/java/org/springframework/cloud/kubernetes/configuration/watcher/HttpBasedSecretsWatchChangeDetectorTests.java index e9ff1588..3b177bae 100644 --- a/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/test/java/org/springframework/cloud/kubernetes/configuration/watcher/HttpBasedSecretsWatchChangeDetectorTests.java +++ b/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/test/java/org/springframework/cloud/kubernetes/configuration/watcher/HttpBasedSecretsWatchChangeDetectorTests.java @@ -38,11 +38,11 @@ import reactor.core.publisher.Flux; import reactor.test.StepVerifier; import org.springframework.cloud.client.ServiceInstance; -import org.springframework.cloud.kubernetes.config.SecretsPropertySourceLocator; -import org.springframework.cloud.kubernetes.config.reload.ConfigReloadProperties; -import org.springframework.cloud.kubernetes.config.reload.ConfigurationUpdateStrategy; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy; import org.springframework.cloud.kubernetes.discovery.KubernetesServiceInstance; import org.springframework.cloud.kubernetes.discovery.reactive.KubernetesReactiveDiscoveryClient; +import org.springframework.cloud.kubernetes.fabric8.config.Fabric8SecretsPropertySourceLocator; import org.springframework.mock.env.MockEnvironment; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.web.reactive.function.client.WebClient; @@ -74,7 +74,7 @@ public class HttpBasedSecretsWatchChangeDetectorTests { private ConfigurationUpdateStrategy updateStrategy; @Mock - private SecretsPropertySourceLocator secretsPropertySourceLocator; + private Fabric8SecretsPropertySourceLocator fabric8SecretsPropertySourceLocator; @Mock private ThreadPoolTaskExecutor threadPoolTaskExecutor; @@ -103,7 +103,7 @@ public class HttpBasedSecretsWatchChangeDetectorTests { configurationWatcherConfigurationProperties = new ConfigurationWatcherConfigurationProperties(); WebClient webClient = WebClient.builder().build(); changeDetector = new HttpBasedSecretsWatchChangeDetector(mockEnvironment, configReloadProperties, client, - updateStrategy, secretsPropertySourceLocator, configurationWatcherConfigurationProperties, + updateStrategy, fabric8SecretsPropertySourceLocator, configurationWatcherConfigurationProperties, threadPoolTaskExecutor, webClient, reactiveDiscoveryClient); } diff --git a/spring-cloud-kubernetes-dependencies/pom.xml b/spring-cloud-kubernetes-dependencies/pom.xml index cfca9df6..3277b9c7 100644 --- a/spring-cloud-kubernetes-dependencies/pom.xml +++ b/spring-cloud-kubernetes-dependencies/pom.xml @@ -77,7 +77,13 @@ org.springframework.cloud - spring-cloud-kubernetes-config + spring-cloud-kubernetes-fabric8-config + ${project.version} + + + + org.springframework.cloud + spring-cloud-kubernetes-client-config ${project.version} diff --git a/spring-cloud-kubernetes-config/.jdk8 b/spring-cloud-kubernetes-fabric8-config/.jdk8 similarity index 100% rename from spring-cloud-kubernetes-config/.jdk8 rename to spring-cloud-kubernetes-fabric8-config/.jdk8 diff --git a/spring-cloud-kubernetes-config/pom.xml b/spring-cloud-kubernetes-fabric8-config/pom.xml similarity index 97% rename from spring-cloud-kubernetes-config/pom.xml rename to spring-cloud-kubernetes-fabric8-config/pom.xml index 4a537063..f243e21a 100644 --- a/spring-cloud-kubernetes-config/pom.xml +++ b/spring-cloud-kubernetes-fabric8-config/pom.xml @@ -10,8 +10,8 @@ 4.0.0 org.springframework.cloud - spring-cloud-kubernetes-config - Spring Cloud Kubernetes :: Config + spring-cloud-kubernetes-fabric8-config + Spring Cloud Kubernetes :: Fabric8 Config diff --git a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/BootstrapConfiguration.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8BootstrapConfiguration.java similarity index 70% rename from spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/BootstrapConfiguration.java rename to spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8BootstrapConfiguration.java index 3a88b26b..6848089d 100644 --- a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/BootstrapConfiguration.java +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8BootstrapConfiguration.java @@ -14,17 +14,20 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.config; +package org.springframework.cloud.kubernetes.fabric8.config; import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.Secret; import io.fabric8.kubernetes.client.KubernetesClient; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.kubernetes.commons.KubernetesCommonsAutoConfiguration; +import org.springframework.cloud.kubernetes.commons.config.ConfigMapConfigProperties; +import org.springframework.cloud.kubernetes.commons.config.KubernetesBootstrapConfiguration; +import org.springframework.cloud.kubernetes.commons.config.SecretsConfigProperties; import org.springframework.cloud.kubernetes.fabric8.Fabric8AutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -38,11 +41,11 @@ import org.springframework.context.annotation.Import; @Configuration(proxyBeanMethods = false) @ConditionalOnProperty(value = "spring.cloud.kubernetes.enabled", matchIfMissing = true) @ConditionalOnClass({ ConfigMap.class, Secret.class }) -public class BootstrapConfiguration { +@AutoConfigureAfter(KubernetesBootstrapConfiguration.class) +public class Fabric8BootstrapConfiguration { @Configuration(proxyBeanMethods = false) @Import({ KubernetesCommonsAutoConfiguration.class, Fabric8AutoConfiguration.class }) - @EnableConfigurationProperties({ ConfigMapConfigProperties.class, SecretsConfigProperties.class }) protected static class KubernetesPropertySourceConfiguration { @Autowired @@ -50,14 +53,15 @@ public class BootstrapConfiguration { @Bean @ConditionalOnProperty(name = "spring.cloud.kubernetes.config.enabled", matchIfMissing = true) - public ConfigMapPropertySourceLocator configMapPropertySourceLocator(ConfigMapConfigProperties properties) { - return new ConfigMapPropertySourceLocator(this.client, properties); + public Fabric8ConfigMapPropertySourceLocator configMapPropertySourceLocator( + ConfigMapConfigProperties properties) { + return new Fabric8ConfigMapPropertySourceLocator(this.client, properties); } @Bean @ConditionalOnProperty(name = "spring.cloud.kubernetes.secrets.enabled", matchIfMissing = true) - public SecretsPropertySourceLocator secretsPropertySourceLocator(SecretsConfigProperties properties) { - return new SecretsPropertySourceLocator(this.client, properties); + public Fabric8SecretsPropertySourceLocator secretsPropertySourceLocator(SecretsConfigProperties properties) { + return new Fabric8SecretsPropertySourceLocator(this.client, properties); } } diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapPropertySource.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapPropertySource.java new file mode 100644 index 00000000..61a8f6cf --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapPropertySource.java @@ -0,0 +1,94 @@ +/* + * 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.fabric8.config; + +import java.util.LinkedHashMap; +import java.util.Map; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.client.KubernetesClient; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.cloud.kubernetes.commons.config.ConfigMapPropertySource; +import org.springframework.core.env.Environment; +import org.springframework.core.env.MapPropertySource; +import org.springframework.util.StringUtils; + +/** + * A {@link MapPropertySource} that uses Kubernetes config maps. + * + * @author Ioannis Canellos + * @author Ali Shahbour + * @author Michael Moudatsos + */ +public class Fabric8ConfigMapPropertySource extends ConfigMapPropertySource { + + private static final Log LOG = LogFactory.getLog(Fabric8ConfigMapPropertySource.class); + + public Fabric8ConfigMapPropertySource(KubernetesClient client, String name) { + this(client, name, null, (Environment) null); + } + + public Fabric8ConfigMapPropertySource(KubernetesClient client, String name, String namespace, + Environment environment) { + super(getName(name, getNamespace(client, namespace)), + asObjectMap(getData(client, name, getNamespace(client, namespace), environment))); + } + + private static String getNamespace(KubernetesClient client, String namespace) { + return StringUtils.isEmpty(namespace) ? client.getNamespace() : namespace; + } + + private static Map getData(KubernetesClient client, String name, String namespace, + Environment environment) { + try { + Map result = new LinkedHashMap<>(); + ConfigMap map = StringUtils.isEmpty(namespace) ? client.configMaps().withName(name).get() + : client.configMaps().inNamespace(namespace).withName(name).get(); + + if (map != null) { + result.putAll(processAllEntries(map.getData(), environment)); + } + + if (environment != null) { + for (String activeProfile : environment.getActiveProfiles()) { + + String mapNameWithProfile = name + "-" + activeProfile; + + ConfigMap mapWithProfile = StringUtils.isEmpty(namespace) + ? client.configMaps().withName(mapNameWithProfile).get() + : client.configMaps().inNamespace(namespace).withName(mapNameWithProfile).get(); + + if (mapWithProfile != null) { + result.putAll(processAllEntries(mapWithProfile.getData(), environment)); + } + + } + } + + return result; + + } + catch (Exception e) { + LOG.warn("Can't read configMap with name: [" + name + "] in namespace:[" + namespace + "]. Ignoring.", e); + } + + return new LinkedHashMap<>(); + } + +} diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapPropertySourceLocator.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapPropertySourceLocator.java new file mode 100644 index 00000000..a6c6947b --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapPropertySourceLocator.java @@ -0,0 +1,55 @@ +/* + * 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.fabric8.config; + +import io.fabric8.kubernetes.client.KubernetesClient; + +import org.springframework.cloud.bootstrap.config.PropertySourceLocator; +import org.springframework.cloud.kubernetes.commons.config.ConfigMapConfigProperties; +import org.springframework.cloud.kubernetes.commons.config.ConfigMapConfigProperties.NormalizedSource; +import org.springframework.cloud.kubernetes.commons.config.ConfigMapPropertySourceLocator; +import org.springframework.core.annotation.Order; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.MapPropertySource; + +import static org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigUtils.getApplicationNamespace; + +/** + * A {@link PropertySourceLocator} that uses config maps. + * + * @author Ioannis Canellos + * @author Michael Moudatsos + */ +@Order(0) +public class Fabric8ConfigMapPropertySourceLocator extends ConfigMapPropertySourceLocator { + + private final KubernetesClient client; + + public Fabric8ConfigMapPropertySourceLocator(KubernetesClient client, ConfigMapConfigProperties properties) { + super(properties); + this.client = client; + } + + @Override + protected MapPropertySource getMapPropertySource(String name, NormalizedSource normalizedSource, + String configurationTarget, ConfigurableEnvironment environment) { + return new Fabric8ConfigMapPropertySource(this.client, name, + getApplicationNamespace(this.client, normalizedSource.getNamespace(), configurationTarget), + environment); + } + +} diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtils.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtils.java new file mode 100644 index 00000000..8e8091f7 --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtils.java @@ -0,0 +1,48 @@ +/* + * 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.fabric8.config; + +import io.fabric8.kubernetes.client.KubernetesClient; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.util.StringUtils; + +/** + * Utility class that works with configuration properties. + * + * @author Ioannis Canellos + */ +public final class Fabric8ConfigUtils { + + private static final Log LOG = LogFactory.getLog(Fabric8ConfigUtils.class); + + private Fabric8ConfigUtils() { + } + + public static String getApplicationNamespace(KubernetesClient client, String configNamespace, + String configurationTarget) { + if (StringUtils.isEmpty(configNamespace)) { + LOG.debug(configurationTarget + " namespace has not been set, taking it from client (ns=" + + client.getNamespace() + ")"); + configNamespace = client.getNamespace(); + } + + return configNamespace; + } + +} diff --git a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/SecretsPropertySource.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretsPropertySource.java similarity index 66% rename from spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/SecretsPropertySource.java rename to spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretsPropertySource.java index 7c96c1db..ba697a1c 100644 --- a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/SecretsPropertySource.java +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretsPropertySource.java @@ -14,9 +14,8 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.config; +package org.springframework.cloud.kubernetes.fabric8.config; -import java.util.Base64; import java.util.HashMap; import java.util.Map; @@ -25,8 +24,8 @@ import io.fabric8.kubernetes.client.KubernetesClient; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.cloud.kubernetes.commons.config.SecretsPropertySource; import org.springframework.core.env.Environment; -import org.springframework.core.env.MapPropertySource; import org.springframework.util.StringUtils; /** @@ -35,20 +34,15 @@ import org.springframework.util.StringUtils; * @author l burgazzoli * @author Haytham Mohamed */ -public class SecretsPropertySource extends MapPropertySource { +public class Fabric8SecretsPropertySource extends SecretsPropertySource { - private static final Log LOG = LogFactory.getLog(SecretsPropertySource.class); + private static final Log LOG = LogFactory.getLog(Fabric8SecretsPropertySource.class); private static final String PREFIX = "secrets"; - public SecretsPropertySource(KubernetesClient client, Environment env, String name, String namespace, + public Fabric8SecretsPropertySource(KubernetesClient client, Environment env, String name, String namespace, Map labels) { - super(getSourceName(client, env, name, namespace), getSourceData(client, env, name, namespace, labels)); - } - - private static String getSourceName(KubernetesClient client, Environment env, String name, String namespace) { - return new StringBuilder().append(PREFIX).append(Constants.PROPERTY_SOURCE_NAME_SEPARATOR).append(name) - .append(Constants.PROPERTY_SOURCE_NAME_SEPARATOR).append(namespace).toString(); + super(getSourceName(name, namespace), getSourceData(client, env, name, namespace, labels)); } private static Map getSourceData(KubernetesClient client, Environment env, String name, @@ -85,18 +79,10 @@ public class SecretsPropertySource extends MapPropertySource { return result; } - // ***************************** - // Helpers - // ***************************** private static void putAll(Secret secret, Map result) { - if (secret != null && secret.getData() != null) { - secret.getData().forEach((k, v) -> result.put(k, new String(Base64.getDecoder().decode(v)).trim())); + if (secret != null) { + putAll(secret.getData(), result); } } - @Override - public String toString() { - return getClass().getSimpleName() + " {name='" + this.name + "'}"; - } - } diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretsPropertySourceLocator.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretsPropertySourceLocator.java new file mode 100644 index 00000000..df725867 --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretsPropertySourceLocator.java @@ -0,0 +1,55 @@ +/* + * 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.fabric8.config; + +import io.fabric8.kubernetes.client.KubernetesClient; + +import org.springframework.cloud.bootstrap.config.PropertySourceLocator; +import org.springframework.cloud.kubernetes.commons.config.SecretsConfigProperties; +import org.springframework.cloud.kubernetes.commons.config.SecretsPropertySourceLocator; +import org.springframework.core.annotation.Order; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.MapPropertySource; + +import static org.springframework.cloud.kubernetes.commons.config.ConfigUtils.getApplicationName; + +/** + * Kubernetes {@link PropertySourceLocator} for secrets. + * + * @author l burgazzoli + * @author Haytham Mohamed + */ +@Order(1) +public class Fabric8SecretsPropertySourceLocator extends SecretsPropertySourceLocator { + + private final KubernetesClient client; + + public Fabric8SecretsPropertySourceLocator(KubernetesClient client, SecretsConfigProperties properties) { + super(properties); + this.client = client; + } + + @Override + protected MapPropertySource getPropertySource(ConfigurableEnvironment environment, + SecretsConfigProperties.NormalizedSource normalizedSource, String configurationTarget) { + return new Fabric8SecretsPropertySource(this.client, environment, + getApplicationName(environment, normalizedSource.getName(), configurationTarget), Fabric8ConfigUtils + .getApplicationNamespace(this.client, normalizedSource.getNamespace(), configurationTarget), + normalizedSource.getLabels()); + } + +} diff --git a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/reload/ConfigReloadAutoConfiguration.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/reload/ConfigReloadAutoConfiguration.java similarity index 72% rename from spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/reload/ConfigReloadAutoConfiguration.java rename to spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/reload/ConfigReloadAutoConfiguration.java index 509609d1..f8381e9c 100644 --- a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/reload/ConfigReloadAutoConfiguration.java +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/reload/ConfigReloadAutoConfiguration.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.config.reload; +package org.springframework.cloud.kubernetes.fabric8.config.reload; import java.util.concurrent.ThreadLocalRandom; @@ -33,10 +33,17 @@ import org.springframework.cloud.autoconfigure.RefreshAutoConfiguration; import org.springframework.cloud.autoconfigure.RefreshEndpointAutoConfiguration; import org.springframework.cloud.context.refresh.ContextRefresher; import org.springframework.cloud.context.restart.RestartEndpoint; -import org.springframework.cloud.kubernetes.config.ConfigMapPropertySourceLocator; -import org.springframework.cloud.kubernetes.config.SecretsPropertySourceLocator; -import org.springframework.cloud.kubernetes.config.reload.condition.EventReloadDetectionMode; -import org.springframework.cloud.kubernetes.config.reload.condition.PollingReloadDetectionMode; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationChangeDetector; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy; +import org.springframework.cloud.kubernetes.commons.config.reload.PollingConfigMapChangeDetector; +import org.springframework.cloud.kubernetes.commons.config.reload.PollingSecretsChangeDetector; +import org.springframework.cloud.kubernetes.commons.config.reload.condition.EventReloadDetectionMode; +import org.springframework.cloud.kubernetes.commons.config.reload.condition.PollingReloadDetectionMode; +import org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigMapPropertySource; +import org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigMapPropertySourceLocator; +import org.springframework.cloud.kubernetes.fabric8.config.Fabric8SecretsPropertySource; +import org.springframework.cloud.kubernetes.fabric8.config.Fabric8SecretsPropertySourceLocator; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; @@ -79,68 +86,72 @@ public class ConfigReloadAutoConfiguration { * Polling configMap ConfigurationChangeDetector. * @param properties config reload properties * @param strategy configuration update strategy - * @param configMapPropertySourceLocator configMap property source locator + * @param fabric8ConfigMapPropertySourceLocator configMap property source locator * @return a bean that listen to configuration changes and fire a reload. */ @Bean - @ConditionalOnBean(ConfigMapPropertySourceLocator.class) + @ConditionalOnBean(Fabric8ConfigMapPropertySourceLocator.class) @Conditional(PollingReloadDetectionMode.class) public ConfigurationChangeDetector configMapPropertyChangePollingWatcher(ConfigReloadProperties properties, - ConfigurationUpdateStrategy strategy, ConfigMapPropertySourceLocator configMapPropertySourceLocator) { + ConfigurationUpdateStrategy strategy, + Fabric8ConfigMapPropertySourceLocator fabric8ConfigMapPropertySourceLocator) { - return new PollingConfigMapChangeDetector(this.environment, properties, this.kubernetesClient, strategy, - configMapPropertySourceLocator); + return new PollingConfigMapChangeDetector(this.environment, properties, strategy, + Fabric8ConfigMapPropertySource.class, fabric8ConfigMapPropertySourceLocator); } /** * Polling secrets ConfigurationChangeDetector. * @param properties config reload properties * @param strategy configuration update strategy - * @param secretsPropertySourceLocator secrets property source locator + * @param fabric8SecretsPropertySourceLocator secrets property source locator * @return a bean that listen to configuration changes and fire a reload. */ @Bean - @ConditionalOnBean(SecretsPropertySourceLocator.class) + @ConditionalOnBean(Fabric8SecretsPropertySourceLocator.class) @Conditional(PollingReloadDetectionMode.class) public ConfigurationChangeDetector secretsPropertyChangePollingWatcher(ConfigReloadProperties properties, - ConfigurationUpdateStrategy strategy, SecretsPropertySourceLocator secretsPropertySourceLocator) { + ConfigurationUpdateStrategy strategy, + Fabric8SecretsPropertySourceLocator fabric8SecretsPropertySourceLocator) { - return new PollingSecretsChangeDetector(this.environment, properties, this.kubernetesClient, strategy, - secretsPropertySourceLocator); + return new PollingSecretsChangeDetector(this.environment, properties, strategy, + Fabric8SecretsPropertySource.class, fabric8SecretsPropertySourceLocator); } /** * Event Based configMap ConfigurationChangeDetector. * @param properties config reload properties * @param strategy configuration update strategy - * @param configMapPropertySourceLocator configMap property source locator + * @param fabric8ConfigMapPropertySourceLocator configMap property source locator * @return a bean that listen to configMap change events and fire a reload. */ @Bean - @ConditionalOnBean(ConfigMapPropertySourceLocator.class) + @ConditionalOnBean(Fabric8ConfigMapPropertySourceLocator.class) @Conditional(EventReloadDetectionMode.class) public ConfigurationChangeDetector configMapPropertyChangeEventWatcher(ConfigReloadProperties properties, - ConfigurationUpdateStrategy strategy, ConfigMapPropertySourceLocator configMapPropertySourceLocator) { + ConfigurationUpdateStrategy strategy, + Fabric8ConfigMapPropertySourceLocator fabric8ConfigMapPropertySourceLocator) { return new EventBasedConfigMapChangeDetector(this.environment, properties, this.kubernetesClient, strategy, - configMapPropertySourceLocator); + fabric8ConfigMapPropertySourceLocator); } /** * Event Based secrets ConfigurationChangeDetector. * @param properties config reload properties * @param strategy configuration update strategy - * @param secretsPropertySourceLocator secrets property source locator + * @param fabric8SecretsPropertySourceLocator secrets property source locator * @return a bean that listen to secrets change events and fire a reload. */ @Bean - @ConditionalOnBean(SecretsPropertySourceLocator.class) + @ConditionalOnBean(Fabric8SecretsPropertySourceLocator.class) @Conditional(EventReloadDetectionMode.class) public ConfigurationChangeDetector secretsPropertyChangeEventWatcher(ConfigReloadProperties properties, - ConfigurationUpdateStrategy strategy, SecretsPropertySourceLocator secretsPropertySourceLocator) { + ConfigurationUpdateStrategy strategy, + Fabric8SecretsPropertySourceLocator fabric8SecretsPropertySourceLocator) { return new EventBasedSecretsChangeDetector(this.environment, properties, this.kubernetesClient, strategy, - secretsPropertySourceLocator); + fabric8SecretsPropertySourceLocator); } /** diff --git a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/reload/ConfigReloadDefaultAutoConfiguration.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/reload/ConfigReloadDefaultAutoConfiguration.java similarity index 66% rename from spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/reload/ConfigReloadDefaultAutoConfiguration.java rename to spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/reload/ConfigReloadDefaultAutoConfiguration.java index 621bf03d..b6e381e5 100644 --- a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/reload/ConfigReloadDefaultAutoConfiguration.java +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/reload/ConfigReloadDefaultAutoConfiguration.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.config.reload; +package org.springframework.cloud.kubernetes.fabric8.config.reload; import java.util.concurrent.ThreadLocalRandom; @@ -25,10 +25,17 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.cloud.kubernetes.config.ConfigMapPropertySourceLocator; -import org.springframework.cloud.kubernetes.config.SecretsPropertySourceLocator; -import org.springframework.cloud.kubernetes.config.reload.condition.EventReloadDetectionMode; -import org.springframework.cloud.kubernetes.config.reload.condition.PollingReloadDetectionMode; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationChangeDetector; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy; +import org.springframework.cloud.kubernetes.commons.config.reload.PollingConfigMapChangeDetector; +import org.springframework.cloud.kubernetes.commons.config.reload.PollingSecretsChangeDetector; +import org.springframework.cloud.kubernetes.commons.config.reload.condition.EventReloadDetectionMode; +import org.springframework.cloud.kubernetes.commons.config.reload.condition.PollingReloadDetectionMode; +import org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigMapPropertySource; +import org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigMapPropertySourceLocator; +import org.springframework.cloud.kubernetes.fabric8.config.Fabric8SecretsPropertySource; +import org.springframework.cloud.kubernetes.fabric8.config.Fabric8SecretsPropertySourceLocator; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; @@ -62,77 +69,90 @@ public class ConfigReloadDefaultAutoConfiguration { private KubernetesClient kubernetesClient; @Autowired - private ConfigMapPropertySourceLocator configMapPropertySourceLocator; + private Fabric8ConfigMapPropertySourceLocator fabric8ConfigMapPropertySourceLocator; @Autowired - private SecretsPropertySourceLocator secretsPropertySourceLocator; + private Fabric8SecretsPropertySourceLocator fabric8SecretsPropertySourceLocator; + + private static void wait(ConfigReloadProperties properties) { + final long waitMillis = ThreadLocalRandom.current().nextLong(properties.getMaxWaitForRestart().toMillis()); + try { + Thread.sleep(waitMillis); + } + catch (InterruptedException ignored) { + } + } /** * Polling configMap ConfigurationChangeDetector. * @param properties config reload properties * @param strategy configuration update strategy - * @param configMapPropertySourceLocator configMap property source locator + * @param fabric8ConfigMapPropertySourceLocator configMap property source locator * @return a bean that listen to configuration changes and fire a reload. */ @Bean - @ConditionalOnBean(ConfigMapPropertySourceLocator.class) + @ConditionalOnBean(Fabric8ConfigMapPropertySourceLocator.class) @Conditional(PollingReloadDetectionMode.class) public ConfigurationChangeDetector configMapPropertyChangePollingWatcher(ConfigReloadProperties properties, - ConfigurationUpdateStrategy strategy, ConfigMapPropertySourceLocator configMapPropertySourceLocator) { + ConfigurationUpdateStrategy strategy, + Fabric8ConfigMapPropertySourceLocator fabric8ConfigMapPropertySourceLocator) { - return new PollingConfigMapChangeDetector(this.environment, properties, this.kubernetesClient, strategy, - configMapPropertySourceLocator); + return new PollingConfigMapChangeDetector(this.environment, properties, strategy, + Fabric8ConfigMapPropertySource.class, fabric8ConfigMapPropertySourceLocator); } /** * Polling secrets ConfigurationChangeDetector. * @param properties config reload properties * @param strategy configuration update strategy - * @param secretsPropertySourceLocator secrets property source locator + * @param fabric8SecretsPropertySourceLocator secrets property source locator * @return a bean that listen to configuration changes and fire a reload. */ @Bean - @ConditionalOnBean(SecretsPropertySourceLocator.class) + @ConditionalOnBean(Fabric8SecretsPropertySourceLocator.class) @Conditional(PollingReloadDetectionMode.class) public ConfigurationChangeDetector secretsPropertyChangePollingWatcher(ConfigReloadProperties properties, - ConfigurationUpdateStrategy strategy, SecretsPropertySourceLocator secretsPropertySourceLocator) { + ConfigurationUpdateStrategy strategy, + Fabric8SecretsPropertySourceLocator fabric8SecretsPropertySourceLocator) { - return new PollingSecretsChangeDetector(this.environment, properties, this.kubernetesClient, strategy, - secretsPropertySourceLocator); + return new PollingSecretsChangeDetector(this.environment, properties, strategy, + Fabric8SecretsPropertySource.class, fabric8SecretsPropertySourceLocator); } /** * Event Based configMap ConfigurationChangeDetector. * @param properties config reload properties * @param strategy configuration update strategy - * @param configMapPropertySourceLocator configMap property source locator + * @param fabric8ConfigMapPropertySourceLocator configMap property source locator * @return a bean that listen to configMap change events and fire a reload. */ @Bean - @ConditionalOnBean(ConfigMapPropertySourceLocator.class) + @ConditionalOnBean(Fabric8ConfigMapPropertySourceLocator.class) @Conditional(EventReloadDetectionMode.class) public ConfigurationChangeDetector configMapPropertyChangeEventWatcher(ConfigReloadProperties properties, - ConfigurationUpdateStrategy strategy, ConfigMapPropertySourceLocator configMapPropertySourceLocator) { + ConfigurationUpdateStrategy strategy, + Fabric8ConfigMapPropertySourceLocator fabric8ConfigMapPropertySourceLocator) { return new EventBasedConfigMapChangeDetector(this.environment, properties, this.kubernetesClient, strategy, - configMapPropertySourceLocator); + fabric8ConfigMapPropertySourceLocator); } /** * Event Based secrets ConfigurationChangeDetector. * @param properties config reload properties * @param strategy configuration update strategy - * @param secretsPropertySourceLocator secrets property source locator + * @param fabric8SecretsPropertySourceLocator secrets property source locator * @return a bean that listen to secrets change events and fire a reload. */ @Bean - @ConditionalOnBean(SecretsPropertySourceLocator.class) + @ConditionalOnBean(Fabric8SecretsPropertySourceLocator.class) @Conditional(EventReloadDetectionMode.class) public ConfigurationChangeDetector secretsPropertyChangeEventWatcher(ConfigReloadProperties properties, - ConfigurationUpdateStrategy strategy, SecretsPropertySourceLocator secretsPropertySourceLocator) { + ConfigurationUpdateStrategy strategy, + Fabric8SecretsPropertySourceLocator fabric8SecretsPropertySourceLocator) { return new EventBasedSecretsChangeDetector(this.environment, properties, this.kubernetesClient, strategy, - secretsPropertySourceLocator); + fabric8SecretsPropertySourceLocator); } /** @@ -154,15 +174,6 @@ public class ConfigReloadDefaultAutoConfiguration { throw new IllegalStateException("Unsupported configuration update strategy: " + properties.getStrategy()); } - private static void wait(ConfigReloadProperties properties) { - final long waitMillis = ThreadLocalRandom.current().nextLong(properties.getMaxWaitForRestart().toMillis()); - try { - Thread.sleep(waitMillis); - } - catch (InterruptedException ignored) { - } - } - } } diff --git a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/reload/EventBasedConfigMapChangeDetector.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/reload/EventBasedConfigMapChangeDetector.java similarity index 70% rename from spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/reload/EventBasedConfigMapChangeDetector.java rename to spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/reload/EventBasedConfigMapChangeDetector.java index c7567d80..e19d5c13 100644 --- a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/reload/EventBasedConfigMapChangeDetector.java +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/reload/EventBasedConfigMapChangeDetector.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.config.reload; +package org.springframework.cloud.kubernetes.fabric8.config.reload; import java.util.HashMap; import java.util.Map; @@ -28,8 +28,11 @@ import io.fabric8.kubernetes.client.KubernetesClientException; import io.fabric8.kubernetes.client.Watch; import io.fabric8.kubernetes.client.Watcher; -import org.springframework.cloud.kubernetes.config.ConfigMapPropertySource; -import org.springframework.cloud.kubernetes.config.ConfigMapPropertySourceLocator; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationChangeDetector; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy; +import org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigMapPropertySource; +import org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigMapPropertySourceLocator; import org.springframework.core.env.AbstractEnvironment; /** @@ -42,19 +45,28 @@ import org.springframework.core.env.AbstractEnvironment; */ public class EventBasedConfigMapChangeDetector extends ConfigurationChangeDetector { - private final ConfigMapPropertySourceLocator configMapPropertySourceLocator; + private final Fabric8ConfigMapPropertySourceLocator fabric8ConfigMapPropertySourceLocator; private final Map watches; + private KubernetesClient kubernetesClient; + public EventBasedConfigMapChangeDetector(AbstractEnvironment environment, ConfigReloadProperties properties, KubernetesClient kubernetesClient, ConfigurationUpdateStrategy strategy, - ConfigMapPropertySourceLocator configMapPropertySourceLocator) { - super(environment, properties, kubernetesClient, strategy); - - this.configMapPropertySourceLocator = configMapPropertySourceLocator; + Fabric8ConfigMapPropertySourceLocator fabric8ConfigMapPropertySourceLocator) { + super(environment, properties, strategy); + this.kubernetesClient = kubernetesClient; + this.fabric8ConfigMapPropertySourceLocator = fabric8ConfigMapPropertySourceLocator; this.watches = new HashMap<>(); } + @PreDestroy + public void shutdown() { + // Ensure the kubernetes client is cleaned up from spare threads when shutting + // down + this.kubernetesClient.close(); + } + @PostConstruct public void watch() { boolean activated = false; @@ -107,8 +119,9 @@ public class EventBasedConfigMapChangeDetector extends ConfigurationChangeDetect } protected void onEvent(ConfigMap configMap) { - boolean changed = changed(locateMapPropertySources(this.configMapPropertySourceLocator, this.environment), - findPropertySources(ConfigMapPropertySource.class)); + boolean changed = changed( + locateMapPropertySources(this.fabric8ConfigMapPropertySourceLocator, this.environment), + findPropertySources(Fabric8ConfigMapPropertySource.class)); if (changed) { this.log.info("Detected change in config maps"); reloadProperties(); diff --git a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/reload/EventBasedSecretsChangeDetector.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/reload/EventBasedSecretsChangeDetector.java similarity index 70% rename from spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/reload/EventBasedSecretsChangeDetector.java rename to spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/reload/EventBasedSecretsChangeDetector.java index f3f24cb1..4e626668 100644 --- a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/reload/EventBasedSecretsChangeDetector.java +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/reload/EventBasedSecretsChangeDetector.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.config.reload; +package org.springframework.cloud.kubernetes.fabric8.config.reload; import java.util.HashMap; import java.util.Map; @@ -28,8 +28,11 @@ import io.fabric8.kubernetes.client.KubernetesClientException; import io.fabric8.kubernetes.client.Watch; import io.fabric8.kubernetes.client.Watcher; -import org.springframework.cloud.kubernetes.config.SecretsPropertySource; -import org.springframework.cloud.kubernetes.config.SecretsPropertySourceLocator; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationChangeDetector; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy; +import org.springframework.cloud.kubernetes.fabric8.config.Fabric8SecretsPropertySource; +import org.springframework.cloud.kubernetes.fabric8.config.Fabric8SecretsPropertySourceLocator; import org.springframework.core.env.AbstractEnvironment; /** @@ -42,19 +45,28 @@ import org.springframework.core.env.AbstractEnvironment; */ public class EventBasedSecretsChangeDetector extends ConfigurationChangeDetector { - private SecretsPropertySourceLocator secretsPropertySourceLocator; + private Fabric8SecretsPropertySourceLocator fabric8SecretsPropertySourceLocator; private Map watches; + private KubernetesClient kubernetesClient; + public EventBasedSecretsChangeDetector(AbstractEnvironment environment, ConfigReloadProperties properties, KubernetesClient kubernetesClient, ConfigurationUpdateStrategy strategy, - SecretsPropertySourceLocator secretsPropertySourceLocator) { - super(environment, properties, kubernetesClient, strategy); - - this.secretsPropertySourceLocator = secretsPropertySourceLocator; + Fabric8SecretsPropertySourceLocator fabric8SecretsPropertySourceLocator) { + super(environment, properties, strategy); + this.kubernetesClient = kubernetesClient; + this.fabric8SecretsPropertySourceLocator = fabric8SecretsPropertySourceLocator; this.watches = new HashMap<>(); } + @PreDestroy + public void shutdown() { + // Ensure the kubernetes client is cleaned up from spare threads when shutting + // down + this.kubernetesClient.close(); + } + @PostConstruct public void watch() { boolean activated = false; @@ -107,8 +119,8 @@ public class EventBasedSecretsChangeDetector extends ConfigurationChangeDetector } protected void onEvent(Secret secret) { - boolean changed = changed(locateMapPropertySources(this.secretsPropertySourceLocator, this.environment), - findPropertySources(SecretsPropertySource.class)); + boolean changed = changed(locateMapPropertySources(this.fabric8SecretsPropertySourceLocator, this.environment), + findPropertySources(Fabric8SecretsPropertySource.class)); if (changed) { this.log.info("Detected change in secrets"); reloadProperties(); diff --git a/spring-cloud-kubernetes-config/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-cloud-kubernetes-fabric8-config/src/main/resources/META-INF/additional-spring-configuration-metadata.json similarity index 100% rename from spring-cloud-kubernetes-config/src/main/resources/META-INF/additional-spring-configuration-metadata.json rename to spring-cloud-kubernetes-fabric8-config/src/main/resources/META-INF/additional-spring-configuration-metadata.json diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/resources/META-INF/spring.factories b/spring-cloud-kubernetes-fabric8-config/src/main/resources/META-INF/spring.factories new file mode 100644 index 00000000..79ca4b9f --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-config/src/main/resources/META-INF/spring.factories @@ -0,0 +1,4 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +org.springframework.cloud.kubernetes.fabric8.config.reload.ConfigReloadAutoConfiguration +org.springframework.cloud.bootstrap.BootstrapConfiguration=\ +org.springframework.cloud.kubernetes.fabric8.config.Fabric8BootstrapConfiguration diff --git a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/ConfigMapTestUtil.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/ConfigMapTestUtil.java similarity index 95% rename from spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/ConfigMapTestUtil.java rename to spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/ConfigMapTestUtil.java index 5cb55d99..1955050d 100644 --- a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/ConfigMapTestUtil.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/ConfigMapTestUtil.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.config; +package org.springframework.cloud.kubernetes.fabric8.config; import java.io.IOException; import java.nio.file.Files; diff --git a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/ConfigMapsFromFilePathsTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/ConfigMapsFromFilePathsTests.java similarity index 89% rename from spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/ConfigMapsFromFilePathsTests.java rename to spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/ConfigMapsFromFilePathsTests.java index 99928dc6..a0ec697e 100644 --- a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/ConfigMapsFromFilePathsTests.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/ConfigMapsFromFilePathsTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.config; +package org.springframework.cloud.kubernetes.fabric8.config; import java.io.IOException; import java.nio.file.Files; @@ -31,12 +31,11 @@ import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.cloud.kubernetes.config.example.App; +import org.springframework.cloud.kubernetes.fabric8.config.example.App; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.reactive.server.WebTestClient; import static org.assertj.core.util.Lists.newArrayList; -import static org.springframework.cloud.kubernetes.config.ConfigMapTestUtil.createFileWithContent; @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = App.class, @@ -86,10 +85,11 @@ public class ConfigMapsFromFilePathsTests { System.setProperty(Config.KUBERNETES_NAMESPACE_SYSTEM_PROPERTY, "test"); System.setProperty(Config.KUBERNETES_HTTP2_DISABLE, "true"); Files.createDirectories(Paths.get(FILES_ROOT_PATH + "/" + FILES_SUB_PATH)); - createFileWithContent(FIRST_FILE_NAME_FULL_PATH, "bean.greeting=Hello from path!"); - createFileWithContent(SECOND_FILE_NAME_FULL_PATH, "bean.farewell=Bye from path!"); - createFileWithContent(UNUSED_FILE_NAME_FULL_PATH, "bean.morning=Morning from path!"); - createFileWithContent(FIRST_FILE_NAME_DUPLICATED_FULL_PATH, "bean.bonjour=Bonjour from path!"); + ConfigMapTestUtil.createFileWithContent(FIRST_FILE_NAME_FULL_PATH, "bean.greeting=Hello from path!"); + ConfigMapTestUtil.createFileWithContent(SECOND_FILE_NAME_FULL_PATH, "bean.farewell=Bye from path!"); + ConfigMapTestUtil.createFileWithContent(UNUSED_FILE_NAME_FULL_PATH, "bean.morning=Morning from path!"); + ConfigMapTestUtil.createFileWithContent(FIRST_FILE_NAME_DUPLICATED_FULL_PATH, + "bean.bonjour=Bonjour from path!"); } @AfterClass diff --git a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/ConfigMapsMixedTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/ConfigMapsMixedTests.java similarity index 93% rename from spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/ConfigMapsMixedTests.java rename to spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/ConfigMapsMixedTests.java index 08b42808..f5a24336 100644 --- a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/ConfigMapsMixedTests.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/ConfigMapsMixedTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.config; +package org.springframework.cloud.kubernetes.fabric8.config; import java.io.IOException; import java.nio.file.Files; @@ -33,12 +33,11 @@ import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.cloud.kubernetes.config.example.App; +import org.springframework.cloud.kubernetes.fabric8.config.example.App; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.reactive.server.WebTestClient; import static org.assertj.core.util.Lists.newArrayList; -import static org.springframework.cloud.kubernetes.config.ConfigMapTestUtil.readResourceFile; @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = App.class, @@ -76,7 +75,8 @@ public class ConfigMapsMixedTests { System.setProperty(Config.KUBERNETES_HTTP2_DISABLE, "true"); Files.createDirectories(Paths.get(FILES_ROOT_PATH)); - ConfigMapTestUtil.createFileWithContent(FILE_NAME_FULL_PATH, readResourceFile("application-path.yaml")); + ConfigMapTestUtil.createFileWithContent(FILE_NAME_FULL_PATH, + ConfigMapTestUtil.readResourceFile("application-path.yaml")); HashMap data = new HashMap<>(); data.put("bean.morning", "Buenos Dias ConfigMap, %s"); diff --git a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/ConfigMapsTest.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/ConfigMapsTest.java similarity index 87% rename from spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/ConfigMapsTest.java rename to spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/ConfigMapsTest.java index 0997152e..dcf20fbd 100644 --- a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/ConfigMapsTest.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/ConfigMapsTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.config; +package org.springframework.cloud.kubernetes.fabric8.config; import java.util.Map; @@ -27,7 +27,7 @@ import org.junit.Rule; import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.cloud.kubernetes.config.ConfigMapTestUtil.readResourceFile; +import static org.springframework.cloud.kubernetes.fabric8.config.ConfigMapTestUtil.readResourceFile; /** * @author Charles Moulliard @@ -74,8 +74,8 @@ public class ConfigMapsTest { .addToData("application.properties", readResourceFile("application.properties")).build()) .once(); - ConfigMapPropertySource cmps = new ConfigMapPropertySource(this.server.getClient().inNamespace(namespace), - configMapName); + Fabric8ConfigMapPropertySource cmps = new Fabric8ConfigMapPropertySource( + this.server.getClient().inNamespace(namespace), configMapName); assertThat(cmps.getProperty("dummy.property.string1")).isEqualTo("a"); assertThat(cmps.getProperty("dummy.property.int1")).isEqualTo("1"); @@ -91,8 +91,8 @@ public class ConfigMapsTest { .addToData("application.yaml", readResourceFile("application.yaml")).build()) .once(); - ConfigMapPropertySource cmps = new ConfigMapPropertySource(this.server.getClient().inNamespace(namespace), - configMapName); + Fabric8ConfigMapPropertySource cmps = new Fabric8ConfigMapPropertySource( + this.server.getClient().inNamespace(namespace), configMapName); assertThat(cmps.getProperty("dummy.property.string2")).isEqualTo("a"); assertThat(cmps.getProperty("dummy.property.int2")).isEqualTo(1); @@ -108,8 +108,8 @@ public class ConfigMapsTest { .addToData("adhoc.yml", readResourceFile("adhoc.yml")).build()) .once(); - ConfigMapPropertySource cmps = new ConfigMapPropertySource(this.server.getClient().inNamespace(namespace), - configMapName); + Fabric8ConfigMapPropertySource cmps = new Fabric8ConfigMapPropertySource( + this.server.getClient().inNamespace(namespace), configMapName); assertThat(cmps.getProperty("dummy.property.string3")).isEqualTo("a"); assertThat(cmps.getProperty("dummy.property.int3")).isEqualTo(1); @@ -125,7 +125,7 @@ public class ConfigMapsTest { .addToData("application.properties", "somevalue").build()) .once(); - new ConfigMapPropertySource(this.server.getClient().inNamespace(namespace), configMapName); + new Fabric8ConfigMapPropertySource(this.server.getClient().inNamespace(namespace), configMapName); // no exception is thrown for unparseable content } @@ -139,7 +139,7 @@ public class ConfigMapsTest { .addToData("application.yaml", "somevalue").build()) .once(); - new ConfigMapPropertySource(this.server.getClient().inNamespace(namespace), configMapName); + new Fabric8ConfigMapPropertySource(this.server.getClient().inNamespace(namespace), configMapName); // no exception is thrown for unparseable content } @@ -155,8 +155,8 @@ public class ConfigMapsTest { .addToData("adhoc.properties", readResourceFile("adhoc.properties")).build()) .once(); - ConfigMapPropertySource cmps = new ConfigMapPropertySource(this.server.getClient().inNamespace(namespace), - configMapName); + Fabric8ConfigMapPropertySource cmps = new Fabric8ConfigMapPropertySource( + this.server.getClient().inNamespace(namespace), configMapName); // application.properties should be read correctly assertThat(cmps.getProperty("dummy.property.string1")).isEqualTo("a"); diff --git a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/ConfigMapsTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/ConfigMapsTests.java similarity index 96% rename from spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/ConfigMapsTests.java rename to spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/ConfigMapsTests.java index dbdbb2eb..5e07dfa8 100644 --- a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/ConfigMapsTests.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/ConfigMapsTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.config; +package org.springframework.cloud.kubernetes.fabric8.config; import java.util.HashMap; @@ -31,7 +31,7 @@ import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.cloud.kubernetes.config.example.App; +import org.springframework.cloud.kubernetes.fabric8.config.example.App; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.reactive.server.WebTestClient; diff --git a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/ConfigMapsWithActiveProfilesNameTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/ConfigMapsWithActiveProfilesNameTests.java similarity index 94% rename from spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/ConfigMapsWithActiveProfilesNameTests.java rename to spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/ConfigMapsWithActiveProfilesNameTests.java index 6d57e29a..aa9771b3 100644 --- a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/ConfigMapsWithActiveProfilesNameTests.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/ConfigMapsWithActiveProfilesNameTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.config; +package org.springframework.cloud.kubernetes.fabric8.config; import java.util.HashMap; @@ -30,13 +30,13 @@ import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.cloud.kubernetes.config.example.App; +import org.springframework.cloud.kubernetes.fabric8.config.example.App; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.reactive.server.WebTestClient; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; -import static org.springframework.cloud.kubernetes.config.ConfigMapTestUtil.readResourceFile; +import static org.springframework.cloud.kubernetes.fabric8.config.ConfigMapTestUtil.readResourceFile; /** * @author Ali Shahbour diff --git a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/ConfigMapsWithProfileExpressionTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/ConfigMapsWithProfileExpressionTests.java similarity index 91% rename from spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/ConfigMapsWithProfileExpressionTests.java rename to spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/ConfigMapsWithProfileExpressionTests.java index ebe1be2e..a2fefb6c 100644 --- a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/ConfigMapsWithProfileExpressionTests.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/ConfigMapsWithProfileExpressionTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.config; +package org.springframework.cloud.kubernetes.fabric8.config; import java.util.HashMap; @@ -30,13 +30,11 @@ import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.cloud.kubernetes.config.example.App; +import org.springframework.cloud.kubernetes.fabric8.config.example.App; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.reactive.server.WebTestClient; -import static org.springframework.cloud.kubernetes.config.ConfigMapTestUtil.readResourceFile; - /** * Tests reading property from YAML document specified by profile expression. */ @@ -68,7 +66,7 @@ public class ConfigMapsWithProfileExpressionTests { System.setProperty(Config.KUBERNETES_HTTP2_DISABLE, "true"); HashMap data = new HashMap<>(); - data.put("application.yml", readResourceFile("application-with-profiles.yaml")); + data.put("application.yml", ConfigMapTestUtil.readResourceFile("application-with-profiles.yaml")); server.expect().withPath("/api/v1/namespaces/test/configmaps/" + APPLICATION_NAME) .andReturn(200, new ConfigMapBuilder().withNewMetadata().withName(APPLICATION_NAME).endMetadata() .addToData(data).build()) diff --git a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/ConfigMapsWithProfilesNoActiveProfileTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/ConfigMapsWithProfilesNoActiveProfileTests.java similarity index 93% rename from spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/ConfigMapsWithProfilesNoActiveProfileTests.java rename to spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/ConfigMapsWithProfilesNoActiveProfileTests.java index a0082bc2..ee46439a 100644 --- a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/ConfigMapsWithProfilesNoActiveProfileTests.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/ConfigMapsWithProfilesNoActiveProfileTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.config; +package org.springframework.cloud.kubernetes.fabric8.config; import java.util.HashMap; @@ -30,11 +30,11 @@ import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.cloud.kubernetes.config.example.App; +import org.springframework.cloud.kubernetes.fabric8.config.example.App; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.reactive.server.WebTestClient; -import static org.springframework.cloud.kubernetes.config.ConfigMapTestUtil.readResourceFile; +import static org.springframework.cloud.kubernetes.fabric8.config.ConfigMapTestUtil.readResourceFile; /** * @author Charles Moulliard diff --git a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/ConfigMapsWithProfilesTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/ConfigMapsWithProfilesTests.java similarity index 92% rename from spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/ConfigMapsWithProfilesTests.java rename to spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/ConfigMapsWithProfilesTests.java index fe26523f..8be8d701 100644 --- a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/ConfigMapsWithProfilesTests.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/ConfigMapsWithProfilesTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.config; +package org.springframework.cloud.kubernetes.fabric8.config; import java.util.HashMap; @@ -30,13 +30,11 @@ import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.cloud.kubernetes.config.example.App; +import org.springframework.cloud.kubernetes.fabric8.config.example.App; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.reactive.server.WebTestClient; -import static org.springframework.cloud.kubernetes.config.ConfigMapTestUtil.readResourceFile; - /** * @author Charles Moulliard */ @@ -73,7 +71,7 @@ public class ConfigMapsWithProfilesTests { System.setProperty(Config.KUBERNETES_HTTP2_DISABLE, "true"); HashMap data = new HashMap<>(); - data.put("application.yml", readResourceFile("application-with-profiles.yaml")); + data.put("application.yml", ConfigMapTestUtil.readResourceFile("application-with-profiles.yaml")); server.expect().withPath("/api/v1/namespaces/test/configmaps/" + APPLICATION_NAME) .andReturn(200, new ConfigMapBuilder().withNewMetadata().withName(APPLICATION_NAME).endMetadata() .addToData(data).build()) diff --git a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/ConfigMapsWithoutProfilesTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/ConfigMapsWithoutProfilesTests.java similarity index 91% rename from spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/ConfigMapsWithoutProfilesTests.java rename to spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/ConfigMapsWithoutProfilesTests.java index 2f0bc98c..fe384179 100644 --- a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/ConfigMapsWithoutProfilesTests.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/ConfigMapsWithoutProfilesTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.config; +package org.springframework.cloud.kubernetes.fabric8.config; import java.util.HashMap; @@ -30,13 +30,11 @@ import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.cloud.kubernetes.config.example.App; +import org.springframework.cloud.kubernetes.fabric8.config.example.App; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.reactive.server.WebTestClient; -import static org.springframework.cloud.kubernetes.config.ConfigMapTestUtil.readResourceFile; - @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = App.class, properties = { "spring.application.name=configmap-without-profile-example", "spring.cloud.kubernetes.reload.enabled=false" }) @@ -67,7 +65,7 @@ public class ConfigMapsWithoutProfilesTests { System.setProperty(Config.KUBERNETES_HTTP2_DISABLE, "true"); HashMap data = new HashMap<>(); - data.put("application.yml", readResourceFile("application-without-profiles.yaml")); + data.put("application.yml", ConfigMapTestUtil.readResourceFile("application-without-profiles.yaml")); server.expect().withPath("/api/v1/namespaces/test/configmaps/" + APPLICATION_NAME) .andReturn(200, new ConfigMapBuilder().withNewMetadata().withName(APPLICATION_NAME).endMetadata() .addToData(data).build()) diff --git a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/CoreTest.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/CoreTest.java similarity index 98% rename from spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/CoreTest.java rename to spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/CoreTest.java index e94d87ed..b0aae403 100644 --- a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/CoreTest.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/CoreTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.config; +package org.springframework.cloud.kubernetes.fabric8.config; import java.util.HashMap; diff --git a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/SecretsPropertySourceTest.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretsPropertySourceTest.java similarity index 92% rename from spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/SecretsPropertySourceTest.java rename to spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretsPropertySourceTest.java index 9e00777b..da7bf7ba 100644 --- a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/SecretsPropertySourceTest.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretsPropertySourceTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.config; +package org.springframework.cloud.kubernetes.fabric8.config; import java.util.Base64; @@ -30,7 +30,7 @@ import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.cloud.kubernetes.config.example.App; +import org.springframework.cloud.kubernetes.fabric8.config.example.App; import org.springframework.core.env.Environment; import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit4.SpringRunner; @@ -41,7 +41,7 @@ import static org.assertj.core.api.Assertions.assertThat; @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = App.class) @TestPropertySource("classpath:/application-secrets.properties") -public class SecretsPropertySourceTest { +public class Fabric8SecretsPropertySourceTest { private static final String NAMESPACE = "test"; @@ -51,7 +51,7 @@ public class SecretsPropertySourceTest { public static KubernetesServer server = new KubernetesServer(false, true); @Autowired - private SecretsPropertySourceLocator propertySourceLocator; + private Fabric8SecretsPropertySourceLocator propertySourceLocator; @Autowired private Environment environment; diff --git a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/HealthIndicatorTest.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/HealthIndicatorTest.java similarity index 95% rename from spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/HealthIndicatorTest.java rename to spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/HealthIndicatorTest.java index 6384df25..25e70e87 100644 --- a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/HealthIndicatorTest.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/HealthIndicatorTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.config; +package org.springframework.cloud.kubernetes.fabric8.config; import io.fabric8.kubernetes.client.Config; import io.fabric8.kubernetes.client.KubernetesClient; @@ -27,7 +27,7 @@ import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.cloud.kubernetes.config.example.App; +import org.springframework.cloud.kubernetes.fabric8.config.example.App; import org.springframework.http.MediaType; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.reactive.server.WebTestClient; diff --git a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/KubernetesConfigConfigurationTest.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/KubernetesConfigConfigurationTest.java similarity index 96% rename from spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/KubernetesConfigConfigurationTest.java rename to spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/KubernetesConfigConfigurationTest.java index a58f6851..b3013afb 100644 --- a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/KubernetesConfigConfigurationTest.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/KubernetesConfigConfigurationTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.config; +package org.springframework.cloud.kubernetes.fabric8.config; import io.fabric8.kubernetes.client.KubernetesClient; import org.junit.After; @@ -23,7 +23,7 @@ import org.junit.Test; import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.autoconfigure.RefreshAutoConfiguration; -import org.springframework.cloud.kubernetes.config.reload.ConfigReloadAutoConfiguration; +import org.springframework.cloud.kubernetes.fabric8.config.reload.ConfigReloadAutoConfiguration; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -124,7 +124,7 @@ public class KubernetesConfigConfigurationTest { private void setup(String... env) { this.context = new SpringApplicationBuilder(PropertyPlaceholderAutoConfiguration.class, - KubernetesClientTestConfiguration.class, BootstrapConfiguration.class, + KubernetesClientTestConfiguration.class, Fabric8BootstrapConfiguration.class, ConfigReloadAutoConfiguration.class, RefreshAutoConfiguration.class) .web(org.springframework.boot.WebApplicationType.NONE).properties(env).run(); } diff --git a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/MissingActuatorTest.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/MissingActuatorTest.java similarity index 97% rename from spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/MissingActuatorTest.java rename to spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/MissingActuatorTest.java index e0c6c606..48cbbbdf 100644 --- a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/MissingActuatorTest.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/MissingActuatorTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.config; +package org.springframework.cloud.kubernetes.fabric8.config; import org.junit.Rule; import org.junit.Test; diff --git a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/MultipleConfigMapsTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/MultipleConfigMapsTests.java similarity index 96% rename from spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/MultipleConfigMapsTests.java rename to spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/MultipleConfigMapsTests.java index 9e452242..a5afa5e0 100644 --- a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/MultipleConfigMapsTests.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/MultipleConfigMapsTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.config; +package org.springframework.cloud.kubernetes.fabric8.config; import java.util.HashMap; import java.util.Map; @@ -31,7 +31,7 @@ import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.cloud.kubernetes.config.example2.ExampleApp; +import org.springframework.cloud.kubernetes.fabric8.config.example2.ExampleApp; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.reactive.server.WebTestClient; diff --git a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/MultipleSecretsTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/MultipleSecretsTests.java similarity index 96% rename from spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/MultipleSecretsTests.java rename to spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/MultipleSecretsTests.java index 676129cc..0ac5fcc0 100644 --- a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/MultipleSecretsTests.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/MultipleSecretsTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.config; +package org.springframework.cloud.kubernetes.fabric8.config; import java.util.Base64; import java.util.HashMap; @@ -33,7 +33,7 @@ import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.cloud.kubernetes.config.example3.MultiSecretsApp; +import org.springframework.cloud.kubernetes.fabric8.config.example3.MultiSecretsApp; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.reactive.server.WebTestClient; diff --git a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/TestApplication.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/TestApplication.java similarity index 93% rename from spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/TestApplication.java rename to spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/TestApplication.java index 4b1a7675..1ad91638 100644 --- a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/TestApplication.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/TestApplication.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.config; +package org.springframework.cloud.kubernetes.fabric8.config; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; diff --git a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/example/App.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/example/App.java similarity index 93% rename from spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/example/App.java rename to spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/example/App.java index abf9e3b8..f617945b 100644 --- a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/example/App.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/example/App.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.config.example; +package org.springframework.cloud.kubernetes.fabric8.config.example; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; diff --git a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/example/GreetingController.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/example/GreetingController.java similarity index 96% rename from spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/example/GreetingController.java rename to spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/example/GreetingController.java index ca71d3a2..9aab422d 100644 --- a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/example/GreetingController.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/example/GreetingController.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.config.example; +package org.springframework.cloud.kubernetes.fabric8.config.example; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; diff --git a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/example/GreetingProperties.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/example/GreetingProperties.java similarity index 95% rename from spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/example/GreetingProperties.java rename to spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/example/GreetingProperties.java index 0c47e8dc..cb5dbe94 100644 --- a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/example/GreetingProperties.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/example/GreetingProperties.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.config.example; +package org.springframework.cloud.kubernetes.fabric8.config.example; import org.springframework.boot.context.properties.ConfigurationProperties; diff --git a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/example/ResponseMessage.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/example/ResponseMessage.java similarity index 92% rename from spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/example/ResponseMessage.java rename to spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/example/ResponseMessage.java index 60b8b196..6f3e210c 100644 --- a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/example/ResponseMessage.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/example/ResponseMessage.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.config.example; +package org.springframework.cloud.kubernetes.fabric8.config.example; /** * @author Charles Moulliard diff --git a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/example2/ExampleApp.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/example2/ExampleApp.java similarity index 91% rename from spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/example2/ExampleApp.java rename to spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/example2/ExampleApp.java index b8932fba..8ebc7338 100644 --- a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/example2/ExampleApp.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/example2/ExampleApp.java @@ -14,11 +14,12 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.config.example2; +package org.springframework.cloud.kubernetes.fabric8.config.example2; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.kubernetes.fabric8.config.example.App; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @@ -27,7 +28,7 @@ import org.springframework.web.bind.annotation.RestController; public class ExampleApp { public static void main(String[] args) { - SpringApplication.run(org.springframework.cloud.kubernetes.config.example.App.class, args); + SpringApplication.run(App.class, args); } @RestController diff --git a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/example2/ExampleAppProps.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/example2/ExampleAppProps.java similarity index 95% rename from spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/example2/ExampleAppProps.java rename to spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/example2/ExampleAppProps.java index ed949277..b5179c88 100644 --- a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/example2/ExampleAppProps.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/example2/ExampleAppProps.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.config.example2; +package org.springframework.cloud.kubernetes.fabric8.config.example2; import org.springframework.boot.context.properties.ConfigurationProperties; diff --git a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/example3/MultiSecretsApp.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/example3/MultiSecretsApp.java similarity index 96% rename from spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/example3/MultiSecretsApp.java rename to spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/example3/MultiSecretsApp.java index 52b88473..015e762e 100644 --- a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/example3/MultiSecretsApp.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/example3/MultiSecretsApp.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.config.example3; +package org.springframework.cloud.kubernetes.fabric8.config.example3; /** * @author Haytham Mohamed diff --git a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/example3/MultiSecretsProperties.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/example3/MultiSecretsProperties.java similarity index 94% rename from spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/example3/MultiSecretsProperties.java rename to spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/example3/MultiSecretsProperties.java index 72ee8750..dccf36b2 100644 --- a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/example3/MultiSecretsProperties.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/example3/MultiSecretsProperties.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.config.example3; +package org.springframework.cloud.kubernetes.fabric8.config.example3; import org.springframework.boot.context.properties.ConfigurationProperties; diff --git a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/reload/ConfigurationChangeDetectorTest.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/reload/ConfigurationChangeDetectorTest.java similarity index 89% rename from spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/reload/ConfigurationChangeDetectorTest.java rename to spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/reload/ConfigurationChangeDetectorTest.java index a68d2f4e..5f59b07b 100644 --- a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/reload/ConfigurationChangeDetectorTest.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/reload/ConfigurationChangeDetectorTest.java @@ -14,17 +14,19 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.config.reload; +package org.springframework.cloud.kubernetes.fabric8.config.reload; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import io.fabric8.kubernetes.client.KubernetesClient; import org.junit.Assert; import org.junit.Test; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationChangeDetector; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.MapPropertySource; @@ -33,7 +35,7 @@ import org.springframework.core.env.MapPropertySource; */ public class ConfigurationChangeDetectorTest { - private final ConfigurationChangeDetectorStub stub = new ConfigurationChangeDetectorStub(null, null, null, null); + private final ConfigurationChangeDetectorStub stub = new ConfigurationChangeDetectorStub(null, null, null); @Test public void testChangedTwoNulls() { @@ -122,8 +124,8 @@ public class ConfigurationChangeDetectorTest { private static final class ConfigurationChangeDetectorStub extends ConfigurationChangeDetector { private ConfigurationChangeDetectorStub(ConfigurableEnvironment environment, ConfigReloadProperties properties, - KubernetesClient kubernetesClient, ConfigurationUpdateStrategy strategy) { - super(environment, properties, kubernetesClient, strategy); + ConfigurationUpdateStrategy strategy) { + super(environment, properties, strategy); } } diff --git a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/reload/EventBasedConfigurationChangeDetectorTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/reload/EventBasedConfigurationChangeDetectorTests.java similarity index 76% rename from spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/reload/EventBasedConfigurationChangeDetectorTests.java rename to spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/reload/EventBasedConfigurationChangeDetectorTests.java index 2e96c23a..a8a3deac 100644 --- a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/reload/EventBasedConfigurationChangeDetectorTests.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/reload/EventBasedConfigurationChangeDetectorTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.config.reload; +package org.springframework.cloud.kubernetes.fabric8.config.reload; import java.util.HashMap; import java.util.List; @@ -29,8 +29,10 @@ import io.fabric8.kubernetes.client.dsl.Resource; import org.junit.Test; import org.springframework.cloud.bootstrap.config.BootstrapPropertySource; -import org.springframework.cloud.kubernetes.config.ConfigMapPropertySource; -import org.springframework.cloud.kubernetes.config.ConfigMapPropertySourceLocator; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy; +import org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigMapPropertySource; +import org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigMapPropertySourceLocator; import org.springframework.mock.env.MockEnvironment; import static org.assertj.core.api.Assertions.assertThat; @@ -59,14 +61,16 @@ public class EventBasedConfigurationChangeDetectorTests { when(mixedOperation.withName(eq("myconfigmap"))).thenReturn(resource); when(k8sClient.configMaps()).thenReturn(mixedOperation); - ConfigMapPropertySource configMapPropertySource = new ConfigMapPropertySource(k8sClient, "myconfigmap"); - env.getPropertySources().addFirst(new BootstrapPropertySource(configMapPropertySource)); + Fabric8ConfigMapPropertySource fabric8ConfigMapPropertySource = new Fabric8ConfigMapPropertySource(k8sClient, + "myconfigmap"); + env.getPropertySources().addFirst(new BootstrapPropertySource(fabric8ConfigMapPropertySource)); ConfigurationUpdateStrategy configurationUpdateStrategy = mock(ConfigurationUpdateStrategy.class); - ConfigMapPropertySourceLocator configMapLocator = mock(ConfigMapPropertySourceLocator.class); + Fabric8ConfigMapPropertySourceLocator configMapLocator = mock(Fabric8ConfigMapPropertySourceLocator.class); EventBasedConfigMapChangeDetector detector = new EventBasedConfigMapChangeDetector(env, configReloadProperties, k8sClient, configurationUpdateStrategy, configMapLocator); - List sources = detector.findPropertySources(ConfigMapPropertySource.class); + List sources = detector + .findPropertySources(Fabric8ConfigMapPropertySource.class); assertThat(sources.size()).isEqualTo(1); assertThat(sources.get(0).getProperty("foo")).isEqualTo("bar"); } diff --git a/spring-cloud-kubernetes-config/src/test/resources/adhoc.properties b/spring-cloud-kubernetes-fabric8-config/src/test/resources/adhoc.properties similarity index 100% rename from spring-cloud-kubernetes-config/src/test/resources/adhoc.properties rename to spring-cloud-kubernetes-fabric8-config/src/test/resources/adhoc.properties diff --git a/spring-cloud-kubernetes-config/src/test/resources/adhoc.yml b/spring-cloud-kubernetes-fabric8-config/src/test/resources/adhoc.yml similarity index 100% rename from spring-cloud-kubernetes-config/src/test/resources/adhoc.yml rename to spring-cloud-kubernetes-fabric8-config/src/test/resources/adhoc.yml diff --git a/spring-cloud-kubernetes-config/src/test/resources/application-path.yaml b/spring-cloud-kubernetes-fabric8-config/src/test/resources/application-path.yaml similarity index 100% rename from spring-cloud-kubernetes-config/src/test/resources/application-path.yaml rename to spring-cloud-kubernetes-fabric8-config/src/test/resources/application-path.yaml diff --git a/spring-cloud-kubernetes-config/src/test/resources/application-secrets.properties b/spring-cloud-kubernetes-fabric8-config/src/test/resources/application-secrets.properties similarity index 64% rename from spring-cloud-kubernetes-config/src/test/resources/application-secrets.properties rename to spring-cloud-kubernetes-fabric8-config/src/test/resources/application-secrets.properties index 93827ece..62735073 100644 --- a/spring-cloud-kubernetes-config/src/test/resources/application-secrets.properties +++ b/spring-cloud-kubernetes-fabric8-config/src/test/resources/application-secrets.properties @@ -1,5 +1,5 @@ spring.application.name=configmap-example spring.cloud.kubernetes.reload.enabled=false -logging.level.org.springframework.cloud.kubernetes.config.SecretsPropertySource=DEBUG +logging.level.org.springframework.cloud.kubernetes.fabric8.config.Fabric8SecretsPropertySource=DEBUG spring.cloud.kubernetes.secrets.labels.foo=bar spring.cloud.kubernetes.secrets.enableApi=true diff --git a/spring-cloud-kubernetes-config/src/test/resources/application-with-active-profiles-name.yaml b/spring-cloud-kubernetes-fabric8-config/src/test/resources/application-with-active-profiles-name.yaml similarity index 100% rename from spring-cloud-kubernetes-config/src/test/resources/application-with-active-profiles-name.yaml rename to spring-cloud-kubernetes-fabric8-config/src/test/resources/application-with-active-profiles-name.yaml diff --git a/spring-cloud-kubernetes-config/src/test/resources/application-with-profiles.yaml b/spring-cloud-kubernetes-fabric8-config/src/test/resources/application-with-profiles.yaml similarity index 100% rename from spring-cloud-kubernetes-config/src/test/resources/application-with-profiles.yaml rename to spring-cloud-kubernetes-fabric8-config/src/test/resources/application-with-profiles.yaml diff --git a/spring-cloud-kubernetes-config/src/test/resources/application-without-profiles.yaml b/spring-cloud-kubernetes-fabric8-config/src/test/resources/application-without-profiles.yaml similarity index 100% rename from spring-cloud-kubernetes-config/src/test/resources/application-without-profiles.yaml rename to spring-cloud-kubernetes-fabric8-config/src/test/resources/application-without-profiles.yaml diff --git a/spring-cloud-kubernetes-config/src/test/resources/application.properties b/spring-cloud-kubernetes-fabric8-config/src/test/resources/application.properties similarity index 100% rename from spring-cloud-kubernetes-config/src/test/resources/application.properties rename to spring-cloud-kubernetes-fabric8-config/src/test/resources/application.properties diff --git a/spring-cloud-kubernetes-config/src/test/resources/application.yaml b/spring-cloud-kubernetes-fabric8-config/src/test/resources/application.yaml similarity index 100% rename from spring-cloud-kubernetes-config/src/test/resources/application.yaml rename to spring-cloud-kubernetes-fabric8-config/src/test/resources/application.yaml diff --git a/spring-cloud-kubernetes-config/src/test/resources/logback-test.xml b/spring-cloud-kubernetes-fabric8-config/src/test/resources/logback-test.xml similarity index 100% rename from spring-cloud-kubernetes-config/src/test/resources/logback-test.xml rename to spring-cloud-kubernetes-fabric8-config/src/test/resources/logback-test.xml diff --git a/spring-cloud-kubernetes-config/src/test/resources/multiple-secrets.yml b/spring-cloud-kubernetes-fabric8-config/src/test/resources/multiple-secrets.yml similarity index 100% rename from spring-cloud-kubernetes-config/src/test/resources/multiple-secrets.yml rename to spring-cloud-kubernetes-fabric8-config/src/test/resources/multiple-secrets.yml diff --git a/spring-cloud-kubernetes-config/src/test/resources/multiplecms.yml b/spring-cloud-kubernetes-fabric8-config/src/test/resources/multiplecms.yml similarity index 100% rename from spring-cloud-kubernetes-config/src/test/resources/multiplecms.yml rename to spring-cloud-kubernetes-fabric8-config/src/test/resources/multiplecms.yml diff --git a/spring-cloud-kubernetes-integration-tests/run.sh b/spring-cloud-kubernetes-integration-tests/run.sh index b4061eb1..e2ca839b 100755 --- a/spring-cloud-kubernetes-integration-tests/run.sh +++ b/spring-cloud-kubernetes-integration-tests/run.sh @@ -145,6 +145,9 @@ main() { # TODO: invoke your tests here cd spring-cloud-kubernetes-core-k8s-client-it ../../mvnw clean install -P it + cd ../ + cd spring-cloud-kubernetes-client-config-it + ../../mvnw clean install -P it cd ../ cd spring-cloud-kubernetes-configuration-watcher-it ../../mvnw clean install -P it diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/k8s/configmap.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/k8s/configmap.yaml new file mode 100644 index 00000000..6a4e128c --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/k8s/configmap.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +data: + application.yaml: |- + my: + config: + myProperty: from-config-map +kind: ConfigMap +metadata: + name: spring-cloud-kubernetes-client-config-it diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/k8s/deployment-it.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/k8s/deployment-it.yaml new file mode 100644 index 00000000..21f1aa11 --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/k8s/deployment-it.yaml @@ -0,0 +1,29 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + creationTimestamp: null + labels: + app: spring-cloud-kubernetes-client-config-it + name: spring-cloud-kubernetes-client-config-it-deployment +spec: + replicas: 1 + selector: + matchLabels: + app: spring-cloud-kubernetes-client-config-it + strategy: {} + template: + metadata: + creationTimestamp: null + labels: + app: spring-cloud-kubernetes-client-config-it + spec: + serviceAccountName: spring-cloud-kubernetes-serviceaccount + containers: + - image: springcloud/spring-cloud-kubernetes-client-config-it:2.0.0-SNAPSHOT + imagePullPolicy: IfNotPresent + name: spring-cloud-kubernetes-client-config-it + resources: {} + env: + - name: SPRING_PROFILES_ACTIVE + value: kubernetes +status: {} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/k8s/deployment-polling-it.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/k8s/deployment-polling-it.yaml new file mode 100644 index 00000000..6cefe2ab --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/k8s/deployment-polling-it.yaml @@ -0,0 +1,31 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + creationTimestamp: null + labels: + app: spring-cloud-kubernetes-client-config-it + name: spring-cloud-kubernetes-client-config-it-deployment +spec: + replicas: 1 + selector: + matchLabels: + app: spring-cloud-kubernetes-client-config-it + strategy: {} + template: + metadata: + creationTimestamp: null + labels: + app: spring-cloud-kubernetes-client-config-it + spec: + serviceAccountName: spring-cloud-kubernetes-serviceaccount + containers: + - image: springcloud/spring-cloud-kubernetes-client-config-it:2.0.0-SNAPSHOT + imagePullPolicy: IfNotPresent + name: spring-cloud-kubernetes-client-config-it + resources: {} + env: + - name: SPRING_PROFILES_ACTIVE + value: kubernetes + - name: SPRING_CLOUD_KUBERNETES_RELOAD_MODE + value: polling +status: {} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/k8s/secret.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/k8s/secret.yaml new file mode 100644 index 00000000..9b68d973 --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/k8s/secret.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +data: + my.config.mySecret: cDQ1NXcwcmQ= +kind: Secret +metadata: + name: spring-cloud-kubernetes-client-config-it +type: Opaque diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/k8s/service-it.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/k8s/service-it.yaml new file mode 100644 index 00000000..06324a87 --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/k8s/service-it.yaml @@ -0,0 +1,18 @@ +apiVersion: v1 +kind: Service +metadata: + creationTimestamp: null + labels: + app: spring-cloud-kubernetes-client-config-it + name: spring-cloud-kubernetes-client-config-it +spec: + ports: + - name: 80-8080 + port: 80 + protocol: TCP + targetPort: 8080 + selector: + app: spring-cloud-kubernetes-client-config-it + type: ClusterIP +status: + loadBalancer: {} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/pom.xml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/pom.xml new file mode 100644 index 00000000..3d6db651 --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/pom.xml @@ -0,0 +1,90 @@ + + + + org.springframework.cloud + spring-cloud-kubernetes-integration-tests + 2.0.0-SNAPSHOT + + 4.0.0 + + spring-cloud-kubernetes-client-config-it + + + + org.springframework.cloud + spring-cloud-starter-kubernetes-client-all + + + org.springframework.cloud + spring-cloud-kubernetes-test-support + + + org.springframework.boot + spring-boot-starter-webflux + + + org.springframework.boot + spring-boot-starter-actuator + + + io.kubernetes + client-java + + + io.kubernetes + client-java-extended + + + com.github.docker-java + docker-java-core + test + + + com.github.docker-java + docker-java-transport-httpclient5 + test + + + + + + + imagename + + + !env.IMAGE + + + + springcloud/${project.artifactId}:${project.version} + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + ${env.IMAGE} + + build-image + + + + package + + build-image + + + + + + + + + diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/skaffold.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/skaffold.yaml new file mode 100644 index 00000000..27f72f02 --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/skaffold.yaml @@ -0,0 +1,33 @@ +apiVersion: skaffold/v2alpha3 +kind: Config +metadata: + name: spring-cloud-kubernetes-client-config-it +build: + artifacts: + - image: springcloud/spring-cloud-kubernetes-client-config-it + custom: + buildCommand: "../../mvnw clean install" + dependencies: + paths: + - src + - pom.xml + +profiles: + - name: polling + deploy: + kubectl: + manifests: + - k8s/deployment-polling-it.yaml + - k8s/service-it.yaml + - k8s/configmap.yaml + - k8s/secret.yaml + - ../permissions.yaml + +deploy: + kubectl: + manifests: + - k8s/deployment-it.yaml + - k8s/service-it.yaml + - k8s/configmap.yaml + - k8s/secret.yaml + - ../permissions.yaml diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/main/java/org/springframework/cloud/kubernetes/client/config/it/KubernetesConfigClientApplicationIt.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/main/java/org/springframework/cloud/kubernetes/client/config/it/KubernetesConfigClientApplicationIt.java new file mode 100644 index 00000000..792a3c3a --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/main/java/org/springframework/cloud/kubernetes/client/config/it/KubernetesConfigClientApplicationIt.java @@ -0,0 +1,57 @@ +/* + * 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.client.config.it; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author Ryan Baxter + */ +@SpringBootApplication +@RestController +public class KubernetesConfigClientApplicationIt { + + @Autowired + private MyConfigurationProperties configurationProperties; + + @GetMapping("/myProperty") + public String myProperty() { + return configurationProperties.getMyProperty(); + } + + @GetMapping("/mySecret") + public String mySecret() { + return configurationProperties.getMySecret(); + } + + public static void main(String[] args) { + SpringApplication.run(KubernetesConfigClientApplicationIt.class, args); + } + + @Configuration + @EnableConfigurationProperties(MyConfigurationProperties.class) + class MyConfig { + + } + +} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/main/java/org/springframework/cloud/kubernetes/client/config/it/MyConfigurationProperties.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/main/java/org/springframework/cloud/kubernetes/client/config/it/MyConfigurationProperties.java new file mode 100644 index 00000000..ac43f4a2 --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/main/java/org/springframework/cloud/kubernetes/client/config/it/MyConfigurationProperties.java @@ -0,0 +1,50 @@ +/* + * 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.client.config.it; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * @author Ryan Baxter + */ +@ConfigurationProperties("my.config") +public class MyConfigurationProperties { + + private String myProperty = "default-value"; + + private String mySecret = "default-secret-value"; + + public MyConfigurationProperties() { + } + + public String getMyProperty() { + return myProperty; + } + + public void setMyProperty(String myProperty) { + this.myProperty = myProperty; + } + + public String getMySecret() { + return mySecret; + } + + public void setMySecret(String mySecret) { + this.mySecret = mySecret; + } + +} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/main/resources/application.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/main/resources/application.yaml new file mode 100644 index 00000000..e81b66d5 --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/main/resources/application.yaml @@ -0,0 +1,11 @@ +management: + endpoints: + web: + exposure: + include: "*" +#logging: +# level: +# org: +# springframework: +# cloud: +# kubernetes: DEBUG diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/main/resources/bootstrap-kubernetes.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/main/resources/bootstrap-kubernetes.yaml new file mode 100644 index 00000000..723f28c9 --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/main/resources/bootstrap-kubernetes.yaml @@ -0,0 +1,15 @@ +spring: + cloud: + kubernetes: + enabled: true + secrets: + enable-api: true + reload: + enabled: true + monitoring-secrets: true +logging: + level: + org: + springframework: + cloud: + kubernetes: DEBUG diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/main/resources/bootstrap.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/main/resources/bootstrap.yaml new file mode 100644 index 00000000..d4075ec0 --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/main/resources/bootstrap.yaml @@ -0,0 +1,7 @@ +spring: + application: + name: spring-cloud-kubernetes-client-config-it + cloud: + kubernetes: + enabled: false + diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/test/java/org/springframework/cloud/kubernetes/client/config/it/ConfigMapAndSecretIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/test/java/org/springframework/cloud/kubernetes/client/config/it/ConfigMapAndSecretIT.java new file mode 100644 index 00000000..6f73abff --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/test/java/org/springframework/cloud/kubernetes/client/config/it/ConfigMapAndSecretIT.java @@ -0,0 +1,255 @@ +/* + * 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.client.config.it; + +import java.io.IOException; +import java.time.Duration; +import java.util.Map; + +import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.core.DefaultDockerClientConfig; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.core.DockerClientImpl; +import com.github.dockerjava.httpclient5.ApacheDockerHttpClient; +import com.github.dockerjava.transport.DockerHttpClient; +import io.kubernetes.client.openapi.ApiClient; +import io.kubernetes.client.openapi.Configuration; +import io.kubernetes.client.openapi.apis.AppsV1Api; +import io.kubernetes.client.openapi.apis.CoreV1Api; +import io.kubernetes.client.openapi.apis.NetworkingV1beta1Api; +import io.kubernetes.client.openapi.models.NetworkingV1beta1Ingress; +import io.kubernetes.client.openapi.models.V1ConfigMap; +import io.kubernetes.client.openapi.models.V1Deployment; +import io.kubernetes.client.openapi.models.V1Secret; +import io.kubernetes.client.openapi.models.V1Service; +import io.kubernetes.client.util.Config; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.junit.After; +import org.junit.BeforeClass; +import org.junit.Test; + +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils; +import org.springframework.http.client.ClientHttpResponse; +import org.springframework.web.client.ResponseErrorHandler; +import org.springframework.web.client.RestTemplate; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +/** + * @author Ryan Baxter + */ +public class ConfigMapAndSecretIT { + + private static final Log LOG = LogFactory.getLog(ConfigMapAndSecretIT.class); + + private static final String KIND_REPO_HOST_PORT = "localhost:5000"; + + private static final String KIND_REPO_URL = "http://" + KIND_REPO_HOST_PORT; + + private static final String IMAGE = "spring-cloud-kubernetes-client-config-it"; + + private static final String IMAGE_TAG = "2.0.0-SNAPSHOT"; + + private static final String LOCAL_REPO = "docker.io/springcloud"; + + private static final String LOCAL_IMAGE = LOCAL_REPO + "/" + IMAGE + ":" + IMAGE_TAG; + + private static final String KIND_IMAGE = KIND_REPO_HOST_PORT + "/" + IMAGE; + + private static final String KIND_IMAGE_WITH_TAG = KIND_IMAGE + ":" + IMAGE_TAG; + + private static final String SPRING_CLOUD_CLIENT_CONFIG_IT_DEPLOYMENT_NAME = "spring-cloud-kubernetes-client-config-it-deployment"; + + private static final String K8S_CONFIG_CLIENT_IT_NAME = "spring-cloud-kubernetes-client-config-it-deployment"; + + private static final String K8S_CONFIG_CLIENT_IT_SERVICE_NAME = "spring-cloud-kubernetes-client-config-it"; + + private static final String NAMESPACE = "default"; + + private static final String MYPROPERTY_URL = "http://localhost:80/client-config-it/myProperty"; + + private static final String MYSECRET_URL = "http://localhost:80/client-config-it/mySecret"; + + private static final String APP_NAME = "spring-cloud-kubernetes-client-config-it"; + + private static ApiClient client; + + private static CoreV1Api api; + + private static AppsV1Api appsApi; + + private static NetworkingV1beta1Api networkingApi; + + private static K8SUtils k8SUtils; + + @BeforeClass + public static void setup() throws Exception { + client = Config.defaultClient(); + // client.setDebugging(true); + Configuration.setDefaultApiClient(client); + api = new CoreV1Api(); + appsApi = new AppsV1Api(); + networkingApi = new NetworkingV1beta1Api(); + k8SUtils = new K8SUtils(api, appsApi); + + DockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder() + .withRegistryUrl(KIND_REPO_URL).build(); + DockerHttpClient httpClient = new ApacheDockerHttpClient.Builder().dockerHost(config.getDockerHost()) + .sslConfig(config.getSSLConfig()).build(); + + DockerClient dockerClient = DockerClientImpl.getInstance(config, httpClient); + dockerClient.tagImageCmd(LOCAL_IMAGE, KIND_IMAGE, IMAGE_TAG).exec(); + dockerClient.pushImageCmd(KIND_IMAGE_WITH_TAG).start(); + } + + @After + public void after() throws Exception { + appsApi.deleteCollectionNamespacedDeployment(NAMESPACE, null, null, null, + "metadata.name=" + K8S_CONFIG_CLIENT_IT_NAME, null, null, null, null, null, null, null, null); + api.deleteNamespacedService(K8S_CONFIG_CLIENT_IT_SERVICE_NAME, NAMESPACE, null, null, null, null, null, null); + networkingApi.deleteNamespacedIngress("it-ingress", NAMESPACE, null, null, null, null, null, null); + api.deleteNamespacedConfigMap(APP_NAME, NAMESPACE, null, null, null, null, null, null); + api.deleteNamespacedSecret(APP_NAME, NAMESPACE, null, null, null, null, null, null); + } + + public void testConfigMapAndSecretRefresh() throws Exception { + + RestTemplate rest = new RestTemplateBuilder().build(); + rest.setErrorHandler(new ResponseErrorHandler() { + @Override + public boolean hasError(ClientHttpResponse clientHttpResponse) throws IOException { + LOG.warn("Received response status code: " + clientHttpResponse.getRawStatusCode()); + if (clientHttpResponse.getRawStatusCode() == 503) { + return false; + } + return true; + } + + @Override + public void handleError(ClientHttpResponse clientHttpResponse) throws IOException { + + } + }); + + // Sometimes the NGINX ingress takes a bit to catch up and realize the service is + // available and we get a 503, we just need to wait a bit + await().timeout(Duration.ofSeconds(60)) + .until(() -> rest.getForEntity(MYPROPERTY_URL, String.class).getStatusCode().is2xxSuccessful()); + + String myProperty = rest.getForObject(MYPROPERTY_URL, String.class); + assertThat(myProperty).isEqualTo("from-config-map"); + String mySecret = rest.getForObject(MYSECRET_URL, String.class); + assertThat(mySecret).isEqualTo("p455w0rd"); + + V1ConfigMap configMap = getConfigK8sClientItConfigMap(); + Map data = configMap.getData(); + data.replace("application.yaml", data.get("application.yaml").replace("from-config-map", "from-unit-test")); + configMap.data(data); + api.replaceNamespacedConfigMap(APP_NAME, NAMESPACE, configMap, null, null, null); + await().timeout(Duration.ofSeconds(15)) + .until(() -> rest.getForObject(MYPROPERTY_URL, String.class).equals("from-unit-test")); + myProperty = rest.getForObject(MYPROPERTY_URL, String.class); + assertThat(myProperty).isEqualTo("from-unit-test"); + + V1Secret secret = getConfigK8sClientItCSecret(); + Map secretData = secret.getData(); + secretData.replace("my.config.mySecret", "p455w1rd".getBytes()); + secret.setData(secretData); + api.replaceNamespacedSecret(APP_NAME, NAMESPACE, secret, null, null, null); + await().timeout(Duration.ofSeconds(15)) + .until(() -> rest.getForObject(MYSECRET_URL, String.class).equals("p455w1rd")); + mySecret = rest.getForObject(MYSECRET_URL, String.class); + assertThat(mySecret).isEqualTo("p455w1rd"); + + } + + @Test + public void testConfigMapAndSecretWatchRefresh() throws Exception { + deployConfigK8sClientIt(); + + // Check to make sure the controller deployment is ready + k8SUtils.waitForDeployment(SPRING_CLOUD_CLIENT_CONFIG_IT_DEPLOYMENT_NAME, NAMESPACE); + testConfigMapAndSecretRefresh(); + } + + @Test + public void testConfigMapAndSecretPollingRefresh() throws Exception { + deployConfigK8sClientPollingIt(); + + // Check to make sure the controller deployment is ready + k8SUtils.waitForDeployment(SPRING_CLOUD_CLIENT_CONFIG_IT_DEPLOYMENT_NAME, NAMESPACE); + testConfigMapAndSecretRefresh(); + } + + private static void deployConfigK8sClientIt() throws Exception { + k8SUtils.waitForDeploymentToBeDeleted(K8S_CONFIG_CLIENT_IT_NAME, NAMESPACE); + api.createNamespacedSecret(NAMESPACE, getConfigK8sClientItCSecret(), null, null, null); + api.createNamespacedConfigMap(NAMESPACE, getConfigK8sClientItConfigMap(), null, null, null); + appsApi.createNamespacedDeployment(NAMESPACE, getConfigK8sClientItDeployment(), null, null, null); + api.createNamespacedService(NAMESPACE, getConfigK8sClientItService(), null, null, null); + networkingApi.createNamespacedIngress(NAMESPACE, getConfigK8sClientItIngress(), null, null, null); + } + + private static void deployConfigK8sClientPollingIt() throws Exception { + k8SUtils.waitForDeploymentToBeDeleted(K8S_CONFIG_CLIENT_IT_NAME, NAMESPACE); + api.createNamespacedSecret(NAMESPACE, getConfigK8sClientItCSecret(), null, null, null); + api.createNamespacedConfigMap(NAMESPACE, getConfigK8sClientItConfigMap(), null, null, null); + appsApi.createNamespacedDeployment(NAMESPACE, getConfigK8sClientItPollingDeployment(), null, null, null); + api.createNamespacedService(NAMESPACE, getConfigK8sClientItService(), null, null, null); + networkingApi.createNamespacedIngress(NAMESPACE, getConfigK8sClientItIngress(), null, null, null); + } + + private static V1Deployment getConfigK8sClientItDeployment() throws Exception { + V1Deployment deployment = (V1Deployment) k8SUtils + .readYamlFromClasspath("spring-cloud-kubernetes-client-config-it-deployment.yaml"); + return deployment; + } + + private static V1Deployment getConfigK8sClientItPollingDeployment() throws Exception { + V1Deployment deployment = (V1Deployment) k8SUtils + .readYamlFromClasspath("spring-cloud-kubernetes-client-config-it-polling-deployment.yaml"); + return deployment; + } + + private static V1Service getConfigK8sClientItService() throws Exception { + V1Service service = (V1Service) k8SUtils + .readYamlFromClasspath("spring-cloud-kubernetes-client-config-it-service.yaml"); + return service; + } + + private static NetworkingV1beta1Ingress getConfigK8sClientItIngress() throws Exception { + NetworkingV1beta1Ingress ingress = (NetworkingV1beta1Ingress) k8SUtils + .readYamlFromClasspath("spring-cloud-kubernetes-client-config-it-ingress.yaml"); + return ingress; + } + + private static V1ConfigMap getConfigK8sClientItConfigMap() throws Exception { + V1ConfigMap configmap = (V1ConfigMap) k8SUtils + .readYamlFromClasspath("spring-cloud-kubernetes-client-config-it-configmap.yaml"); + return configmap; + } + + private static V1Secret getConfigK8sClientItCSecret() throws Exception { + V1Secret secret = (V1Secret) k8SUtils + .readYamlFromClasspath("spring-cloud-kubernetes-client-config-it-secret.yaml"); + return secret; + } + +} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/test/resources/spring-cloud-kubernetes-client-config-it-configmap.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/test/resources/spring-cloud-kubernetes-client-config-it-configmap.yaml new file mode 100644 index 00000000..6a4e128c --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/test/resources/spring-cloud-kubernetes-client-config-it-configmap.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +data: + application.yaml: |- + my: + config: + myProperty: from-config-map +kind: ConfigMap +metadata: + name: spring-cloud-kubernetes-client-config-it diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/test/resources/spring-cloud-kubernetes-client-config-it-deployment.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/test/resources/spring-cloud-kubernetes-client-config-it-deployment.yaml new file mode 100644 index 00000000..7ad29f53 --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/test/resources/spring-cloud-kubernetes-client-config-it-deployment.yaml @@ -0,0 +1,31 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: spring-cloud-kubernetes-client-config-it-deployment +spec: + selector: + matchLabels: + app: spring-cloud-kubernetes-client-config-it + template: + metadata: + labels: + app: spring-cloud-kubernetes-client-config-it + spec: + serviceAccountName: spring-cloud-kubernetes-serviceaccount + containers: + - name: spring-cloud-kubernetes-client-config-it + image: localhost:5000/spring-cloud-kubernetes-client-config-it:2.0.0-SNAPSHOT + imagePullPolicy: IfNotPresent + readinessProbe: + httpGet: + port: 8080 + path: /actuator/health/readiness + livenessProbe: + httpGet: + port: 8080 + path: /actuator/health/liveness + env: + - name: SPRING_PROFILES_ACTIVE + value: kubernetes + ports: + - containerPort: 8080 diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/test/resources/spring-cloud-kubernetes-client-config-it-ingress.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/test/resources/spring-cloud-kubernetes-client-config-it-ingress.yaml new file mode 100644 index 00000000..1cac3593 --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/test/resources/spring-cloud-kubernetes-client-config-it-ingress.yaml @@ -0,0 +1,14 @@ +apiVersion: networking.k8s.io/v1beta1 +kind: Ingress +metadata: + name: it-ingress + annotations: + nginx.ingress.kubernetes.io/rewrite-target: /$2 +spec: + rules: + - http: + paths: + - path: /client-config-it(/|$)(.*) + backend: + serviceName: spring-cloud-kubernetes-client-config-it + servicePort: 8080 diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/test/resources/spring-cloud-kubernetes-client-config-it-polling-deployment.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/test/resources/spring-cloud-kubernetes-client-config-it-polling-deployment.yaml new file mode 100644 index 00000000..f769348e --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/test/resources/spring-cloud-kubernetes-client-config-it-polling-deployment.yaml @@ -0,0 +1,33 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: spring-cloud-kubernetes-client-config-it-deployment +spec: + selector: + matchLabels: + app: spring-cloud-kubernetes-client-config-it + template: + metadata: + labels: + app: spring-cloud-kubernetes-client-config-it + spec: + serviceAccountName: spring-cloud-kubernetes-serviceaccount + containers: + - name: spring-cloud-kubernetes-client-config-it + image: localhost:5000/spring-cloud-kubernetes-client-config-it:2.0.0-SNAPSHOT + imagePullPolicy: IfNotPresent + readinessProbe: + httpGet: + port: 8080 + path: /actuator/health/readiness + livenessProbe: + httpGet: + port: 8080 + path: /actuator/health/liveness + env: + - name: SPRING_PROFILES_ACTIVE + value: kubernetes + - name: SPRING_CLOUD_KUBERNETES_RELOAD_MODE + value: polling + ports: + - containerPort: 8080 diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/test/resources/spring-cloud-kubernetes-client-config-it-secret.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/test/resources/spring-cloud-kubernetes-client-config-it-secret.yaml new file mode 100644 index 00000000..9b68d973 --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/test/resources/spring-cloud-kubernetes-client-config-it-secret.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +data: + my.config.mySecret: cDQ1NXcwcmQ= +kind: Secret +metadata: + name: spring-cloud-kubernetes-client-config-it +type: Opaque diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/test/resources/spring-cloud-kubernetes-client-config-it-service.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/test/resources/spring-cloud-kubernetes-client-config-it-service.yaml new file mode 100644 index 00000000..10aaed50 --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/test/resources/spring-cloud-kubernetes-client-config-it-service.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + app: spring-cloud-kubernetes-client-config-it + name: spring-cloud-kubernetes-client-config-it +spec: + ports: + - name: http + port: 8080 + targetPort: 8080 + selector: + app: spring-cloud-kubernetes-client-config-it + type: ClusterIP diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-core-k8s-client-it/src/test/java/org/springframework/cloud/kubernetes/core/k8s/it/ActuatorEndpointIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-core-k8s-client-it/src/test/java/org/springframework/cloud/kubernetes/core/k8s/it/ActuatorEndpointIT.java index 213ddb02..fabd3577 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-core-k8s-client-it/src/test/java/org/springframework/cloud/kubernetes/core/k8s/it/ActuatorEndpointIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-core-k8s-client-it/src/test/java/org/springframework/cloud/kubernetes/core/k8s/it/ActuatorEndpointIT.java @@ -16,6 +16,8 @@ package org.springframework.cloud.kubernetes.core.k8s.it; +import java.io.IOException; +import java.time.Duration; import java.util.Map; import com.github.dockerjava.api.DockerClient; @@ -33,6 +35,8 @@ import io.kubernetes.client.openapi.models.NetworkingV1beta1Ingress; import io.kubernetes.client.openapi.models.V1Deployment; import io.kubernetes.client.openapi.models.V1Service; import io.kubernetes.client.util.Config; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; @@ -41,9 +45,12 @@ import org.mockito.junit.MockitoJUnitRunner; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils; +import org.springframework.http.client.ClientHttpResponse; +import org.springframework.web.client.ResponseErrorHandler; import org.springframework.web.client.RestTemplate; import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; +import static org.awaitility.Awaitility.await; /** * @author Ryan Baxter @@ -51,6 +58,8 @@ import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; @RunWith(MockitoJUnitRunner.class) public class ActuatorEndpointIT { + private static final Log LOG = LogFactory.getLog(ActuatorEndpointIT.class); + private static final String KIND_REPO_HOST_PORT = "localhost:5000"; private static final String KIND_REPO_URL = "http://" + KIND_REPO_HOST_PORT; @@ -96,9 +105,9 @@ public class ActuatorEndpointIT { k8SUtils = new K8SUtils(api, appsApi); DockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder() - .withRegistryUrl(KIND_REPO_URL).build(); + .withRegistryUrl(KIND_REPO_URL).build(); DockerHttpClient httpClient = new ApacheDockerHttpClient.Builder().dockerHost(config.getDockerHost()) - .sslConfig(config.getSSLConfig()).build(); + .sslConfig(config.getSSLConfig()).build(); DockerClient dockerClient = DockerClientImpl.getInstance(config, httpClient); dockerClient.tagImageCmd(LOCAL_IMAGE, KIND_IMAGE, IMAGE_TAG).exec(); @@ -118,19 +127,19 @@ public class ActuatorEndpointIT { private static V1Deployment getCoreK8sClientItDeployment() throws Exception { V1Deployment deployment = (V1Deployment) k8SUtils - .readYamlFromClasspath("spring-cloud-kubernetes-core-k8s-client-it-deployment.yaml"); + .readYamlFromClasspath("spring-cloud-kubernetes-core-k8s-client-it-deployment.yaml"); return deployment; } private static V1Service getCoreK8sClientItService() throws Exception { V1Service service = (V1Service) k8SUtils - .readYamlFromClasspath("spring-cloud-kubernetes-core-k8s-client-it-service.yaml"); + .readYamlFromClasspath("spring-cloud-kubernetes-core-k8s-client-it-service.yaml"); return service; } private static NetworkingV1beta1Ingress getCoreK8sClientItIngress() throws Exception { NetworkingV1beta1Ingress ingress = (NetworkingV1beta1Ingress) k8SUtils - .readYamlFromClasspath("spring-cloud-kubernetes-core-k8s-client-it-ingress.yaml"); + .readYamlFromClasspath("spring-cloud-kubernetes-core-k8s-client-it-ingress.yaml"); return ingress; } @@ -138,8 +147,29 @@ public class ActuatorEndpointIT { public void testHealth() { RestTemplate rest = new RestTemplateBuilder().build(); + rest.setErrorHandler(new ResponseErrorHandler() { + @Override + public boolean hasError(ClientHttpResponse clientHttpResponse) throws IOException { + LOG.warn("Received response status code: " + clientHttpResponse.getRawStatusCode()); + if (clientHttpResponse.getRawStatusCode() == 503) { + return false; + } + return true; + } + + @Override + public void handleError(ClientHttpResponse clientHttpResponse) throws IOException { + + } + }); + + //Sometimes the NGINX ingress takes a bit to catch up and realize the service is available and we get a 503, we just need to wait a bit + await().timeout(Duration.ofSeconds(60)) + .until(() -> rest.getForEntity("http://localhost:80/core-k8s-client-it/actuator/health", String.class) + .getStatusCode().is2xxSuccessful()); + Map health = rest.getForObject("http://localhost:80/core-k8s-client-it/actuator/health", - Map.class); + Map.class); Map components = (Map) health.get("components"); assertThat(components.containsKey("kubernetes")).isTrue(); Map kubernetes = (Map) components.get("kubernetes"); @@ -159,6 +189,28 @@ public class ActuatorEndpointIT { public void testInfo() { RestTemplate rest = new RestTemplateBuilder().build(); + + rest.setErrorHandler(new ResponseErrorHandler() { + @Override + public boolean hasError(ClientHttpResponse clientHttpResponse) throws IOException { + LOG.warn("Received response status code: " + clientHttpResponse.getRawStatusCode()); + if (clientHttpResponse.getRawStatusCode() == 503) { + return false; + } + return true; + } + + @Override + public void handleError(ClientHttpResponse clientHttpResponse) throws IOException { + + } + }); + + //Sometimes the NGINX ingress takes a bit to catch up and realize the service is available and we get a 503, we just need to wait a bit + await().timeout(Duration.ofSeconds(60)) + .until(() -> rest.getForEntity("http://localhost:80/core-k8s-client-it/actuator/info", String.class) + .getStatusCode().is2xxSuccessful()); + Map info = rest.getForObject("http://localhost:80/core-k8s-client-it/actuator/info", Map.class); Map kubernetes = (Map) info.get("kubernetes"); assertThat(kubernetes.containsKey("hostIp")).isTrue(); @@ -173,7 +225,7 @@ public class ActuatorEndpointIT { @AfterClass public static void after() throws Exception { appsApi.deleteCollectionNamespacedDeployment(NAMESPACE, null, null, null, - "metadata.name=" + K8S_CONFIG_CLIENT_IT_NAME, null, null, null, null, null, null, null, null); + "metadata.name=" + K8S_CONFIG_CLIENT_IT_NAME, null, null, null, null, null, null, null, null); api.deleteNamespacedService(K8S_CONFIG_CLIENT_IT_SERVICE_NAME, NAMESPACE, null, null, null, null, null, null); networkingApi.deleteNamespacedIngress("it-ingress", NAMESPACE, null, null, null, null, null, null); } diff --git a/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/K8SUtils.java b/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/K8SUtils.java index 5dbc521f..191b0c96 100644 --- a/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/K8SUtils.java +++ b/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/K8SUtils.java @@ -154,6 +154,15 @@ public class K8SUtils { .until(() -> isDeployentReady(deploymentName, namespace)); } + public void waitForDeploymentToBeDeleted(String deploymentName, String namespace) { + await().timeout( + Duration.ofSeconds(90)).until( + () -> appsApi + .listNamespacedDeployment(namespace, null, null, null, + "metadata.name=" + deploymentName, null, null, null, null, null) + .getItems().isEmpty()); + } + public boolean isDeployentReady(String deploymentName, String namespace) throws ApiException { V1DeploymentList deployments = appsApi.listNamespacedDeployment(namespace, null, null, null, "metadata.name=" + deploymentName, null, null, null, null, null); diff --git a/spring-cloud-starter-kubernetes-all/pom.xml b/spring-cloud-starter-kubernetes-all/pom.xml index ed34d2c6..8e3a9909 100644 --- a/spring-cloud-starter-kubernetes-all/pom.xml +++ b/spring-cloud-starter-kubernetes-all/pom.xml @@ -38,7 +38,7 @@ org.springframework.cloud - spring-cloud-kubernetes-config + spring-cloud-kubernetes-fabric8-config diff --git a/spring-cloud-starter-kubernetes-client-all/pom.xml b/spring-cloud-starter-kubernetes-client-all/pom.xml index 1e356384..608c03cf 100644 --- a/spring-cloud-starter-kubernetes-client-all/pom.xml +++ b/spring-cloud-starter-kubernetes-client-all/pom.xml @@ -16,6 +16,10 @@ org.springframework.cloud spring-cloud-kubernetes-client-autoconfig + + org.springframework.cloud + spring-cloud-kubernetes-client-config + diff --git a/spring-cloud-starter-kubernetes-config/pom.xml b/spring-cloud-starter-kubernetes-config/pom.xml index 54c5365c..d4332ece 100644 --- a/spring-cloud-starter-kubernetes-config/pom.xml +++ b/spring-cloud-starter-kubernetes-config/pom.xml @@ -42,7 +42,7 @@ org.springframework.cloud - spring-cloud-kubernetes-config + spring-cloud-kubernetes-fabric8-config