Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
5 changes: 5 additions & 0 deletions spring-cloud-config-server/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,11 @@
<artifactId>google-cloud-secretmanager</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-parametermanager</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-iamcredentials</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.util.List;
import java.util.Optional;

import com.google.cloud.parametermanager.v1.ParameterManagerClient;
import com.google.cloud.secretmanager.v1.SecretManagerServiceClient;
import io.micrometer.observation.ObservationRegistry;
import jakarta.servlet.http.HttpServletRequest;
Expand Down Expand Up @@ -58,6 +59,9 @@
import org.springframework.cloud.config.server.environment.CredhubEnvironmentRepositoryFactory;
import org.springframework.cloud.config.server.environment.EnvironmentRepository;
import org.springframework.cloud.config.server.environment.EnvironmentWatch;
import org.springframework.cloud.config.server.environment.GoogleParameterManagerEnvironmentProperties;
import org.springframework.cloud.config.server.environment.GoogleParameterManagerEnvironmentRepository;
import org.springframework.cloud.config.server.environment.GoogleParameterManagerEnvironmentRepositoryFactory;
import org.springframework.cloud.config.server.environment.GoogleSecretManagerEnvironmentProperties;
import org.springframework.cloud.config.server.environment.GoogleSecretManagerEnvironmentRepository;
import org.springframework.cloud.config.server.environment.GoogleSecretManagerEnvironmentRepositoryFactory;
Expand Down Expand Up @@ -122,13 +126,15 @@
JdbcEnvironmentProperties.class, NativeEnvironmentProperties.class, VaultEnvironmentProperties.class,
RedisEnvironmentProperties.class, AwsS3EnvironmentProperties.class,
AwsSecretsManagerEnvironmentProperties.class, AwsParameterStoreEnvironmentProperties.class,
GoogleSecretManagerEnvironmentProperties.class, MongoDbEnvironmentProperties.class })
GoogleSecretManagerEnvironmentProperties.class, GoogleParameterManagerEnvironmentProperties.class,
MongoDbEnvironmentProperties.class })
@Import({ CompositeRepositoryConfiguration.class, JdbcRepositoryConfiguration.class, VaultConfiguration.class,
SpringVaultRepositoryConfiguration.class, CredhubConfiguration.class, CredhubRepositoryConfiguration.class,
SvnRepositoryConfiguration.class, NativeRepositoryConfiguration.class, GitRepositoryConfiguration.class,
RedisRepositoryConfiguration.class, GoogleCloudSourceConfiguration.class, AwsS3RepositoryConfiguration.class,
AwsSecretsManagerRepositoryConfiguration.class, AwsParameterStoreRepositoryConfiguration.class,
GoogleSecretManagerRepositoryConfiguration.class, MongoRepositoryConfiguration.class,
GoogleSecretManagerRepositoryConfiguration.class, GoogleParameterManagerRepositoryConfiguration.class,
MongoRepositoryConfiguration.class,
// DefaultRepositoryConfiguration must be last
DefaultRepositoryConfiguration.class })
public class EnvironmentRepositoryConfiguration {
Expand Down Expand Up @@ -284,6 +290,17 @@ public GoogleSecretManagerEnvironmentRepositoryFactory googleSecretManagerEnviro

}

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(ParameterManagerClient.class)
static class GoogleParameterManagerFactoryConfig {

@Bean
public GoogleParameterManagerEnvironmentRepositoryFactory googleParameterManagerEnvironmentRepositoryFactory() {
return new GoogleParameterManagerEnvironmentRepositoryFactory();
}

}

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(VaultTemplate.class)
@Import(SpringVaultClientConfiguration.class)
Expand Down Expand Up @@ -562,6 +579,20 @@ public GoogleSecretManagerEnvironmentRepository googleSecretManagerEnvironmentRe

}

@Configuration(proxyBeanMethods = false)
@Profile("parameter-manager")
@ConditionalOnClass(ParameterManagerClient.class)
class GoogleParameterManagerRepositoryConfiguration {

@Bean
public GoogleParameterManagerEnvironmentRepository googleParameterManagerEnvironmentRepository(
GoogleParameterManagerEnvironmentRepositoryFactory factory,
GoogleParameterManagerEnvironmentProperties environmentProperties) throws Exception {
return factory.build(environmentProperties);
}

}

@Configuration(proxyBeanMethods = false)
@Profile("mongodb")
@ConditionalOnClass(MongoTemplate.class)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright 2013-present 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.config.server.environment;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.config.server.support.EnvironmentRepositoryProperties;

