Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import io.kubernetes.client.openapi.apis.CoreV1Api;

import org.springframework.cloud.kubernetes.commons.config.NormalizedSource;
import org.springframework.cloud.kubernetes.commons.config.ReadType;
import org.springframework.core.env.Environment;

/**
Expand All @@ -27,10 +28,5 @@
* @author wind57
*/
public record KubernetesClientConfigContext(CoreV1Api client, NormalizedSource normalizedSource, String namespace,
Environment environment, boolean includeDefaultProfileData) {

public KubernetesClientConfigContext(CoreV1Api client, NormalizedSource normalizedSource, String namespace,
Environment environment) {
this(client, normalizedSource, namespace, environment, true);
}
Environment environment, boolean includeDefaultProfileData, ReadType readType) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ protected void registerBeans(ConfigDataLocationResolverContext resolverContext,
coreV1Api, configMapProperties, namespaceProvider);
if (isRetryEnabledForConfigMap(configMapProperties)) {
configMapPropertySourceLocator = new ConfigDataRetryableConfigMapPropertySourceLocator(
configMapPropertySourceLocator, configMapProperties, new KubernetesClientConfigMapsCache());
configMapPropertySourceLocator, configMapProperties);
}

registerSingle(bootstrapContext, ConfigMapPropertySourceLocator.class, configMapPropertySourceLocator,
Expand All @@ -76,7 +76,7 @@ protected void registerBeans(ConfigDataLocationResolverContext resolverContext,
coreV1Api, namespaceProvider, secretsProperties);
if (isRetryEnabledForSecrets(secretsProperties)) {
secretsPropertySourceLocator = new ConfigDataRetryableSecretsPropertySourceLocator(
secretsPropertySourceLocator, secretsProperties, new KubernetesClientSecretsCache());
secretsPropertySourceLocator, secretsProperties);
}

