From 4194a2230474c6837b93f0690a80337da59abb06 Mon Sep 17 00:00:00 2001 From: erabii Date: Tue, 2 Apr 2024 22:17:41 +0300 Subject: [PATCH 1/4] Add log statement (#1620) --- .../cloud/kubernetes/client/KubernetesClientPodUtils.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/spring-cloud-kubernetes-client-autoconfig/src/main/java/org/springframework/cloud/kubernetes/client/KubernetesClientPodUtils.java b/spring-cloud-kubernetes-client-autoconfig/src/main/java/org/springframework/cloud/kubernetes/client/KubernetesClientPodUtils.java index a87fd6140..c1edcf87b 100644 --- a/spring-cloud-kubernetes-client-autoconfig/src/main/java/org/springframework/cloud/kubernetes/client/KubernetesClientPodUtils.java +++ b/spring-cloud-kubernetes-client-autoconfig/src/main/java/org/springframework/cloud/kubernetes/client/KubernetesClientPodUtils.java @@ -109,6 +109,9 @@ private V1Pod internalGetPod() { } catch (Throwable t) { if (failFast) { + if (t instanceof ApiException apiException) { + LOG.error("error reading pod : " + apiException.getResponseBody()); + } throw new RuntimeException(t); } if (t instanceof ApiException apiException) { From b6300eb4273fff4fb0ce155386a1394921e53bb9 Mon Sep 17 00:00:00 2001 From: erabii Date: Tue, 2 Apr 2024 23:11:08 +0300 Subject: [PATCH 2/4] Some cleanup for config server 3 (#1617) --- .../commons/config/ConfigUtils.java | 60 ++- .../config/ConfigUtilsProcessSourceTests.java | 481 ++++++++++++++++++ ...awDataContainsProfileBasedSourceTests.java | 95 ++++ 3 files changed, 618 insertions(+), 18 deletions(-) create mode 100644 spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/ConfigUtilsProcessSourceTests.java create mode 100644 spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/ConfigUtilsRawDataContainsProfileBasedSourceTests.java diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigUtils.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigUtils.java index 70f6b1ce9..9efc8ba77 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigUtils.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigUtils.java @@ -23,7 +23,10 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; +import java.util.function.BiPredicate; +import java.util.function.BooleanSupplier; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -51,6 +54,12 @@ public final class ConfigUtils { private static final Log LOG = LogFactory.getLog(ConfigUtils.class); + // sourceName (configmap or secret name) ends with : "-dev.yaml" or the like. + private static final BiPredicate ENDS_WITH_PROFILE_AND_EXTENSION = (sourceName, + activeProfile) -> sourceName.endsWith("-" + activeProfile + ".yml") + || sourceName.endsWith("-" + activeProfile + ".yaml") + || sourceName.endsWith("-" + activeProfile + ".properties"); + private ConfigUtils() { } @@ -205,19 +214,13 @@ public static MultipleSourcesContainer processNamedData(List sourceName.endsWith("-" + activeProfile) - || "default".equals(activeProfile)); - if (includeDefaultProfileData || containsActiveProfile - || containsDataWithProfile(rawData, environment.getActiveProfiles())) { + /* + * In some cases we want to include properties from the default profile + * along with any active profiles In these cases includeDefaultProfileData + * will be true If includeDefaultProfileData is false then we want to make + * sure that we only return properties from any active profiles + */ + if (processSource(includeDefaultProfileData, environment, sourceName, rawData)) { data.putAll(SourceDataEntriesProcessor.processAllEntries(rawData == null ? Map.of() : rawData, environment, includeDefaultProfileData)); } @@ -227,13 +230,34 @@ public static MultipleSourcesContainer processNamedData(List sourceRawData) { + List activeProfiles = Arrays.stream(environment.getActiveProfiles()).toList(); + + boolean emptyActiveProfiles = activeProfiles.isEmpty(); + + boolean profileBasedSourceName = activeProfiles.stream() + .anyMatch(activeProfile -> sourceName.endsWith("-" + activeProfile)); + + boolean defaultProfilePresent = activeProfiles.contains("default"); + + return includeDefaultProfileData || emptyActiveProfiles || profileBasedSourceName || defaultProfilePresent + || rawDataContainsProfileBasedSource(activeProfiles, sourceRawData).getAsBoolean(); + } + /* - * In the case there the data contains yaml or properties files we need to check their - * names to see if they contain any active profiles. + * this one is not inlined into 'processSource' because other filters, that come + * before it, might have already resolved to 'true', so no need to compute it at all. + * + * This method is supposed to answer the question if raw data that a certain source + * (configmap or secret) has entries that are themselves profile based + * yaml/yml/properties. For example: 'account-k8s.yaml' or the like. */ - private static boolean containsDataWithProfile(Map rawData, String[] activeProfiles) { - return rawData.keySet().stream().anyMatch(key -> Arrays.stream(activeProfiles) - .anyMatch(activeProfile -> key.contains("-" + activeProfile) || "default".equals(activeProfile))); + static BooleanSupplier rawDataContainsProfileBasedSource(List activeProfiles, + Map sourceRawData) { + return () -> Optional.ofNullable(sourceRawData).orElse(Map.of()).keySet().stream() + .anyMatch(keyName -> activeProfiles.stream() + .anyMatch(activeProfile -> ENDS_WITH_PROFILE_AND_EXTENSION.test(keyName, activeProfile))); } /** diff --git a/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/ConfigUtilsProcessSourceTests.java b/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/ConfigUtilsProcessSourceTests.java new file mode 100644 index 000000000..90a14ec7d --- /dev/null +++ b/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/ConfigUtilsProcessSourceTests.java @@ -0,0 +1,481 @@ +/* + * Copyright 2013-2024 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.LinkedHashSet; +import java.util.List; +import java.util.Map; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import org.springframework.boot.test.system.CapturedOutput; +import org.springframework.boot.test.system.OutputCaptureExtension; +import org.springframework.core.env.Environment; +import org.springframework.mock.env.MockEnvironment; + +/** + * Class that is supposed to test only ConfigUtils::processSource and + * ConfigUtils::processNamedData. + * + * @author wind57 + */ +@ExtendWith(OutputCaptureExtension.class) +class ConfigUtilsProcessSourceTests { + + /** + *
+	 *		- includeDefaultProfileData         = true
+	 *		- emptyActiveProfiles               = does not matter
+	 *		- profileBasedSourceName            = does not matter
+	 * 		- defaultProfilePresent             = does not matter
+	 * 		- rawDataContainsProfileBasedSource = does not matter
+	 * 
+ * + * Since 'includeDefaultProfileData=true', all other arguments are irrelevant and + * method must return 'true'. + */ + @Test + void testProcessSourceOne() { + boolean includeDefaultProfileData = true; + Environment environment = new MockEnvironment(); + String sourceName = "account"; + Map sourceRawData = Map.of(); + + boolean result = ConfigUtils.processSource(includeDefaultProfileData, environment, sourceName, sourceRawData); + Assertions.assertTrue(result); + } + + /** + * this case is not very "interesting" because 'includeDefaultProfileData = true' + * which denotes a request not from config server; and such cases are tested in + * various other tests, before we fixed: + * https://github.com/spring-cloud/spring-cloud-kubernetes/pull/1600 + */ + @Test + void testProcessNamedDataOne() { + List strippedSources = List + .of(new StrippedSourceContainer(Map.of(), "configmap-a", Map.of("one", "1"))); + Environment environment = new MockEnvironment(); + LinkedHashSet sourceNames = new LinkedHashSet<>(List.of("configmap-a")); + String namespace = "namespace-a"; + boolean decode = false; + boolean includeDefaultProfileData = true; + + MultipleSourcesContainer result = ConfigUtils.processNamedData(strippedSources, environment, sourceNames, + namespace, decode, includeDefaultProfileData); + Assertions.assertNotNull(result); + Assertions.assertEquals(result.names().toString(), "[configmap-a]"); + Assertions.assertEquals(result.data(), Map.of("one", "1")); + } + + /** + *
+	 *		- includeDefaultProfileData         = false
+	 * 		- emptyActiveProfiles               = false
+	 *		- profileBasedSourceName            = false
+	 * 		- defaultProfilePresent             = true
+	 * 		- rawDataContainsProfileBasedSource = does not matter
+	 * 
+ * + * Since 'defaultProfilePresent=true', this method must return 'true'. + */ + @Test + void testProcessSourceTwo() { + boolean includeDefaultProfileData = false; + MockEnvironment environment = new MockEnvironment(); + environment.setActiveProfiles("default"); + String sourceName = "account"; + Map sourceRawData = Map.of(); + + boolean result = ConfigUtils.processSource(includeDefaultProfileData, environment, sourceName, sourceRawData); + Assertions.assertTrue(result); + } + + /** + *
+	 *		- request is coming from config server
+	 *		- activeProfile = ['default']
+	 *		- sourceName = 'account'
+	 * 
+ * + * As such the above will generate: + * + *
+	 *     - includeDefaultProfileData         = false
+	 * 	   - emptyActiveProfiles               = false
+	 * 	   - profileBasedSourceName            = false
+	 * 	   - defaultProfilePresent             = true
+	 * 	   - rawDataContainsProfileBasedSource = does not matter
+	 * 
+ * + * In this case, three types of properties will be read from the source: + * + *
+	 *     - all simple properties
+	 *     - all nested ones (yaml/yml/properties themselves) that match "${SOURCE_NAME}.{EXTENSION}"
+	 *       (in our case 'account.properties')
+	 *     - all nested ones (yaml/yml/properties themselves) that match "${SOURCE_NAME}-${ACTIVE_PROFILE}.{EXTENSION}"
+	 *       (in our case 'account-default.properties')
+	 *     - there are strict sorting rules if both of the above are matched
+	 * 
+ */ + @Test + void testProcessNamedDataTwo(CapturedOutput output) { + // @formatter:off + Map sourceRawData = Map.of( + "one", "1", + "two", "2", + "account-default.properties", "five=5", + "account.properties", "one=11\nthree=3", + "account-k8s.properties", "one=22\nfour=4" + ); + // @formatter:on + String sourceName = "account"; + List strippedSources = List + .of(new StrippedSourceContainer(Map.of(), sourceName, sourceRawData)); + MockEnvironment environment = new MockEnvironment().withProperty("spring.application.name", sourceName); + environment.setActiveProfiles("default"); + LinkedHashSet sourceNames = new LinkedHashSet<>(List.of(sourceName)); + String namespace = "namespace-a"; + boolean decode = false; + boolean includeDefaultProfileData = false; + + MultipleSourcesContainer result = ConfigUtils.processNamedData(strippedSources, environment, sourceNames, + namespace, decode, includeDefaultProfileData); + Assertions.assertNotNull(result); + Assertions.assertEquals(result.names().toString(), "[account]"); + + /** + *
+		 * there are some things to see here:
+		 *
+		 * 		1. 'three=3' is present in the result, which means we have read 'account.properties'
+		 *		2. 'five=5' is present in the result, which means we have read 'account-default.properties'
+		 *		3. even if we have read 'account.properties', we have 'one=1' (and not 'one=11'),
+		 *			since simple properties override the ones from yml/yaml/properties
+		 *		4. 'four=4' is not present in the result, because we do not read 'account-k8s.properties',
+		 *			since 'k8s' is not an active profile.
+		 * 
+ */ + Assertions.assertEquals(result.data(), Map.of("one", "1", "two", "2", "three", "3", "five", "5")); + Assertions.assertTrue(output.getOut().contains("entry : account-k8s.properties will be skipped")); + } + + /** + *
+	 *		- includeDefaultProfileData         = false
+	 * 		- emptyActiveProfiles               = false
+	 * 		- profileBasedSourceName            = true
+	 * 		- defaultProfilePresent             = false
+	 * 		- rawDataContainsProfileBasedSource = does not matter
+	 * 
+ * + * Since 'profileBasedSourceName=true', this method must return 'true'. + */ + @Test + void testProcessSourceThree() { + boolean includeDefaultProfileData = false; + MockEnvironment environment = new MockEnvironment(); + environment.setActiveProfiles("default"); + String sourceName = "account-default"; + Map sourceRawData = Map.of(); + + boolean result = ConfigUtils.processSource(includeDefaultProfileData, environment, sourceName, sourceRawData); + Assertions.assertTrue(result); + } + + /** + *
+	 *		- request is coming from config server
+	 *		- activeProfile = ['default']
+	 *		- sourceName = 'account-default'
+	 * 
+ * + * As such the above will generate: + * + *
+	 *     - includeDefaultProfileData         = false
+	 * 	   - emptyActiveProfiles               = false
+	 * 	   - profileBasedSourceName            = true
+	 * 	   - defaultProfilePresent             = does not matter
+	 * 	   - rawDataContainsProfileBasedSource = does not matter
+	 * 
+ * + * In this case, three types of properties will be read from the source: + * + *
+	 *     - all simple properties
+	 *     - all nested ones (yaml/yml/properties themselves) that match "${SOURCE_NAME}.{EXTENSION}"
+	 *       (in our case 'account.properties')
+	 *     - all nested ones (yaml/yml/properties themselves) that match "${SOURCE_NAME}-${ACTIVE_PROFILE}.{EXTENSION}"
+	 *       (in our case 'account-default.properties')
+	 *     - there are strict sorting rules if both of the above are matched
+	 * 
+ */ + @Test + void testProcessNamedDataThree(CapturedOutput output) { + // @formatter:off + Map sourceRawData = Map.of( + "one", "1", + "two", "2", + "account.properties", "one=11\nthree=3", + "account-default.properties", "one=111\nfive=5", + "account-k8s.properties", "one=22\nfour=4" + ); + // @formatter:on + String sourceName = "account-default"; + List strippedSources = List + .of(new StrippedSourceContainer(Map.of(), sourceName, sourceRawData)); + MockEnvironment environment = new MockEnvironment().withProperty("spring.application.name", "account"); + environment.setActiveProfiles("default"); + LinkedHashSet sourceNames = new LinkedHashSet<>(List.of(sourceName)); + String namespace = "namespace-a"; + boolean decode = false; + boolean includeDefaultProfileData = false; + + MultipleSourcesContainer result = ConfigUtils.processNamedData(strippedSources, environment, sourceNames, + namespace, decode, includeDefaultProfileData); + Assertions.assertNotNull(result); + Assertions.assertEquals(result.names().toString(), "[account-default]"); + + /** + *
+		 * there are some things to see here:
+		 *
+		 *		1. 'one=1' is present in the result, which means we have read simple properties.
+		 *		2. 'two-2' is present in the result, which means we have read simple properties.
+		 *		3. even if we have read 'account.properties', we have 'one=1' (and not 'one=11'),
+		 *			since simple properties override the ones from yml/yaml/properties
+		 *		4. 'three=3' is present in the result, which means we have read 'account.properties'.
+		 *		5. 'five=5' is present in the result, which means we have read 'account-default.properties'
+		 *			(but we don't have 'one=111', instead : 'one=1')
+		 *		6. we do not have 'four=4' since we do not read 'account-k8s.properties'
+		 * 
+ */ + Assertions.assertEquals(result.data(), Map.of("one", "1", "two", "2", "three", "3", "five", "5")); + Assertions.assertTrue(output.getOut().contains("entry : account-k8s.properties will be skipped")); + + } + + /** + *
+	 *		- includeDefaultProfileData         = false
+	 * 		- emptyActiveProfiles               = false
+	 *		- profileBasedSourceName            = false
+	 *		- defaultProfilePresent             = false
+	 *		- rawDataContainsProfileBasedSource = false
+	 * 
+ * + */ + @Test + void testProcessSourceFour() { + boolean includeDefaultProfileData = false; + MockEnvironment environment = new MockEnvironment(); + environment.setActiveProfiles("k8s"); + String sourceName = "account"; + Map sourceRawData = Map.of("one", "1"); + + boolean result = ConfigUtils.processSource(includeDefaultProfileData, environment, sourceName, sourceRawData); + Assertions.assertFalse(result); + } + + /** + *
+	 *		- includeDefaultProfileData         = false
+	 * 		- emptyActiveProfiles               = false
+	 * 		- profileBasedSourceName            = false
+	 * 		- defaultProfilePresent             = false
+	 * 		- rawDataContainsProfileBasedSource = true
+	 * 
+ * + */ + @Test + void testProcessSourceFive() { + boolean includeDefaultProfileData = false; + MockEnvironment environment = new MockEnvironment(); + environment.setActiveProfiles("k8s"); + String sourceName = "account"; + Map sourceRawData = Map.of("one", "1", "account-k8s.properties", "one=11"); + + boolean result = ConfigUtils.processSource(includeDefaultProfileData, environment, sourceName, sourceRawData); + Assertions.assertTrue(result); + } + + /** + *
+	 *		- request is coming from config server
+	 *		- activeProfile = ['k8s']
+	 *		- sourceName = 'account'
+	 *	    - rawData inside the source has an entry : 'account-k8s.properties'
+	 * 
+ * + * As such the above will generate: + * + *
+	 *     - includeDefaultProfileData         = false
+	 * 	   - emptyActiveProfiles               = false
+	 * 	   - profileBasedSourceName            = false
+	 * 	   - defaultProfilePresent             = false
+	 * 	   - rawDataContainsProfileBasedSource = true
+	 * 
+ * + * In this case, only one type of source data will be read + * + *
+	 *     - "${SOURCE_NAME}-${ACTIVE_PROFILE}.{EXTENSION}"
+	 * 
+ */ + @Test + void testProcessNamedDataFive(CapturedOutput output) { + // @formatter:off + Map sourceRawData = Map.of( + "one", "1", + "account.properties", "one=11\ntwo=2", + "account-default.properties", "one=111\nthree=3", + "account-k8s.properties", "one=1111\nfour=4" + ); + // @formatter:on + String sourceName = "account"; + List strippedSources = List + .of(new StrippedSourceContainer(Map.of(), sourceName, sourceRawData)); + MockEnvironment environment = new MockEnvironment().withProperty("spring.application.name", sourceName); + environment.setActiveProfiles("k8s"); + LinkedHashSet sourceNames = new LinkedHashSet<>(List.of(sourceName)); + String namespace = "namespace-a"; + boolean decode = false; + boolean includeDefaultProfileData = false; + + MultipleSourcesContainer result = ConfigUtils.processNamedData(strippedSources, environment, sourceNames, + namespace, decode, includeDefaultProfileData); + Assertions.assertNotNull(result); + Assertions.assertEquals(result.names().toString(), "[account]"); + + /** + *
+		 * 		- we only read from 'account-k8s.properties'
+		 * 
+ */ + Assertions.assertEquals(result.data(), Map.of("one", "1111", "four", "4")); + Assertions.assertTrue(output.getOut().contains("entry : account.properties will be skipped")); + Assertions.assertTrue(output.getOut().contains("entry : account-default.properties will be skipped")); + + } + + /** + *
+	 *		- includeDefaultProfileData         = false
+	 *		- emptyActiveProfiles               = false
+	 *		- profileBasedSourceName            = true
+	 * 		- defaultProfilePresent             = does not matter
+	 * 		- rawDataContainsProfileBasedSource = does not matter
+	 * 
+ * + */ + @Test + void testProcessSourceSix() { + boolean includeDefaultProfileData = false; + MockEnvironment environment = new MockEnvironment(); + environment.setActiveProfiles("k8s"); + String sourceName = "account-k8s"; + Map sourceRawData = Map.of("one", "1", "account-k8s.properties", "one=11"); + + boolean result = ConfigUtils.processSource(includeDefaultProfileData, environment, sourceName, sourceRawData); + Assertions.assertTrue(result); + } + + /** + *
+	 *		- request is coming from config server
+	 *		- activeProfile = ['k8s']
+	 *		- sourceName = 'account-k8s'
+	 * 
+ * + * As such the above will generate: + * + *
+	 *     - includeDefaultProfileData         = false
+	 * 	   - emptyActiveProfiles               = false
+	 * 	   - profileBasedSourceName            = true
+	 * 	   - defaultProfilePresent             = does not matter
+	 * 	   - rawDataContainsProfileBasedSource = does not matter
+	 * 
+ * + * In this case, a few types of data will be read: + * + *
+	 *     - all simple properties
+	 *     - all nested ones (yaml/yml/properties themselves) that match "${SOURCE_NAME}.{EXTENSION}"
+	 *       (in our case 'account.properties')
+	 *     - all nested ones (yaml/yml/properties themselves) that match "${SOURCE_NAME}-${ACTIVE_PROFILE}.{EXTENSION}"
+	 *       (in our case 'account-k8s.properties')
+	 *     - there are strict sorting rules if both of the above are matched
+	 * 
+ */ + @Test + void testProcessNamedDataSix(CapturedOutput output) { + // @formatter:off + Map sourceRawData = Map.of( + "one", "1", + "two", "2", + "account.properties", "one=11\nthree=3", + "account-default.properties", "one=111\nfour=4", + "account-k8s.properties", "one=1111\nfive=5", + "account-prod.properties", "six=6" + ); + // @formatter:on + String sourceName = "account-k8s"; + List strippedSources = List + .of(new StrippedSourceContainer(Map.of(), sourceName, sourceRawData)); + MockEnvironment environment = new MockEnvironment().withProperty("spring.application.name", "account"); + environment.setActiveProfiles("k8s"); + LinkedHashSet sourceNames = new LinkedHashSet<>(List.of(sourceName)); + String namespace = "namespace-a"; + boolean decode = false; + boolean includeDefaultProfileData = false; + + MultipleSourcesContainer result = ConfigUtils.processNamedData(strippedSources, environment, sourceNames, + namespace, decode, includeDefaultProfileData); + Assertions.assertNotNull(result); + Assertions.assertEquals(result.names().toString(), "[account-k8s]"); + + /** + *
+		 * there are some things to see here:
+		 *
+		 * 		1. 'one=1' is not present in the result, we are not supposed to read simple properties.
+		 *		2. 'two-2' is not present in the result, we are not supposed to read simple properties.
+		 *		3. even if we have read 'account.properties', we have 'one=1' (and not 'one=11'),
+		 *			since simple properties override the ones from yml/yaml/properties
+		 *		4. 'three=3' is not present in the result, we are not supposed to read '${SPRING>APPLICATION.NAME}'
+		 *			properties.
+		 *		5. 'four=4' is not present in the result, which means we have not read 'account-default.properties'
+		 *			(but we don't have 'one=111', instead : 'one=1')
+		 *		6. we do not have 'three=3' since we do not read 'account-default.properties'
+		 *		7. we do not have 'six=6' since we do not read 'account-prod.properties'
+		 *			(because 'prod' is not an active profile)
+		 * 
+ */ + Assertions.assertEquals(result.data(), Map.of("one", "1111", "five", "5")); + Assertions.assertTrue(output.getOut().contains("entry : account-prod.properties will be skipped")); + Assertions.assertTrue(output.getOut().contains("entry : account.properties will be skipped")); + Assertions.assertTrue(output.getOut().contains("entry : account-default.properties will be skipped")); + + } + +} diff --git a/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/ConfigUtilsRawDataContainsProfileBasedSourceTests.java b/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/ConfigUtilsRawDataContainsProfileBasedSourceTests.java new file mode 100644 index 000000000..4458864b0 --- /dev/null +++ b/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/ConfigUtilsRawDataContainsProfileBasedSourceTests.java @@ -0,0 +1,95 @@ +/* + * Copyright 2013-2024 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.List; +import java.util.Map; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +/** + * Class that is supposed to test only ConfigUtils::rawDataContainsProfileBasedSource + * + * @author wind57 + */ +class ConfigUtilsRawDataContainsProfileBasedSourceTests { + + @Test + void nullSourceRawData() { + List activeProfiles = List.of(); + Map rawData = null; + + boolean result = ConfigUtils.rawDataContainsProfileBasedSource(activeProfiles, rawData).getAsBoolean(); + Assertions.assertFalse(result); + } + + @Test + void sourceRawDataPresentEmptyActiveProfiles() { + List activeProfiles = List.of(); + Map rawData = Map.of("account-k8s.yaml", "value"); + + boolean result = ConfigUtils.rawDataContainsProfileBasedSource(activeProfiles, rawData).getAsBoolean(); + Assertions.assertFalse(result); + } + + @Test + void plainValuesOnly() { + List activeProfiles = List.of("k8s"); + Map rawData = Map.of("account", "value"); + + boolean result = ConfigUtils.rawDataContainsProfileBasedSource(activeProfiles, rawData).getAsBoolean(); + Assertions.assertFalse(result); + } + + @Test + void noMatchInActiveProfiles() { + List activeProfiles = List.of("k8s"); + Map rawData = Map.of("account-dev.yml", "value"); + + boolean result = ConfigUtils.rawDataContainsProfileBasedSource(activeProfiles, rawData).getAsBoolean(); + Assertions.assertFalse(result); + } + + @Test + void matchInActiveProfilesWithYml() { + List activeProfiles = List.of("dev"); + Map rawData = Map.of("account-dev.yml", "value"); + + boolean result = ConfigUtils.rawDataContainsProfileBasedSource(activeProfiles, rawData).getAsBoolean(); + Assertions.assertTrue(result); + } + + @Test + void matchInActiveProfilesWithYaml() { + List activeProfiles = List.of("dev", "k8s"); + Map rawData = Map.of("account-dev.yaml", "value"); + + boolean result = ConfigUtils.rawDataContainsProfileBasedSource(activeProfiles, rawData).getAsBoolean(); + Assertions.assertTrue(result); + } + + @Test + void matchInActiveProfilesWithProperties() { + List activeProfiles = List.of("dev", "k8s"); + Map rawData = Map.of("account-dev.properties", "value"); + + boolean result = ConfigUtils.rawDataContainsProfileBasedSource(activeProfiles, rawData).getAsBoolean(); + Assertions.assertTrue(result); + } + +} From 7803d3e7b4ae212e82406ead801ccb85f3ffe7ad Mon Sep 17 00:00:00 2001 From: erabii Date: Tue, 2 Apr 2024 23:54:40 +0300 Subject: [PATCH 3/4] Some cleanup for config server 4 (#1618) --- .../config/SourceDataEntriesProcessor.java | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SourceDataEntriesProcessor.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SourceDataEntriesProcessor.java index a73c8508f..a52c362a6 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SourceDataEntriesProcessor.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SourceDataEntriesProcessor.java @@ -24,6 +24,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Predicate; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -47,6 +48,9 @@ public class SourceDataEntriesProcessor extends MapPropertySource { private static final Log LOG = LogFactory.getLog(SourceDataEntriesProcessor.class); + private static Predicate ENDS_IN_EXTENSION = x -> x.endsWith(".yml") || + x.endsWith(".yaml") || x.endsWith(".properties"); + public SourceDataEntriesProcessor(SourceData sourceData) { super(sourceData.sourceName(), sourceData.sourceData()); } @@ -92,7 +96,7 @@ static List> sorted(Map input, Environ * 3. then plain properties * */ - static List> sorted(Map input, Environment environment, + static List> sorted(Map rawData, Environment environment, boolean includeDefaultProfileData) { record WeightedEntry(Map.Entry entry, int weight) { @@ -124,16 +128,17 @@ record WeightedEntry(Map.Entry entry, int weight) { int current = orderedFileNames.size() - 1; List weightedEntries = new ArrayList<>(); - if (input.entrySet().stream().noneMatch(entry -> entry.getKey().endsWith(".yml") - || entry.getKey().endsWith(".yaml") || entry.getKey().endsWith(".properties"))) { - for (Map.Entry entry : input.entrySet()) { + boolean plainEntriesOnly = rawData.keySet().stream().noneMatch(ENDS_IN_EXTENSION); + + if (plainEntriesOnly) { + for (Map.Entry entry : rawData.entrySet()) { weightedEntries.add(new WeightedEntry(entry, ++current)); } } else { - for (Map.Entry entry : input.entrySet()) { + for (Map.Entry entry : rawData.entrySet()) { String key = entry.getKey(); - if (key.endsWith(".yml") || key.endsWith(".yaml") || key.endsWith(".properties")) { + if (ENDS_IN_EXTENSION.test(key)) { String withoutExtension = key.split("\\.", 2)[0]; int index = orderedFileNames.indexOf(withoutExtension); if (index >= 0) { From 9ff722d3c8b3833c103c48afce0b4bdf70fa4400 Mon Sep 17 00:00:00 2001 From: buildmaster Date: Wed, 3 Apr 2024 01:54:53 +0000 Subject: [PATCH 4/4] Bumping versions --- .../kubernetes/commons/config/SourceDataEntriesProcessor.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SourceDataEntriesProcessor.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SourceDataEntriesProcessor.java index a52c362a6..d54cec876 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SourceDataEntriesProcessor.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SourceDataEntriesProcessor.java @@ -48,8 +48,8 @@ public class SourceDataEntriesProcessor extends MapPropertySource { private static final Log LOG = LogFactory.getLog(SourceDataEntriesProcessor.class); - private static Predicate ENDS_IN_EXTENSION = x -> x.endsWith(".yml") || - x.endsWith(".yaml") || x.endsWith(".properties"); + private static Predicate ENDS_IN_EXTENSION = x -> x.endsWith(".yml") || x.endsWith(".yaml") + || x.endsWith(".properties"); public SourceDataEntriesProcessor(SourceData sourceData) { super(sourceData.sourceName(), sourceData.sourceData());