Skip to content

Commit 25b3e33

Browse files
committed
feat(springboot cloudconfig): A new library that implement a backend of Spring Cloud Config
Originally from https://github.com/fangkehou-team/dapr-spring, this library created a backend of SpringCloudConfig just like SpringCloudVault. The original library only uses secret store as config store api is not stable at that time. As the configuration api is stable now, the config loader using that api would be implemented later. Signed-off-by: lony2003 <[email protected]>
1 parent 3b1c131 commit 25b3e33

16 files changed

+1018
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
<parent>
7+
<groupId>io.dapr.spring</groupId>
8+
<artifactId>dapr-spring-parent</artifactId>
9+
<version>0.14.0-SNAPSHOT</version>
10+
</parent>
11+
12+
<artifactId>dapr-spring-cloudconfig</artifactId>
13+
<name>dapr-spring-cloudconfig</name>
14+
<description>Dapr Spring Cloud Config</description>
15+
<packaging>jar</packaging>
16+
17+
<dependencies>
18+
<dependency>
19+
<groupId>io.dapr.spring</groupId>
20+
<artifactId>dapr-spring-boot-autoconfigure</artifactId>
21+
<version>${project.parent.version}</version>
22+
<scope>compile</scope>
23+
</dependency>
24+
</dependencies>
25+
26+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright (c) 2016-2024 Team Fangkehou
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.dapr.spring.boot.cloudconfig.autoconfigure;
18+
19+
import io.dapr.spring.boot.cloudconfig.config.DaprCloudConfigProperties;
20+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
21+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
22+
import org.springframework.boot.context.properties.EnableConfigurationProperties;
23+
import org.springframework.context.annotation.Configuration;
24+
25+
import io.dapr.client.DaprClient;
26+
27+
@Configuration(proxyBeanMethods = false)
28+
@EnableConfigurationProperties(DaprCloudConfigProperties.class)
29+
@ConditionalOnProperty(name = "dapr.secretstore.enabled", matchIfMissing = true)
30+
@ConditionalOnClass(DaprClient.class)
31+
public class DaprCloudConfigAutoConfiguration {
32+
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright 2024 The Dapr Authors
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
* Unless required by applicable law or agreed to in writing, software
8+
* distributed under the License is distributed on an "AS IS" BASIS,
9+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
* See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
14+
package io.dapr.spring.boot.cloudconfig.config;
15+
16+
import io.dapr.spring.boot.autoconfigure.client.DaprClientProperties;
17+
import io.dapr.spring.boot.autoconfigure.client.DaprConnectionDetails;
18+
19+
class CloudConfigPropertiesDaprConnectionDetails implements DaprConnectionDetails {
20+
21+
private final DaprClientProperties daprClientProperties;
22+
23+
public CloudConfigPropertiesDaprConnectionDetails(DaprClientProperties daprClientProperties) {
24+
this.daprClientProperties = daprClientProperties;
25+
}
26+
27+
@Override
28+
public String httpEndpoint() {
29+
return this.daprClientProperties.getHttpEndpoint();
30+
}
31+
32+
@Override
33+
public String grpcEndpoint() {
34+
return this.daprClientProperties.getGrpcEndpoint();
35+
}
36+
37+
@Override
38+
public Integer httpPort() {
39+
return this.daprClientProperties.getHttpPort();
40+
}
41+
42+
@Override
43+
public Integer grpcPort() {
44+
return this.daprClientProperties.getGrpcPort();
45+
}
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* Copyright (c) 2016-2024 Team Fangkehou
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.dapr.spring.boot.cloudconfig.config;
18+
19+
import io.dapr.client.DaprClient;
20+
import io.dapr.client.DaprClientBuilder;
21+
import io.dapr.client.DaprPreviewClient;
22+
import io.dapr.config.Properties;
23+
import io.dapr.spring.boot.autoconfigure.client.DaprClientProperties;
24+
import io.dapr.spring.boot.autoconfigure.client.DaprConnectionDetails;
25+
26+
27+
public class DaprCloudConfigClientManager {
28+
29+
private static DaprClient daprClient;
30+
private static DaprPreviewClient daprPreviewClient;
31+
private final DaprCloudConfigProperties daprCloudConfigProperties;
32+
private final DaprClientProperties daprClientConfig;
33+
34+
public DaprCloudConfigClientManager(DaprCloudConfigProperties daprCloudConfigProperties,
35+
DaprClientProperties daprClientConfig) {
36+
this.daprCloudConfigProperties = daprCloudConfigProperties;
37+
this.daprClientConfig = daprClientConfig;
38+
39+
DaprClientBuilder daprClientBuilder = createDaprClientBuilder(
40+
createDaprConnectionDetails(daprClientConfig)
41+
);
42+
43+
if (DaprCloudConfigClientManager.daprClient == null) {
44+
DaprCloudConfigClientManager.daprClient = daprClientBuilder.build();
45+
}
46+
47+
if (DaprCloudConfigClientManager.daprPreviewClient == null) {
48+
DaprCloudConfigClientManager.daprPreviewClient = daprClientBuilder.buildPreviewClient();
49+
}
50+
}
51+
52+
private DaprConnectionDetails createDaprConnectionDetails(DaprClientProperties properties) {
53+
return new CloudConfigPropertiesDaprConnectionDetails(properties);
54+
}
55+
DaprClientBuilder createDaprClientBuilder(DaprConnectionDetails daprConnectionDetails) {
56+
DaprClientBuilder builder = new DaprClientBuilder();
57+
if (daprConnectionDetails.httpEndpoint() != null) {
58+
builder.withPropertyOverride(Properties.HTTP_ENDPOINT, daprConnectionDetails.httpEndpoint());
59+
}
60+
if (daprConnectionDetails.grpcEndpoint() != null) {
61+
builder.withPropertyOverride(Properties.GRPC_ENDPOINT, daprConnectionDetails.grpcEndpoint());
62+
}
63+
if (daprConnectionDetails.httpPort() != null) {
64+
builder.withPropertyOverride(Properties.HTTP_PORT, String.valueOf(daprConnectionDetails.httpPort()));
65+
}
66+
if (daprConnectionDetails.grpcPort() != null) {
67+
builder.withPropertyOverride(Properties.GRPC_PORT, String.valueOf(daprConnectionDetails.grpcPort()));
68+
}
69+
return builder;
70+
}
71+
72+
public static DaprPreviewClient getDaprPreviewClient() {
73+
return DaprCloudConfigClientManager.daprPreviewClient;
74+
}
75+
76+
public static DaprClient getDaprClient() {
77+
return DaprCloudConfigClientManager.daprClient;
78+
}
79+
80+
public DaprCloudConfigProperties getDaprCloudConfigProperties() {
81+
return daprCloudConfigProperties;
82+
}
83+
84+
public DaprClientProperties getDaprClientConfig() {
85+
return daprClientConfig;
86+
}
87+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Copyright (c) 2016-2024 Team Fangkehou
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.dapr.spring.boot.cloudconfig.config;
18+
19+
import org.springframework.boot.context.properties.ConfigurationProperties;
20+
21+
/**
22+
* The properties for creating dapr client.
23+
*/
24+
@ConfigurationProperties(DaprCloudConfigProperties.PROPERTY_PREFIX)
25+
public class DaprCloudConfigProperties {
26+
27+
public static final String PROPERTY_PREFIX = "dapr.cloudconfig";
28+
29+
/**
30+
* whether enable secret store
31+
*/
32+
private Boolean enabled = true;
33+
34+
/**
35+
* get config timeout
36+
*/
37+
private Integer timeout = 2000;
38+
39+
public Integer getTimeout() {
40+
return timeout;
41+
}
42+
43+
public void setTimeout(Integer timeout) {
44+
this.timeout = timeout;
45+
}
46+
47+
public Boolean getEnabled() {
48+
return enabled;
49+
}
50+
51+
public void setEnabled(Boolean enabled) {
52+
this.enabled = enabled;
53+
}
54+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/*
2+
* Copyright (c) 2016-2024 Team Fangkehou
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.dapr.spring.boot.cloudconfig.configdata.config;
18+
19+
import io.dapr.client.DaprClient;
20+
import io.dapr.spring.boot.cloudconfig.config.DaprCloudConfigClientManager;
21+
import io.dapr.spring.boot.cloudconfig.config.DaprCloudConfigProperties;
22+
import io.dapr.spring.boot.cloudconfig.parser.DaprSecretStoreParserHandler;
23+
import org.apache.commons.logging.Log;
24+
import org.springframework.boot.context.config.ConfigData;
25+
import org.springframework.boot.context.config.ConfigDataLoader;
26+
import org.springframework.boot.context.config.ConfigDataLoaderContext;
27+
import org.springframework.boot.context.config.ConfigDataResourceNotFoundException;
28+
import org.springframework.boot.logging.DeferredLogFactory;
29+
import org.springframework.core.env.PropertySource;
30+
import reactor.core.publisher.Mono;
31+
32+
import java.io.IOException;
33+
import java.time.Duration;
34+
import java.util.ArrayList;
35+
import java.util.Collections;
36+
import java.util.List;
37+
import java.util.Map;
38+
39+
import static org.springframework.boot.context.config.ConfigData.Option.*;
40+
41+
public class DaprConfigurationConfigDataLoader implements ConfigDataLoader<DaprConfigurationConfigDataResource> {
42+
43+
private final Log log;
44+
45+
private DaprClient daprClient;
46+
47+
private DaprCloudConfigProperties daprSecretStoreConfig;
48+
49+
public DaprConfigurationConfigDataLoader(DeferredLogFactory logFactory, DaprClient daprClient,
50+
DaprCloudConfigProperties daprSecretStoreConfig) {
51+
this.log = logFactory.getLog(getClass());
52+
this.daprClient = daprClient;
53+
this.daprSecretStoreConfig = daprSecretStoreConfig;
54+
}
55+
56+
57+
/**
58+
* Load {@link ConfigData} for the given resource.
59+
*
60+
* @param context the loader context
61+
* @param resource the resource to load
62+
* @return the loaded config data or {@code null} if the location should be skipped
63+
* @throws IOException on IO error
64+
* @throws ConfigDataResourceNotFoundException if the resource cannot be found
65+
*/
66+
@Override
67+
public ConfigData load(ConfigDataLoaderContext context, DaprConfigurationConfigDataResource resource)
68+
throws IOException, ConfigDataResourceNotFoundException {
69+
DaprCloudConfigClientManager daprClientSecretStoreConfigManager =
70+
getBean(context, DaprCloudConfigClientManager.class);
71+
72+
daprClient = DaprCloudConfigClientManager.getDaprClient();
73+
daprSecretStoreConfig = daprClientSecretStoreConfigManager.getDaprCloudConfigProperties();
74+
75+
if (resource.getSecretName() == null) {
76+
return fetchConfig(resource.getStoreName());
77+
} else {
78+
return fetchConfig(resource.getStoreName(), resource.getSecretName());
79+
}
80+
}
81+
82+
private ConfigData fetchConfig(String storeName) {
83+
Mono<Map<String, Map<String, String>>> secretMapMono = daprClient.getBulkSecret(storeName);
84+
85+
Map<String, Map<String, String>> secretMap =
86+
secretMapMono.block(Duration.ofMillis(daprSecretStoreConfig.getTimeout()));
87+
88+
if (secretMap == null) {
89+
return new ConfigData(Collections.emptyList(), IGNORE_IMPORTS, IGNORE_PROFILES, PROFILE_SPECIFIC);
90+
}
91+
92+
List<PropertySource<?>> sourceList = new ArrayList<>();
93+
94+
for (Map.Entry<String, Map<String, String>> entry : secretMap.entrySet()) {
95+
sourceList.addAll(DaprSecretStoreParserHandler.getInstance().parseDaprSecretStoreData(entry.getKey(),
96+
entry.getValue()));
97+
}
98+
99+
return new ConfigData(sourceList, IGNORE_IMPORTS, IGNORE_PROFILES, PROFILE_SPECIFIC);
100+
}
101+
102+
private ConfigData fetchConfig(String storeName, String secretName) {
103+
Mono<Map<String, String>> secretMapMono = daprClient.getSecret(storeName, secretName);
104+
105+
Map<String, String> secretMap = secretMapMono.block(Duration.ofMillis(daprSecretStoreConfig.getTimeout()));
106+
107+
if (secretMap == null) {
108+
return new ConfigData(Collections.emptyList(), IGNORE_IMPORTS, IGNORE_PROFILES, PROFILE_SPECIFIC);
109+
}
110+
111+
List<PropertySource<?>> sourceList = new ArrayList<>(
112+
DaprSecretStoreParserHandler.getInstance().parseDaprSecretStoreData(secretName, secretMap));
113+
114+
return new ConfigData(sourceList, IGNORE_IMPORTS, IGNORE_PROFILES, PROFILE_SPECIFIC);
115+
}
116+
117+
protected <T> T getBean(ConfigDataLoaderContext context, Class<T> type) {
118+
if (context.getBootstrapContext().isRegistered(type)) {
119+
return context.getBootstrapContext().get(type);
120+
}
121+
return null;
122+
}
123+
}

0 commit comments

Comments
 (0)