/**
* @author Yash Chauhan
*/
@ConfigurationProperties("spring.cloud.config.server.gcp-parameter-manager")
public class GoogleParameterManagerEnvironmentProperties implements EnvironmentRepositoryProperties {

private int order = DEFAULT_ORDER;

private String applicationLabel = "application";

private String profileLabel = "profile";

private String location = "global";

private String projectId;

public void setOrder(int order) {
this.order = order;
}

public int getOrder() {
return this.order;
}

public String getApplicationLabel() {
return this.applicationLabel;
}

public void setApplicationLabel(String applicationLabel) {
this.applicationLabel = applicationLabel;
}

public String getProfileLabel() {
return this.profileLabel;
}

public void setProfileLabel(String profileLabel) {
this.profileLabel = profileLabel;
}

public String getLocation() {
return this.location;
}

public void setLocation(String location) {
this.location = location;
}

public String getProjectId() {
return this.projectId;
}

public void setProjectId(String projectId) {
this.projectId = projectId;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*
* Copyright 2013-present 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.config.server.environment;

import java.util.HashMap;
import java.util.Map;

import com.google.cloud.parametermanager.v1.LocationName;
import com.google.cloud.parametermanager.v1.Parameter;
import com.google.cloud.parametermanager.v1.ParameterManagerClient;
import com.google.cloud.parametermanager.v1.RenderParameterVersionResponse;

import org.springframework.cloud.config.environment.Environment;
import org.springframework.cloud.config.environment.PropertySource;
import org.springframework.core.Ordered;
import org.springframework.util.StringUtils;

/**
* @author Yash Chauhan
*/
public class GoogleParameterManagerEnvironmentRepository implements EnvironmentRepository, Ordered {

private final ParameterManagerClient parameterManagerClient;

private final GoogleParameterManagerEnvironmentProperties properties;

private final int order;

public GoogleParameterManagerEnvironmentRepository(ParameterManagerClient parameterManagerClient,
GoogleParameterManagerEnvironmentProperties properties) {

this.parameterManagerClient = parameterManagerClient;
this.properties = properties;
this.order = properties.getOrder();
}

@Override
public Environment findOne(String application, String profile, String label) {
if (!StringUtils.hasText(label)) {
label = "master";
}

if (!StringUtils.hasText(profile)) {
profile = "default";
}

if (!profile.startsWith("default")) {
profile = "default," + profile;
}

String[] profiles = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(profile));

Environment result = new Environment(application, profile, label, null, null);

try {
for (String profileUnit : profiles) {
Map<String, String> parameters = getParameters(this.parameterManagerClient, application, profileUnit);
if (!parameters.isEmpty()) {
result.add(new PropertySource("gpm:" + application + "-" + profileUnit, parameters));
}
}
}
catch (Exception ex) {
throw new IllegalStateException("Could not access Google Cloud Parameter Manager", ex);
}

return result;
}

private Map<String, String> getParameters(ParameterManagerClient client, String application, String profile) {
Map<String, String> result = new HashMap<>();

String projectId = properties.getProjectId();
if (!StringUtils.hasText(projectId)) {
throw new IllegalStateException("Google Cloud project ID must be configured");
}

LocationName locationName = LocationName.of(projectId, properties.getLocation());

for (Parameter parameter : client.listParameters(locationName).iterateAll()) {
if (parameter.getLabelsOrDefault(properties.getApplicationLabel(), "application")
.equalsIgnoreCase(application)
&& parameter.getLabelsOrDefault(properties.getProfileLabel(), "profile")
.equalsIgnoreCase(profile)) {

RenderParameterVersionResponse response = client
.renderParameterVersion(parameter.getName() + "/versions/latest");

String parameterName = parameter.getName();
String prefix = "projects/" + projectId + "/locations/" + properties.getLocation() + "/parameters/";

if (parameterName.startsWith(prefix)) {
parameterName = parameterName.substring(prefix.length());
}

result.put(parameterName, response.getRenderedPayload().toStringUtf8());
}
}

return result;
}

@Override
public int getOrder() {
return order;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright 2013-present 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.config.server.environment;

import com.google.cloud.parametermanager.v1.ParameterManagerClient;

/**
* @author Yash Chauhan
*/
public class GoogleParameterManagerEnvironmentRepositoryFactory implements
EnvironmentRepositoryFactory<GoogleParameterManagerEnvironmentRepository, GoogleParameterManagerEnvironmentProperties> {

@Override
public GoogleParameterManagerEnvironmentRepository build(
GoogleParameterManagerEnvironmentProperties environmentProperties) throws Exception {
return new GoogleParameterManagerEnvironmentRepository(ParameterManagerClient.create(), environmentProperties);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Copyright 2013-present 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.config.server.environment;

import java.util.List;

import com.google.cloud.parametermanager.v1.LocationName;
import com.google.cloud.parametermanager.v1.Parameter;
import com.google.cloud.parametermanager.v1.ParameterManagerClient;
import com.google.cloud.parametermanager.v1.RenderParameterVersionResponse;
import com.google.protobuf.ByteString;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

import org.springframework.cloud.config.environment.Environment;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class GoogleParameterManagerEnvironmentRepositoryTests {

@Test
@SuppressWarnings("unchecked")
public void testFindOne() {
ParameterManagerClient client = mock(ParameterManagerClient.class);

Parameter parameter = Parameter.newBuilder()
.setName("projects/test-project/locations/global/parameters/test-property")
.putLabels("application", "application")
.putLabels("profile", "default")
.build();

ParameterManagerClient.ListParametersPagedResponse parametersResponse = mock(
ParameterManagerClient.ListParametersPagedResponse.class);

when(parametersResponse.iterateAll()).thenReturn(List.of(parameter));
when(client.listParameters(any(LocationName.class))).thenReturn(parametersResponse);

RenderParameterVersionResponse renderResponse = RenderParameterVersionResponse.newBuilder()
.setRenderedPayload(ByteString.copyFromUtf8("test-value"))
.build();

when(client.renderParameterVersion(any(String.class))).thenReturn(renderResponse);

GoogleParameterManagerEnvironmentProperties properties = new GoogleParameterManagerEnvironmentProperties();
properties.setProjectId("test-project");

GoogleParameterManagerEnvironmentRepository repository = new GoogleParameterManagerEnvironmentRepository(client,
properties);

Environment environment = repository.findOne("application", "default", null);

assertThat(environment.getPropertySources()).hasSize(1);

assertThat(environment.getPropertySources().get(0).getSource().get("test-property")).isEqualTo("test-value");
Mockito.verify(client)
.renderParameterVersion("projects/test-project/locations/global/parameters/test-property/versions/latest");
}

}