diff --git a/README.adoc b/README.adoc index 608b4b90..614fccec 100644 --- a/README.adoc +++ b/README.adoc @@ -272,6 +272,9 @@ spring: In the preceding example, if `spring.cloud.kubernetes.config.namespace` had not been set, the `ConfigMap` named `c1` would be looked up in the namespace that the application runs. +See <> to get a better understanding of how the namespace +of the application is resolved. + Any matching `ConfigMap` that is found is processed as follows: @@ -494,6 +497,179 @@ spec: ---- ==== +You could run into a situation where there are multiple configs maps that have the same property names. For example: + +==== +[source,yaml] +---- +kind: ConfigMap +apiVersion: v1 +metadata: + name: config-map-one +data: + application.yml: |- + greeting: + message: Say Hello from one +---- +==== + +and + +==== +[source,yaml] +---- +kind: ConfigMap +apiVersion: v1 +metadata: + name: config-map-two +data: + application.yml: |- + greeting: + message: Say Hello from two +---- +==== + +Depending on the order in which you place these in `bootstrap.yaml|properties`, you might end up with an un-expected result (the last config map wins). For example: + +==== +[source,yaml] +---- +spring: + application: + name: cloud-k8s-app + cloud: + kubernetes: + config: + namespace: default-namespace + sources: + - name: config-map-two + - name: config-map-one +---- +==== + +will result in property `greetings.message` being `Say Hello from one`. + +There is a way to change this default configuration by specifying `useNameAsPrefix`. For example: + +==== +[source,yaml] +---- +spring: + application: + name: with-prefix + cloud: + kubernetes: + config: + useNameAsPrefix: true + namespace: default-namespace + sources: + - name: config-map-one + useNameAsPrefix: false + - name: config-map-two +---- +==== + +Such a configuration will result in two properties being generated: + + - `greetings.message` equal to `Say Hello from one`. + + - `config-map-two.greetings.message` equal to `Say Hello from two` + +Notice that `spring.cloud.kubernetes.config.useNameAsPrefix` has a _lower_ priority than `spring.cloud.kubernetes.config.sources.useNameAsPrefix`. +This allows you to set a "default" strategy for all sources, at the same time allowing to override only a few. + +If using the config map name is not an option, you can specify a different strategy, called : `explicitPrefix`. Since this is an _explicit_ prefix that +you select, it can only be supplied to the `sources` level. At the same time it has a higher priority than `useNameAsPrefix`. Let's suppose we have a third config map with these entries: + + +==== +[source,yaml] +---- +kind: ConfigMap +apiVersion: v1 +metadata: + name: config-map-three +data: + application.yml: |- + greeting: + message: Say Hello from three +---- +==== + +A configuration like the one below: + +==== +[source,yaml] +---- +spring: + application: + name: with-prefix + cloud: + kubernetes: + config: + useNameAsPrefix: true + namespace: default-namespace + sources: + - name: config-map-one + useNameAsPrefix: false + - name: config-map-two + explicitPrefix: two + - name: config-map-three +---- +==== + +will result in three properties being generated: + + - `greetings.message` equal to `Say Hello from one`. + + - `two.greetings.message` equal to `Say Hello from two`. + + - `config-map-three.greetings.message` equal to `Say Hello from three`. + +By default, besides reading the config map that is specified in the `sources` configuration, Spring will also try to read +all properties from "profile aware" sources. The easiest way to explain this is via an example. Let's suppose your application +enables a profile called "dev" and you have a configuration like the one below: + +==== +[source,yaml] +---- +spring: + application: + name: spring-k8s + cloud: + kubernetes: + config: + namespace: default-namespace + sources: + - name: config-map-one +---- +==== + +Besides reading the `config-map-one`, Spring will also try to read `config-map-one-dev`; in this particular order. Each active profile +generates such a profile aware config map. + +Though your application should not be impacted by such a config map, it can be disabled if needed: + +==== +[source,yaml] +---- +spring: + application: + name: spring-k8s + cloud: + kubernetes: + config: + includeProfileSpecificSources: false + namespace: default-namespace + sources: + - name: config-map-one + includeProfileSpecificSources: false +---- +==== + +Notice that just like before, there are two levels where you can specify this property: for all config maps or +for individual ones; the latter having a higher priority. + NOTE: You should check the security configuration section. To access config maps from inside a pod you need to have the correct Kubernetes service accounts, roles and role bindings. @@ -659,7 +835,7 @@ spring: sources: # Spring Cloud Kubernetes looks up a Secret named s1 in namespace default-namespace - name: s1 - # Spring Cloud Kubernetes looks up a Secret named default-name in whatever namespace n2 + # Spring Cloud Kubernetes looks up a Secret named default-name in namespace n2 - namespace: n2 # Spring Cloud Kubernetes looks up a Secret named s3 in namespace n3 - namespace: n3 @@ -669,6 +845,8 @@ spring: In the preceding example, if `spring.cloud.kubernetes.secrets.namespace` had not been set, the `Secret` named `s1` would be looked up in the namespace that the application runs. +See <> to get a better understanding of how the namespace +of the application is resolved. .Properties: @@ -694,6 +872,45 @@ https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-Configuration-Bi You can find an example of an application that uses secrets (though it has not been updated to use the new `spring-cloud-kubernetes` project) at https://github.com/fabric8-quickstarts/spring-boot-camel-config[spring-boot-camel-config] +[[namespace-resolution]] +=== Namespace resolution +Finding an application namespace happens on a best-effort basis. There are some steps that we iterate in order +to find it. The easiest and most common one, is to specify it in the proper configuration, for example: + +==== +[source,yaml] +---- +spring: + application: + name: app + cloud: + kubernetes: + secrets: + name: secret + namespace: default + sources: + # Spring Cloud Kubernetes looks up a Secret named 'a' in namespace 'default' + - name: a + # Spring Cloud Kubernetes looks up a Secret named 'secret' in namespace 'b' + - namespace: b + # Spring Cloud Kubernetes looks up a Secret named 'd' in namespace 'c' + - namespace: c + name: d +---- +==== + +Remember that the same can be done for config maps. If such a namespace is not specified, it will be read (in this order): + +1. from property `spring.cloud.kubernetes.client.namespace` +2. from a String residing in a file denoted by `spring.cloud.kubernetes.client.serviceAccountNamespacePath` property +3. from a String residing in `/var/run/secrets/kubernetes.io/serviceaccount/namespace` file +(kubernetes default namespace path) +4. from a designated client method call (for example fabric8's : `KubernetesClient::getNamespace`), if the client provides +such a method. This, in turn, could be configured via environment properties. For example fabric8 client can be configured via +"KUBERNETES_NAMESPACE" property; consult the client documentation for exact details. + +Failure to find a namespace from the above steps will result in an Exception being raised. + === `PropertySource` Reload WARNING: This functionality has been deprecated in the 2020.0 release. Please see @@ -856,13 +1073,16 @@ The Kubernetes health indicator (which is part of the core module) exposes the f * Pod name, IP address, namespace, service account, node name, and its IP address * A flag that indicates whether the Spring Boot application is internal or external to Kubernetes +You can disable this `HealthContributor` by setting `management.health.kubernetes.enabled` +to `false` in `application.[properties | yaml]`. + == Info Contributor Spring Cloud Kubernetes includes an `InfoContributor` which adds Pod information to Spring Boot's `/info` Acturator endpoint. You can disable this `InfoContributor` by setting `management.info.kubernetes.enabled` -to `false` in `bootstrap.[properties | yaml]`. +to `false` in `application.[properties | yaml]`. == Leader Election The Spring Cloud Kubernetes leader election mechanism implements the leader election API of Spring Integration using a Kubernetes ConfigMap. @@ -1250,7 +1470,7 @@ To see the list of all Kubernetes related configuration properties please check == Building -:jdkversion: 1.8 +:jdkversion: 17 === Basic Compile and Test @@ -1276,23 +1496,9 @@ the `.mvn` configuration, so if you find you have to do it to make a build succeed, please raise a ticket to get the settings added to source control. -For hints on how to build the project look in `.travis.yml` if there -is one. There should be a "script" and maybe "install" command. Also -look at the "services" section to see if any services need to be -running locally (e.g. mongo or rabbit). Ignore the git-related bits -that you might find in "before_install" since they're related to setting git -credentials and you already have those. +The projects that require middleware (i.e. Redis) for testing generally +require that a local instance of [Docker](https://www.docker.com/get-started) is installed and running. -The projects that require middleware generally include a -`docker-compose.yml`, so consider using -https://docs.docker.com/compose/[Docker Compose] to run the middeware servers -in Docker containers. See the README in the -https://github.com/spring-cloud-samples/scripts[scripts demo -repository] for specific instructions about the common cases of mongo, -rabbit and redis. - -NOTE: If all else fails, build with the command from `.travis.yml` (usually -`./mvnw install`). === Documentation @@ -1525,3 +1731,54 @@ Go to `File` -> `Settings` -> `Other settings` -> `Checkstyle`. There click on t - `checkstyle.additional.suppressions.file` - this variable corresponds to suppressions in your local project. E.g. you're working on `spring-cloud-contract`. Then point to the `project-root/src/checkstyle/checkstyle-suppressions.xml` folder. Example for `spring-cloud-contract` would be: `/home/username/spring-cloud-contract/src/checkstyle/checkstyle-suppressions.xml`. IMPORTANT: Remember to set the `Scan Scope` to `All sources` since we apply checkstyle rules for production and test sources. + +=== Duplicate Finder + +Spring Cloud Build brings along the `basepom:duplicate-finder-maven-plugin`, that enables flagging duplicate and conflicting classes and resources on the java classpath. + +==== Duplicate Finder configuration + +Duplicate finder is *enabled by default* and will run in the `verify` phase of your Maven build, but it will only take effect in your project if you add the `duplicate-finder-maven-plugin` to the `build` section of the projecst's `pom.xml`. + +.pom.xml +[source,xml] +---- + + + + org.basepom.maven + duplicate-finder-maven-plugin + + + +---- + +For other properties, we have set defaults as listed in the https://github.com/basepom/duplicate-finder-maven-plugin/wiki[plugin documentation]. + +You can easily override them but setting the value of the selected property prefixed with `duplicate-finder-maven-plugin`. For example, set `duplicate-finder-maven-plugin.skip` to `true` in order to skip duplicates check in your build. + +If you need to add `ignoredClassPatterns` or `ignoredResourcePatterns` to your setup, make sure to add them in the plugin configuration section of your project: + +[source,xml] +---- + + + + org.basepom.maven + duplicate-finder-maven-plugin + + + org.joda.time.base.BaseDateTime + .*module-info + + + changelog.txt + + + + + + + +---- + diff --git a/docs/src/main/asciidoc/_configprops.adoc b/docs/src/main/asciidoc/_configprops.adoc index 924fd661..a1ce0b13 100644 --- a/docs/src/main/asciidoc/_configprops.adoc +++ b/docs/src/main/asciidoc/_configprops.adoc @@ -41,6 +41,7 @@ |spring.cloud.kubernetes.client.service-account-namespace-path | `/var/run/secrets/kubernetes.io/serviceaccount/namespace` | |spring.cloud.kubernetes.client.trust-certs | | |spring.cloud.kubernetes.client.trustCerts | `false` | Kubernetes API Trust Certificates +|spring.cloud.kubernetes.client.user-agent | `Spring-Cloud-Kubernetes-Application` | |spring.cloud.kubernetes.client.username | | Kubernetes API Username |spring.cloud.kubernetes.client.watch-reconnect-interval | | |spring.cloud.kubernetes.client.watch-reconnect-limit | | @@ -48,10 +49,12 @@ |spring.cloud.kubernetes.client.watchReconnectLimit | `-1` | Reconnect Interval limit retries |spring.cloud.kubernetes.config.enable-api | `true` | |spring.cloud.kubernetes.config.enabled | `true` | Enable the ConfigMap property source locator. +|spring.cloud.kubernetes.config.include-profile-specific-sources | `true` | |spring.cloud.kubernetes.config.name | | |spring.cloud.kubernetes.config.namespace | | |spring.cloud.kubernetes.config.paths | | |spring.cloud.kubernetes.config.sources | | +|spring.cloud.kubernetes.config.use-name-as-prefix | `false` | |spring.cloud.kubernetes.discovery.all-namespaces | `false` | If discovering all namespaces. |spring.cloud.kubernetes.discovery.cache-loading-timeout-seconds | `60` | Timeout for initializing discovery cache, will abort the application if exceeded. |spring.cloud.kubernetes.discovery.enabled | `true` | If Kubernetes Discovery is enabled. @@ -91,11 +94,12 @@ |spring.cloud.kubernetes.reload.strategy | | Sets the reload strategy for Kubernetes configuration reload on change. |spring.cloud.kubernetes.secrets.enable-api | `false` | |spring.cloud.kubernetes.secrets.enabled | `true` | Enable the Secrets property source locator. +|spring.cloud.kubernetes.secrets.include-profile-specific-sources | `true` | |spring.cloud.kubernetes.secrets.labels | | |spring.cloud.kubernetes.secrets.name | | |spring.cloud.kubernetes.secrets.namespace | | |spring.cloud.kubernetes.secrets.paths | | -|spring.cloud.kubernetes.secrets.sources | | -|spring.cloud.kubernetes.userAgent | `Spring-Cloud-Kubernetes-Application` | `User-Agent` header +|spring.cloud.kubernetes.secrets.sources | | +|spring.cloud.kubernetes.secrets.use-name-as-prefix | `false` | -|=== +|=== \ No newline at end of file diff --git a/pom.xml b/pom.xml index 3e573e1c..799dd551 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ org.springframework.cloud spring-cloud-build - 3.0.5 + 3.0.6-SNAPSHOT @@ -64,7 +64,7 @@ 3.0.6-SNAPSHOT 3.0.7-SNAPSHOT - 3.0.3 + 3.0.4-SNAPSHOT 3.0.6-SNAPSHOT diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-loadbalancer-it/src/test/java/org/springframework/cloud/kubernetes/client/loadbalancer/it/LoadBalancerIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-loadbalancer-it/src/test/java/org/springframework/cloud/kubernetes/client/loadbalancer/it/LoadBalancerIT.java index 942b87a1..e7d01d9f 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-loadbalancer-it/src/test/java/org/springframework/cloud/kubernetes/client/loadbalancer/it/LoadBalancerIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-loadbalancer-it/src/test/java/org/springframework/cloud/kubernetes/client/loadbalancer/it/LoadBalancerIT.java @@ -211,8 +211,7 @@ public class LoadBalancerIT { } private V1Ingress getWiremockIngress() throws Exception { - V1Ingress ingress = (V1Ingress) k8SUtils - .readYamlFromClasspath("wiremock-ingress.yaml"); + V1Ingress ingress = (V1Ingress) k8SUtils.readYamlFromClasspath("wiremock-ingress.yaml"); return ingress; } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-reactive-discovery-client-it/src/test/java/org/springframework/cloud/kubernetes/client/reactive/discovery/it/ReactiveDiscoveryClientIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-reactive-discovery-client-it/src/test/java/org/springframework/cloud/kubernetes/client/reactive/discovery/it/ReactiveDiscoveryClientIT.java index 3cce10a1..34134e69 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-reactive-discovery-client-it/src/test/java/org/springframework/cloud/kubernetes/client/reactive/discovery/it/ReactiveDiscoveryClientIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-reactive-discovery-client-it/src/test/java/org/springframework/cloud/kubernetes/client/reactive/discovery/it/ReactiveDiscoveryClientIT.java @@ -213,8 +213,7 @@ public class ReactiveDiscoveryClientIT { } private V1Ingress getWiremockIngress() throws Exception { - V1Ingress ingress = (V1Ingress) k8SUtils - .readYamlFromClasspath("wiremock-ingress.yaml"); + V1Ingress ingress = (V1Ingress) k8SUtils.readYamlFromClasspath("wiremock-ingress.yaml"); return ingress; } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-configuration-watcher-it/src/test/java/org/springframework/cloud/kubernetes/configuration/watcher/ActuatorRefreshIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-configuration-watcher-it/src/test/java/org/springframework/cloud/kubernetes/configuration/watcher/ActuatorRefreshIT.java index 9b5a0205..c5a1ee89 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-configuration-watcher-it/src/test/java/org/springframework/cloud/kubernetes/configuration/watcher/ActuatorRefreshIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-configuration-watcher-it/src/test/java/org/springframework/cloud/kubernetes/configuration/watcher/ActuatorRefreshIT.java @@ -177,8 +177,7 @@ public class ActuatorRefreshIT { } private V1Ingress getWiremockIngress() throws Exception { - V1Ingress ingress = (V1Ingress) k8SUtils - .readYamlFromClasspath("wiremock-ingress.yaml"); + V1Ingress ingress = (V1Ingress) k8SUtils.readYamlFromClasspath("wiremock-ingress.yaml"); return ingress; }