registerSingle(bootstrapContext, SecretsPropertySourceLocator.class, secretsPropertySourceLocator,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,15 @@
import org.springframework.cloud.kubernetes.commons.config.ConfigMapConfigProperties;
import org.springframework.cloud.kubernetes.commons.config.ConfigMapPropertySourceLocator;
import org.springframework.cloud.kubernetes.commons.config.NormalizedSource;
import org.springframework.cloud.kubernetes.commons.config.ReadType;
import org.springframework.core.annotation.Order;
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.client.KubernetesClientUtils.getApplicationNamespace;
import static org.springframework.cloud.kubernetes.client.config.KubernetesClientSourcesBatchRead.discardConfigMaps;

/**
* @author Ryan Baxter
Expand All @@ -41,19 +45,26 @@ public class KubernetesClientConfigMapPropertySourceLocator extends ConfigMapPro

public KubernetesClientConfigMapPropertySourceLocator(CoreV1Api coreV1Api, ConfigMapConfigProperties properties,
KubernetesNamespaceProvider kubernetesNamespaceProvider) {
super(properties, new KubernetesClientConfigMapsCache());
super(properties);
this.coreV1Api = coreV1Api;
this.kubernetesNamespaceProvider = kubernetesNamespaceProvider;
}

public PropertySource<?> locate(Environment environment) {
PropertySource<?> propertySource = super.locate(environment);
discardConfigMaps();
return propertySource;
}

@Override
protected MapPropertySource getMapPropertySource(NormalizedSource source, ConfigurableEnvironment environment) {
protected MapPropertySource getMapPropertySource(NormalizedSource source, ConfigurableEnvironment environment,
ReadType readType) {

String normalizedNamespace = source.namespace().orElse(null);
String namespace = getApplicationNamespace(normalizedNamespace, source.target(), kubernetesNamespaceProvider);

KubernetesClientConfigContext context = new KubernetesClientConfigContext(coreV1Api, source, namespace,
environment);
environment, true, readType);
return new KubernetesClientConfigMapPropertySource(context);
}

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -20,30 +20,37 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import io.kubernetes.client.openapi.ApiException;
import io.kubernetes.client.openapi.apis.CoreV1Api;
import org.apache.commons.logging.Log;
import io.kubernetes.client.openapi.models.V1ConfigMap;
import io.kubernetes.client.openapi.models.V1Secret;
import org.apache.commons.logging.LogFactory;

import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider;
import org.springframework.cloud.kubernetes.commons.config.ConfigUtils;
import org.springframework.cloud.kubernetes.commons.config.MultipleSourcesContainer;
import org.springframework.cloud.kubernetes.commons.config.ReadType;
import org.springframework.cloud.kubernetes.commons.config.StrippedSourceContainer;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties;
import org.springframework.core.env.Environment;
import org.springframework.core.log.LogAccessor;
import org.springframework.util.ObjectUtils;

import static org.springframework.cloud.kubernetes.client.KubernetesClientUtils.getApplicationNamespace;
import static org.springframework.cloud.kubernetes.client.config.KubernetesClientSourcesBatchRead.strippedConfigMaps;
import static org.springframework.cloud.kubernetes.client.config.KubernetesClientSourcesBatchRead.strippedSecrets;
import static org.springframework.cloud.kubernetes.client.config.KubernetesClientSourcesSingleRead.strippedConfigMaps;
import static org.springframework.cloud.kubernetes.client.config.KubernetesClientSourcesSingleRead.strippedSecrets;
import static org.springframework.cloud.kubernetes.commons.config.ConfigUtils.processLabeledData;
import static org.springframework.cloud.kubernetes.commons.config.ConfigUtils.processNamedData;

/**
* @author Ryan Baxter
*/
public final class KubernetesClientConfigUtils {

private static final Log LOG = LogFactory.getLog(KubernetesClientConfigUtils.class);

// k8s-native client already returns data from secrets as being decoded
// this flags makes sure we use it everywhere
private static final boolean DECODE = Boolean.FALSE;
private static final LogAccessor LOG = new LogAccessor(LogFactory.getLog(KubernetesClientConfigUtils.class));

private KubernetesClientConfigUtils() {
}
Expand All @@ -61,38 +68,32 @@ public static Set<String> namespaces(KubernetesNamespaceProvider provider, Confi
return namespaces;
}

/**
* <pre>
* 1. read all secrets in the provided namespace
* 2. from the above, filter the ones that we care about (filter by labels)
* 3. see if any of the secrets from (2) has a single yaml/properties file
* 4. gather all the names of the secrets + data they hold
* </pre>
*/
static MultipleSourcesContainer secretsDataByLabels(CoreV1Api coreV1Api, String namespace,
Map<String, String> labels, Environment environment) {
List<StrippedSourceContainer> strippedSecrets = strippedSecrets(coreV1Api, namespace);
if (strippedSecrets.isEmpty()) {
return MultipleSourcesContainer.empty();
}
return ConfigUtils.processLabeledData(strippedSecrets, environment, labels, namespace, DECODE);
}

/**
* <pre>
* 1. read all config maps in the provided namespace
* 2. from the above, filter the ones that we care about (filter by labels)
* 3. see if any from (2) has a single yaml/properties file
* 2. from the above, filter the ones that we care about (by name)
* 3. see if any of the config maps has a single yaml/properties file
* 4. gather all the names of the config maps + data they hold
* </pre>
*/
static MultipleSourcesContainer configMapsDataByLabels(CoreV1Api coreV1Api, String namespace,
Map<String, String> labels, Environment environment) {
List<StrippedSourceContainer> strippedConfigMaps = strippedConfigMaps(coreV1Api, namespace);
if (strippedConfigMaps.isEmpty()) {
return MultipleSourcesContainer.empty();
static MultipleSourcesContainer configMapsByName(CoreV1Api client, String namespace,
LinkedHashSet<String> sourceNames, Environment environment, boolean includeDefaultProfileData,
ReadType readType) {

List<StrippedSourceContainer> strippedConfigMaps;

if (readType.equals(ReadType.BATCH)) {
LOG.debug(() -> "Will read all configmaps in namespace : " + namespace);
strippedConfigMaps = strippedConfigMaps(client, namespace);
}
return ConfigUtils.processLabeledData(strippedConfigMaps, environment, labels, namespace, DECODE);
else {
LOG.debug(() -> "Will read individual configmaps in namespace : " + namespace + " with names : "
+ sourceNames);
strippedConfigMaps = strippedConfigMaps(client, namespace, sourceNames);
}

return processNamedData(strippedConfigMaps, environment, sourceNames, namespace, false,
includeDefaultProfileData);
}

/**
Expand All @@ -103,49 +104,100 @@ static MultipleSourcesContainer configMapsDataByLabels(CoreV1Api coreV1Api, Stri
* 4. gather all the names of the secrets + decoded data they hold
* </pre>
*/
static MultipleSourcesContainer secretsDataByName(CoreV1Api coreV1Api, String namespace,
LinkedHashSet<String> sourceNames, Environment environment, boolean includeDefaultProfileData) {
List<StrippedSourceContainer> strippedSecrets = strippedSecrets(coreV1Api, namespace);
if (strippedSecrets.isEmpty()) {
return MultipleSourcesContainer.empty();
static MultipleSourcesContainer secretsByName(CoreV1Api client, String namespace, LinkedHashSet<String> sourceNames,
Environment environment, boolean includeDefaultProfileData, ReadType readType) {

List<StrippedSourceContainer> strippedSecrets;

if (readType.equals(ReadType.BATCH)) {
LOG.debug(() -> "Will read all secrets in namespace : " + namespace);
strippedSecrets = strippedSecrets(client, namespace);
}
return ConfigUtils.processNamedData(strippedSecrets, environment, sourceNames, namespace, DECODE,
includeDefaultProfileData);
else {
LOG.debug(
() -> "Will read individual secrets in namespace : " + namespace + " with names : " + sourceNames);
strippedSecrets = strippedSecrets(client, namespace, sourceNames);
}

return processNamedData(strippedSecrets, environment, sourceNames, namespace, false, includeDefaultProfileData);
}

/**
* <pre>
* 1. read all config maps in the provided namespace
* 2. from the above, filter the ones that we care about (by name)
* 3. see if any of the config maps has a single yaml/properties file
* 2. from the above, filter the ones that we care about (filter by labels)
* 3. see if any from (2) has a single yaml/properties file
* 4. gather all the names of the config maps + data they hold
* </pre>
*/
static MultipleSourcesContainer configMapsDataByName(CoreV1Api coreV1Api, String namespace,
LinkedHashSet<String> sourceNames, Environment environment, boolean includeDefaultProfileData) {
List<StrippedSourceContainer> strippedConfigMaps = strippedConfigMaps(coreV1Api, namespace);
if (strippedConfigMaps.isEmpty()) {
return MultipleSourcesContainer.empty();
static MultipleSourcesContainer configMapsByLabels(CoreV1Api client, String namespace, Map<String, String> labels,
Environment environment, ReadType readType) {

List<StrippedSourceContainer> strippedConfigMaps;

if (readType.equals(ReadType.BATCH)) {
LOG.debug(() -> "Will read all configmaps in namespace : " + namespace);
strippedConfigMaps = strippedConfigMaps(client, namespace);
}
return ConfigUtils.processNamedData(strippedConfigMaps, environment, sourceNames, namespace, DECODE,
includeDefaultProfileData);
else {
LOG.debug(() -> "Will read individual configmaps in namespace : " + namespace + " with labels : " + labels);
strippedConfigMaps = strippedConfigMaps(client, namespace, labels);
}

return processLabeledData(strippedConfigMaps, environment, labels, namespace, false);
}

private static List<StrippedSourceContainer> strippedConfigMaps(CoreV1Api coreV1Api, String namespace) {
List<StrippedSourceContainer> strippedConfigMaps = KubernetesClientConfigMapsCache.byNamespace(coreV1Api,
namespace);
if (strippedConfigMaps.isEmpty()) {
LOG.debug("No configmaps in namespace '" + namespace + "'");
/**
* <pre>
* 1. read all secrets in the provided namespace
* 2. from the above, filter the ones that we care about (filter by labels)
* 3. see if any of the secrets from (2) has a single yaml/properties file
* 4. gather all the names of the secrets + data they hold
* </pre>
*/
static MultipleSourcesContainer secretsByLabels(CoreV1Api client, String namespace, Map<String, String> labels,
Environment environment, ReadType readType) {

List<StrippedSourceContainer> strippedSecrets;

if (readType.equals(ReadType.BATCH)) {
LOG.debug(() -> "Will read all secrets in namespace : " + namespace);
strippedSecrets = strippedSecrets(client, namespace);
}
return strippedConfigMaps;
else {
LOG.debug(() -> "Will read individual secrets in namespace : " + namespace + " with labels : " + labels);
strippedSecrets = strippedSecrets(client, namespace, labels);
}

return processLabeledData(strippedSecrets, environment, labels, namespace, false);
}

private static List<StrippedSourceContainer> strippedSecrets(CoreV1Api coreV1Api, String namespace) {
List<StrippedSourceContainer> strippedSecrets = KubernetesClientSecretsCache.byNamespace(coreV1Api, namespace);
if (strippedSecrets.isEmpty()) {
LOG.debug("No secrets in namespace '" + namespace + "'");
static List<StrippedSourceContainer> stripSecrets(List<V1Secret> secrets) {
return secrets.stream()
.map(secret -> new StrippedSourceContainer(secret.getMetadata().getLabels(), secret.getMetadata().getName(),
transform(secret.getData())))
.toList();
}

static List<StrippedSourceContainer> stripConfigMaps(List<V1ConfigMap> configMaps) {
return configMaps.stream()
.map(configMap -> new StrippedSourceContainer(configMap.getMetadata().getLabels(),
configMap.getMetadata().getName(), configMap.getData()))
.toList();
}

static void handleApiException(ApiException e, String sourceName) {
if (e.getCode() == 404) {
LOG.warn(() -> "source with name : " + sourceName + " not found. Ignoring");
}
return strippedSecrets;
else {
throw new RuntimeException(e.getResponseBody(), e);
}
}

private static Map<String, String> transform(Map<String, byte[]> in) {
return ObjectUtils.isEmpty(in) ? Map.of()
: in.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, en -> new String(en.getValue())));
}

}
Loading
Loading