Initial commit
This commit is contained in:
302
spring-cloud-config-server/.flattened-pom.xml
Normal file
302
spring-cloud-config-server/.flattened-pom.xml
Normal file
@@ -0,0 +1,302 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-config</artifactId>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-config-server</artifactId>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
<name>spring-cloud-config-server</name>
|
||||
<description>spring-cloud-config-server</description>
|
||||
<url>https://projects.spring.io/spring-cloud/spring-cloud-config-server/</url>
|
||||
<organization>
|
||||
<name>Pivotal Software, Inc.</name>
|
||||
<url>https://www.spring.io</url>
|
||||
</organization>
|
||||
<licenses>
|
||||
<license>
|
||||
<name>Apache License, Version 2.0</name>
|
||||
<url>https://www.apache.org/licenses/LICENSE-2.0</url>
|
||||
<comments>Copyright 2014-2021 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.</comments>
|
||||
</license>
|
||||
</licenses>
|
||||
<developers>
|
||||
<developer>
|
||||
<id>dsyer</id>
|
||||
<name>Dave Syer</name>
|
||||
<email>dsyer at pivotal.io</email>
|
||||
<organization>Pivotal Software, Inc.</organization>
|
||||
<organizationUrl>https://www.spring.io</organizationUrl>
|
||||
<roles>
|
||||
<role>lead</role>
|
||||
</roles>
|
||||
</developer>
|
||||
<developer>
|
||||
<id>sgibb</id>
|
||||
<name>Spencer Gibb</name>
|
||||
<email>sgibb at pivotal.io</email>
|
||||
<organization>Pivotal Software, Inc.</organization>
|
||||
<organizationUrl>https://www.spring.io</organizationUrl>
|
||||
<roles>
|
||||
<role>lead</role>
|
||||
</roles>
|
||||
</developer>
|
||||
<developer>
|
||||
<id>mgrzejszczak</id>
|
||||
<name>Marcin Grzejszczak</name>
|
||||
<email>mgrzejszczak at pivotal.io</email>
|
||||
<organization>Pivotal Software, Inc.</organization>
|
||||
<organizationUrl>https://www.spring.io</organizationUrl>
|
||||
<roles>
|
||||
<role>developer</role>
|
||||
</roles>
|
||||
</developer>
|
||||
<developer>
|
||||
<id>rbaxter</id>
|
||||
<name>Ryan Baxter</name>
|
||||
<email>rbaxter at pivotal.io</email>
|
||||
<organization>Pivotal Software, Inc.</organization>
|
||||
<organizationUrl>https://www.spring.io</organizationUrl>
|
||||
<roles>
|
||||
<role>developer</role>
|
||||
</roles>
|
||||
</developer>
|
||||
<developer>
|
||||
<id>omaciaszeksharma</id>
|
||||
<name>Olga Maciaszek-Sharma</name>
|
||||
<email>omaciaszeksharma at pivotal.io</email>
|
||||
<organization>Pivotal Software, Inc.</organization>
|
||||
<organizationUrl>https://www.spring.io</organizationUrl>
|
||||
<roles>
|
||||
<role>developer</role>
|
||||
</roles>
|
||||
</developer>
|
||||
</developers>
|
||||
<scm>
|
||||
<connection>scm:git:git://github.com/spring-cloud/spring-cloud-config.git/spring-cloud-config-server</connection>
|
||||
<developerConnection>scm:git:ssh://git@github.com/spring-cloud/spring-cloud-config.git/spring-cloud-config-server</developerConnection>
|
||||
<url>https://github.com/spring-cloud/spring-cloud-config/spring-cloud-config-server</url>
|
||||
</scm>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<version>3.0.0-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-jdbc</artifactId>
|
||||
<version>3.0.0-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
<version>3.0.0-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-config-client</artifactId>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
<version>3.0.0-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.micrometer</groupId>
|
||||
<artifactId>micrometer-observation</artifactId>
|
||||
<version>1.10.0-RC1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<version>3.0.0-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
<version>3.0.0-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-crypto</artifactId>
|
||||
<version>6.0.0-RC1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-rsa</artifactId>
|
||||
<version>1.0.11.RELEASE</version>
|
||||
<scope>compile</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-core</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-crypto</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.vault</groupId>
|
||||
<artifactId>spring-vault-core</artifactId>
|
||||
<version>3.0.0-RC1</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.credhub</groupId>
|
||||
<artifactId>spring-credhub-core</artifactId>
|
||||
<version>2.1.1.RELEASE</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents.client5</groupId>
|
||||
<artifactId>httpclient5</artifactId>
|
||||
<version>5.1.3</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jgit</groupId>
|
||||
<artifactId>org.eclipse.jgit</artifactId>
|
||||
<version>6.2.0.202206071550-r</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jgit</groupId>
|
||||
<artifactId>org.eclipse.jgit.http.apache</artifactId>
|
||||
<version>6.2.0.202206071550-r</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jgit</groupId>
|
||||
<artifactId>org.eclipse.jgit.ssh.apache</artifactId>
|
||||
<version>6.2.0.202206071550-r</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
<version>1.33</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||
<artifactId>jackson-dataformat-yaml</artifactId>
|
||||
<version>2.14.0-rc2</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.tmatesoft.svnkit</groupId>
|
||||
<artifactId>svnkit</artifactId>
|
||||
<version>1.10.1</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>software.amazon.awssdk</groupId>
|
||||
<artifactId>auth</artifactId>
|
||||
<version>2.17.195</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>software.amazon.awssdk</groupId>
|
||||
<artifactId>s3</artifactId>
|
||||
<version>2.17.195</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>software.amazon.awssdk</groupId>
|
||||
<artifactId>aws-core</artifactId>
|
||||
<version>2.17.195</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>software.amazon.awssdk</groupId>
|
||||
<artifactId>secretsmanager</artifactId>
|
||||
<version>2.17.195</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>software.amazon.awssdk</groupId>
|
||||
<artifactId>ssm</artifactId>
|
||||
<version>2.17.195</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure-processor</artifactId>
|
||||
<version>3.0.0-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.apis</groupId>
|
||||
<artifactId>google-api-services-iam</artifactId>
|
||||
<version>v1-rev20201112-1.30.10</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.cloud</groupId>
|
||||
<artifactId>google-cloud-secretmanager</artifactId>
|
||||
<version>1.0.1</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.apis</groupId>
|
||||
<artifactId>google-api-services-cloudresourcemanager</artifactId>
|
||||
<version>v1-rev20200210-1.30.9</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.auth</groupId>
|
||||
<artifactId>google-auth-library-oauth2-http</artifactId>
|
||||
<version>0.22.0</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -1,264 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>spring-cloud-config-server</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>spring-cloud-config-server</name>
|
||||
<description>spring-cloud-config-server</description>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-config</artifactId>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-jdbc</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-config-client</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.micrometer</groupId>
|
||||
<artifactId>micrometer-observation</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-crypto</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-rsa</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.vault</groupId>
|
||||
<artifactId>spring-vault-core</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.credhub</groupId>
|
||||
<artifactId>spring-credhub-core</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents.client5</groupId>
|
||||
<artifactId>httpclient5</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jgit</groupId>
|
||||
<artifactId>org.eclipse.jgit</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jgit</groupId>
|
||||
<artifactId>org.eclipse.jgit.http.apache</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jgit</groupId>
|
||||
<artifactId>org.eclipse.jgit.ssh.apache</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||
<artifactId>jackson-dataformat-yaml</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.tmatesoft.svnkit</groupId>
|
||||
<artifactId>svnkit</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>software.amazon.awssdk</groupId>
|
||||
<artifactId>auth</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>software.amazon.awssdk</groupId>
|
||||
<artifactId>s3</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>software.amazon.awssdk</groupId>
|
||||
<artifactId>aws-core</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>software.amazon.awssdk</groupId>
|
||||
<artifactId>secretsmanager</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>software.amazon.awssdk</groupId>
|
||||
<artifactId>ssm</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.apis</groupId>
|
||||
<artifactId>google-api-services-iam</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.cloud</groupId>
|
||||
<artifactId>google-cloud-secretmanager</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.apis</groupId>
|
||||
<artifactId>google-api-services-cloudresourcemanager</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.auth</groupId>
|
||||
<artifactId>google-auth-library-oauth2-http</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.platform</groupId>
|
||||
<artifactId>junit-platform-launcher</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-test-support</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jgit</groupId>
|
||||
<artifactId>org.eclipse.jgit.junit.http</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.tomakehurst</groupId>
|
||||
<artifactId>wiremock-jre8-standalone</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.micrometer</groupId>
|
||||
<artifactId>micrometer-observation-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.micrometer</groupId>
|
||||
<artifactId>micrometer-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.micrometer</groupId>
|
||||
<artifactId>micrometer-tracing-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.micrometer</groupId>
|
||||
<artifactId>micrometer-tracing-bridge-brave</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>localstack</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- AWS SDK v1 is required by testcontainers-localstack -->
|
||||
<dependency>
|
||||
<groupId>com.amazonaws</groupId>
|
||||
<artifactId>aws-java-sdk-core</artifactId>
|
||||
<version>1.12.287</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.zipkin.brave</groupId>
|
||||
<artifactId>brave-tests</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.zipkin.reporter2</groupId>
|
||||
<artifactId>zipkin-reporter-brave</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.zipkin.reporter2</groupId>
|
||||
<artifactId>zipkin-sender-urlconnection</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.awspring.cloud</groupId>
|
||||
<artifactId>spring-cloud-aws-s3</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<start-class>org.springframework.cloud.config.server.ConfigServerApplication
|
||||
</start-class>
|
||||
</properties>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>3.2.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>test-jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2019 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;
|
||||
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Configuration server application.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @deprecated Will be removed in 4.0.0.
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableAutoConfiguration
|
||||
@EnableConfigServer
|
||||
@Deprecated
|
||||
public class ConfigServerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
new SpringApplicationBuilder(ConfigServerApplication.class).properties("spring.config.name=configserver")
|
||||
.run(args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2019 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;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.cloud.config.server.config.ConfigServerConfiguration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Import(ConfigServerConfiguration.class)
|
||||
public @interface EnableConfigServer {
|
||||
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-2021 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.bootstrap;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
|
||||
import org.springframework.boot.env.EnvironmentPostProcessor;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
|
||||
/**
|
||||
* Normally you don't want the config server to be a config client itself, so this
|
||||
* listener disables the config client unless <code>spring.cloud.config.enabled</code> is
|
||||
* explicitly "true". It has to be "true" at the time this listener is fired, which means
|
||||
* <b>before</b> the <code>bootstrap.yml</code> is parsed, which in turn means to you need
|
||||
* to launch the application with an existing primed {@link Environment} (e.g. via System
|
||||
* properties or a {@link SpringApplicationBuilder}). This is the same rule of precedence
|
||||
* as for anything else affecting the bootstrap process itself, e.g. setting
|
||||
* <code>spring.cloud.bootstrap.name</code> to something other than "bootstrap".
|
||||
* <p>
|
||||
* N.B. a config server can always be an "embedded" config client (using its own config
|
||||
* repository as a property source) if you set
|
||||
* <code>spring.cloud.config.server.bootstrap=true</code> in <code>bootstrap.yml</code>.
|
||||
* This listener is only to prevent it from using HTTP to contact itself.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Ryan Baxter
|
||||
*
|
||||
*/
|
||||
// TODO We changed this class from an ApplicationListener to an EnvironmentPostProcessor
|
||||
// but did not change the class name to avoid breaking anyone in a minor release.
|
||||
// In the next major it will be safe to change the class name and remove the
|
||||
// ApplicationListener interface.
|
||||
public class ConfigServerBootstrapApplicationListener
|
||||
implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, EnvironmentPostProcessor, Ordered {
|
||||
|
||||
/**
|
||||
* Default order of the bootstrap application listener.
|
||||
*/
|
||||
public static final int DEFAULT_ORDER = Ordered.HIGHEST_PRECEDENCE + 4;
|
||||
|
||||
private int order = DEFAULT_ORDER;
|
||||
|
||||
private PropertySource<?> propertySource = new MapPropertySource("configServerClient",
|
||||
Collections.<String, Object>singletonMap("spring.cloud.config.enabled", "false"));
|
||||
|
||||
@Override
|
||||
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
|
||||
if (!environment.resolvePlaceholders("${spring.cloud.config.enabled:false}").equalsIgnoreCase("true")) {
|
||||
if (!environment.getPropertySources().contains(this.propertySource.getName())) {
|
||||
environment.getPropertySources().addLast(this.propertySource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return this.order;
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
|
||||
postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2019 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.bootstrap;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.config.client.ConfigClientProperties;
|
||||
import org.springframework.cloud.config.server.config.ConfigServerProperties;
|
||||
import org.springframework.cloud.config.server.config.EnvironmentRepositoryConfiguration;
|
||||
import org.springframework.cloud.config.server.environment.EnvironmentRepository;
|
||||
import org.springframework.cloud.config.server.environment.EnvironmentRepositoryPropertySourceLocator;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Bootstrap configuration to fetch external configuration from a (possibly remote)
|
||||
* {@link EnvironmentRepository}. Off by default because it can delay startup, but can be
|
||||
* enabled with <code>spring.cloud.config.server.bootstrap=true</code>. This would be
|
||||
* useful, for example, if the config server were embedded in another app that wanted to
|
||||
* be configured from the same repository as all the other clients.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Roy Clarkson
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnProperty("spring.cloud.config.server.bootstrap")
|
||||
public class ConfigServerBootstrapConfiguration {
|
||||
|
||||
@EnableConfigurationProperties(ConfigServerProperties.class)
|
||||
@Import({ EnvironmentRepositoryConfiguration.class })
|
||||
protected static class LocalPropertySourceLocatorConfiguration {
|
||||
|
||||
@Autowired
|
||||
private EnvironmentRepository repository;
|
||||
|
||||
@Autowired
|
||||
private ConfigClientProperties client;
|
||||
|
||||
@Autowired
|
||||
private ConfigServerProperties server;
|
||||
|
||||
@Bean
|
||||
public EnvironmentRepositoryPropertySourceLocator environmentRepositoryPropertySourceLocator() {
|
||||
return new EnvironmentRepositoryPropertySourceLocator(this.repository, this.client.getName(),
|
||||
this.client.getProfile(), getDefaultLabel());
|
||||
}
|
||||
|
||||
private String getDefaultLabel() {
|
||||
if (StringUtils.hasText(this.client.getLabel())) {
|
||||
return this.client.getLabel();
|
||||
}
|
||||
else if (StringUtils.hasText(this.server.getDefaultLabel())) {
|
||||
return this.server.getDefaultLabel();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2022 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.bootstrap;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.web.context.WebServerGracefulShutdownLifecycle;
|
||||
import org.springframework.cloud.config.server.config.ConfigServerProperties;
|
||||
import org.springframework.cloud.config.server.environment.EnvironmentController;
|
||||
import org.springframework.cloud.context.properties.ConfigurationPropertiesRebinder;
|
||||
import org.springframework.cloud.context.scope.refresh.RefreshScope;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnProperty("spring.cloud.config.server.bootstrap")
|
||||
public class ConfigServerBootstrapOverridesAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
ConfigServerBootstrapOverridesLifecycle configServerBootstrapOverridesLifecycle(ApplicationContext context) {
|
||||
return new ConfigServerBootstrapOverridesLifecycle(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* SmartLifecycle that refreshes and rebinds beans needed for
|
||||
* spring.cloud.config.server.overrides to work.
|
||||
*/
|
||||
private static class ConfigServerBootstrapOverridesLifecycle implements SmartLifecycle {
|
||||
|
||||
private final ApplicationContext context;
|
||||
|
||||
private volatile boolean running;
|
||||
|
||||
ConfigServerBootstrapOverridesLifecycle(ApplicationContext context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
this.running = true;
|
||||
ConfigurationPropertiesRebinder rebinder = context.getBean(ConfigurationPropertiesRebinder.class);
|
||||
rebinder.rebind(ConfigServerProperties.class);
|
||||
RefreshScope refreshScope = context.getBean(RefreshScope.class);
|
||||
refreshScope.refresh(EnvironmentController.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
this.running = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return this.running;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPhase() {
|
||||
// Run before WebServerStartStopLifecycle
|
||||
return WebServerGracefulShutdownLifecycle.SMART_LIFECYCLE_PHASE - 3072;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2019 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.composite;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.boot.context.properties.bind.Binder;
|
||||
import org.springframework.cloud.config.server.environment.EnvironmentRepository;
|
||||
import org.springframework.cloud.config.server.support.EnvironmentRepositoryProperties;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
/**
|
||||
* A {@link BeanFactoryPostProcessor} to register {@link EnvironmentRepository}
|
||||
* {@link BeanDefinition}s based on the composite list configuration.
|
||||
*
|
||||
* @author Dylan Roberts
|
||||
*/
|
||||
public class CompositeEnvironmentBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
|
||||
|
||||
private Environment environment;
|
||||
|
||||
public CompositeEnvironmentBeanFactoryPostProcessor(Environment environment) {
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
|
||||
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
|
||||
|
||||
List<String> typePropertyList = CompositeUtils.getCompositeTypeList(this.environment);
|
||||
for (int i = 0; i < typePropertyList.size(); i++) {
|
||||
String type = typePropertyList.get(i);
|
||||
String factoryName = CompositeUtils.getFactoryName(type, beanFactory);
|
||||
|
||||
Type[] factoryTypes = CompositeUtils.getEnvironmentRepositoryFactoryTypeParams(beanFactory, factoryName);
|
||||
Class<? extends EnvironmentRepositoryProperties> propertiesClass;
|
||||
propertiesClass = (Class<? extends EnvironmentRepositoryProperties>) factoryTypes[1];
|
||||
EnvironmentRepositoryProperties properties = bindProperties(i, propertiesClass, this.environment);
|
||||
AbstractBeanDefinition propertiesDefinition = BeanDefinitionBuilder
|
||||
.genericBeanDefinition(EnvironmentRepositoryProperties.class, () -> properties).getBeanDefinition();
|
||||
String propertiesBeanName = String.format("%s-env-repo-properties%d", type, i);
|
||||
registry.registerBeanDefinition(propertiesBeanName, propertiesDefinition);
|
||||
|
||||
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||
.genericBeanDefinition(EnvironmentRepository.class).setFactoryMethodOnBean("build", factoryName)
|
||||
.addConstructorArgValue(properties).getBeanDefinition();
|
||||
String beanName = String.format("%s-env-repo%d", type, i);
|
||||
registry.registerBeanDefinition(beanName, beanDefinition);
|
||||
}
|
||||
}
|
||||
|
||||
private <P extends EnvironmentRepositoryProperties> P bindProperties(int index, Class<P> propertiesClass,
|
||||
Environment environment) {
|
||||
Binder binder = Binder.get(environment);
|
||||
String environmentConfigurationPropertyName = String.format("spring.cloud.config.server.composite[%d]", index);
|
||||
P properties = binder.bindOrCreate(environmentConfigurationPropertyName, propertiesClass);
|
||||
properties.setOrder(index + 1);
|
||||
return properties;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2019 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.composite;
|
||||
|
||||
import java.lang.reflect.AnnotatedType;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.boot.context.properties.bind.Binder;
|
||||
import org.springframework.cloud.config.server.environment.EnvironmentRepositoryFactory;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.type.MethodMetadata;
|
||||
|
||||
/**
|
||||
* @author Dylan Roberts
|
||||
*/
|
||||
public final class CompositeUtils {
|
||||
|
||||
private CompositeUtils() {
|
||||
throw new IllegalStateException("Can't instantiate a utility class");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of values of the `type` field from the
|
||||
* `spring.cloud.config.server.composite` collection.
|
||||
* @param environment Spring Environment
|
||||
* @return list of matching types
|
||||
*/
|
||||
public static List<String> getCompositeTypeList(Environment environment) {
|
||||
return Binder.get(environment).bind("spring.cloud.config.server", CompositeConfig.class).get().getComposite()
|
||||
.stream().map(map -> (String) map.get("type")).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a type of EnvironmentRepository (git, svn, native, etc...) returns the name
|
||||
* of the factory bean. See {@link #getCompositeTypeList(Environment)}
|
||||
* @param type type of a repository
|
||||
* @param beanFactory Spring Bean Factory
|
||||
* @return name of the factory bean
|
||||
*/
|
||||
public static String getFactoryName(String type, ConfigurableListableBeanFactory beanFactory) {
|
||||
String[] factoryNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory,
|
||||
EnvironmentRepositoryFactory.class, true, false);
|
||||
return Arrays.stream(factoryNames).filter(n -> n.startsWith(type)).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a Factory Name return the generic type parameters of the factory (The actual
|
||||
* repository class, and its properties class).
|
||||
* @param beanFactory Spring Bean Factory
|
||||
* @param factoryName name of the factory
|
||||
* @return generic type params of the factory
|
||||
*/
|
||||
public static Type[] getEnvironmentRepositoryFactoryTypeParams(ConfigurableListableBeanFactory beanFactory,
|
||||
String factoryName) {
|
||||
MethodMetadata methodMetadata = (MethodMetadata) beanFactory.getBeanDefinition(factoryName).getSource();
|
||||
Class<?> factoryClass = null;
|
||||
try {
|
||||
factoryClass = Class.forName(methodMetadata.getReturnTypeName());
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
Optional<AnnotatedType> annotatedFactoryType = Arrays.stream(factoryClass.getAnnotatedInterfaces())
|
||||
.filter(i -> {
|
||||
ParameterizedType parameterizedType = (ParameterizedType) i.getType();
|
||||
return parameterizedType.getRawType().equals(EnvironmentRepositoryFactory.class);
|
||||
}).findFirst();
|
||||
ParameterizedType factoryParameterizedType = (ParameterizedType) annotatedFactoryType
|
||||
.orElse(factoryClass.getAnnotatedSuperclass()).getType();
|
||||
return factoryParameterizedType.getActualTypeArguments();
|
||||
}
|
||||
|
||||
static class CompositeConfig {
|
||||
|
||||
List<Map<String, Object>> composite;
|
||||
|
||||
public List<Map<String, Object>> getComposite() {
|
||||
return this.composite;
|
||||
}
|
||||
|
||||
public void setComposite(List<Map<String, Object>> composite) {
|
||||
this.composite = composite;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2019 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.composite;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
|
||||
/**
|
||||
* @author Dylan Roberts
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Conditional(OnSearchPathLocatorPresent.class)
|
||||
public @interface ConditionalOnMissingSearchPathLocator {
|
||||
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2019 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.composite;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
|
||||
/**
|
||||
* @author Dylan Roberts
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Conditional(OnSearchPathLocatorPresent.class)
|
||||
public @interface ConditionalOnSearchPathLocator {
|
||||
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2019 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.composite;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
|
||||
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
|
||||
import org.springframework.cloud.config.server.environment.EnvironmentRepository;
|
||||
import org.springframework.cloud.config.server.environment.SearchPathLocator;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
|
||||
/**
|
||||
* @author Dylan Roberts
|
||||
*/
|
||||
public class OnSearchPathLocatorPresent extends SpringBootCondition {
|
||||
|
||||
@Override
|
||||
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
|
||||
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
|
||||
List<String> types = CompositeUtils.getCompositeTypeList(context.getEnvironment());
|
||||
|
||||
// get EnvironmentRepository types from registered factories
|
||||
List<Class<? extends EnvironmentRepository>> repositoryTypes = new ArrayList<>();
|
||||
for (String type : types) {
|
||||
String factoryName = CompositeUtils.getFactoryName(type, beanFactory);
|
||||
Type[] actualTypeArguments = CompositeUtils.getEnvironmentRepositoryFactoryTypeParams(beanFactory,
|
||||
factoryName);
|
||||
Class<? extends EnvironmentRepository> repositoryType;
|
||||
repositoryType = (Class<? extends EnvironmentRepository>) actualTypeArguments[0];
|
||||
repositoryTypes.add(repositoryType);
|
||||
}
|
||||
|
||||
boolean required = metadata.isAnnotated(ConditionalOnSearchPathLocator.class.getName());
|
||||
boolean foundSearchPathLocator = repositoryTypes.stream().anyMatch(SearchPathLocator.class::isAssignableFrom);
|
||||
if (required && !foundSearchPathLocator) {
|
||||
return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnSearchPathLocator.class)
|
||||
.notAvailable(SearchPathLocator.class.getTypeName()));
|
||||
}
|
||||
if (!required && foundSearchPathLocator) {
|
||||
return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnMissingSearchPathLocator.class)
|
||||
.available(SearchPathLocator.class.getTypeName()));
|
||||
}
|
||||
return ConditionOutcome.match();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2019 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.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.cloud.config.server.environment.CompositeEnvironmentRepository;
|
||||
import org.springframework.cloud.config.server.environment.EnvironmentRepository;
|
||||
import org.springframework.cloud.config.server.environment.SearchPathCompositeEnvironmentRepository;
|
||||
import org.springframework.cloud.config.server.environment.SearchPathLocator;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnMissingBean(CompositeEnvironmentRepository.class)
|
||||
public class CompositeConfiguration {
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
@ConditionalOnBean(SearchPathLocator.class)
|
||||
public SearchPathCompositeEnvironmentRepository searchPathCompositeEnvironmentRepository(
|
||||
@Autowired(required = false) List<EnvironmentRepository> environmentRepos,
|
||||
ConfigServerProperties properties, ObjectProvider<ObservationRegistry> observationRegistry) {
|
||||
environmentRepos = environmentRepos != null ? environmentRepos : new ArrayList<>();
|
||||
return new SearchPathCompositeEnvironmentRepository(environmentRepos,
|
||||
observationRegistry.getIfAvailable(() -> ObservationRegistry.NOOP),
|
||||
properties.isFailOnCompositeError());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
@ConditionalOnMissingBean(SearchPathLocator.class)
|
||||
public CompositeEnvironmentRepository compositeEnvironmentRepository(
|
||||
@Autowired(required = false) List<EnvironmentRepository> environmentRepos,
|
||||
ConfigServerProperties properties, ObjectProvider<ObservationRegistry> observationRegistry) {
|
||||
environmentRepos = environmentRepos != null ? environmentRepos : new ArrayList<>();
|
||||
return new CompositeEnvironmentRepository(environmentRepos,
|
||||
observationRegistry.getIfAvailable(() -> ObservationRegistry.NOOP),
|
||||
properties.isFailOnCompositeError());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2019 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.config;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnBean(ConfigServerConfiguration.Marker.class)
|
||||
@ConditionalOnProperty(name = ConfigServerProperties.PREFIX + ".enabled", matchIfMissing = true)
|
||||
@EnableConfigurationProperties(ConfigServerProperties.class)
|
||||
@Import({ EnvironmentRepositoryConfiguration.class, CompositeConfiguration.class, ResourceRepositoryConfiguration.class,
|
||||
ConfigServerEncryptionConfiguration.class, ConfigServerMvcConfiguration.class,
|
||||
ResourceEncryptorConfiguration.class })
|
||||
public class ConfigServerAutoConfiguration {
|
||||
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2019 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.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class ConfigServerConfiguration {
|
||||
|
||||
@Bean
|
||||
public Marker enableConfigServerMarker() {
|
||||
return new Marker();
|
||||
}
|
||||
|
||||
class Marker {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2019 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.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cloud.config.server.encryption.EncryptionController;
|
||||
import org.springframework.cloud.config.server.encryption.TextEncryptorLocator;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author Bartosz Wojtkiewicz
|
||||
* @author Rafal Zukowski
|
||||
*
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class ConfigServerEncryptionConfiguration {
|
||||
|
||||
@Autowired(required = false)
|
||||
private TextEncryptorLocator encryptor;
|
||||
|
||||
@Autowired
|
||||
private ConfigServerProperties properties;
|
||||
|
||||
@Bean
|
||||
public EncryptionController encryptionController() {
|
||||
EncryptionController controller = new EncryptionController(this.encryptor);
|
||||
controller.setDefaultApplicationName(this.properties.getDefaultApplicationName());
|
||||
controller.setDefaultProfile(this.properties.getDefaultProfile());
|
||||
return controller;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,166 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2019 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.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
import org.springframework.boot.actuate.health.Status;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.cloud.config.environment.Environment;
|
||||
import org.springframework.cloud.config.environment.PropertySource;
|
||||
import org.springframework.cloud.config.server.environment.EnvironmentRepository;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@ConfigurationProperties("spring.cloud.config.server.health")
|
||||
public class ConfigServerHealthIndicator extends AbstractHealthIndicator {
|
||||
|
||||
private static Log logger = LogFactory.getLog(ConfigServerHealthIndicator.class);
|
||||
|
||||
private EnvironmentRepository environmentRepository;
|
||||
|
||||
private Map<String, Repository> repositories = new LinkedHashMap<>();
|
||||
|
||||
private String downHealthStatus = Status.DOWN.getCode();
|
||||
|
||||
// autowired required or boot constructor binding produces an error
|
||||
@Autowired
|
||||
public ConfigServerHealthIndicator(EnvironmentRepository environmentRepository) {
|
||||
this.environmentRepository = environmentRepository;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
if (this.repositories.isEmpty()) {
|
||||
this.repositories.put("app", new Repository());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doHealthCheck(Health.Builder builder) {
|
||||
builder.up();
|
||||
List<Map<String, Object>> details = new ArrayList<>();
|
||||
for (String name : this.repositories.keySet()) {
|
||||
Repository repository = this.repositories.get(name);
|
||||
String application = (repository.getName() == null) ? name : repository.getName();
|
||||
String profiles = repository.getProfiles();
|
||||
|
||||
try {
|
||||
Environment environment = this.environmentRepository.findOne(application, profiles,
|
||||
repository.getLabel(), false);
|
||||
|
||||
HashMap<String, Object> detail = new HashMap<>();
|
||||
detail.put("name", environment.getName());
|
||||
detail.put("label", environment.getLabel());
|
||||
if (environment.getProfiles() != null && environment.getProfiles().length > 0) {
|
||||
detail.put("profiles", Arrays.asList(environment.getProfiles()));
|
||||
}
|
||||
|
||||
if (!CollectionUtils.isEmpty(environment.getPropertySources())) {
|
||||
List<String> sources = new ArrayList<>();
|
||||
for (PropertySource source : environment.getPropertySources()) {
|
||||
sources.add(source.getName());
|
||||
}
|
||||
detail.put("sources", sources);
|
||||
}
|
||||
details.add(detail);
|
||||
}
|
||||
catch (Exception e) {
|
||||
logger.debug("Could not read repository: " + application, e);
|
||||
HashMap<String, String> map = new HashMap<>();
|
||||
map.put("application", application);
|
||||
map.put("profiles", profiles);
|
||||
builder.withDetail("repository", map);
|
||||
builder.status(this.downHealthStatus).withException(e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
builder.withDetail("repositories", details);
|
||||
|
||||
}
|
||||
|
||||
public Map<String, Repository> getRepositories() {
|
||||
return this.repositories;
|
||||
}
|
||||
|
||||
public void setRepositories(Map<String, Repository> repositories) {
|
||||
this.repositories = repositories;
|
||||
}
|
||||
|
||||
public String getDownHealthStatus() {
|
||||
return downHealthStatus;
|
||||
}
|
||||
|
||||
public void setDownHealthStatus(String downHealthStatus) {
|
||||
this.downHealthStatus = downHealthStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Repository properties.
|
||||
*/
|
||||
public static class Repository {
|
||||
|
||||
private String name;
|
||||
|
||||
private String profiles = "default";
|
||||
|
||||
private String label;
|
||||
|
||||
public Repository() {
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getProfiles() {
|
||||
return this.profiles;
|
||||
}
|
||||
|
||||
public void setProfiles(String profiles) {
|
||||
this.profiles = profiles;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return this.label;
|
||||
}
|
||||
|
||||
public void setLabel(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2019 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.config;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.cloud.config.server.encryption.EnvironmentEncryptor;
|
||||
import org.springframework.cloud.config.server.encryption.ResourceEncryptor;
|
||||
import org.springframework.cloud.config.server.environment.EnvironmentController;
|
||||
import org.springframework.cloud.config.server.environment.EnvironmentEncryptorEnvironmentRepository;
|
||||
import org.springframework.cloud.config.server.environment.EnvironmentRepository;
|
||||
import org.springframework.cloud.config.server.resource.ResourceController;
|
||||
import org.springframework.cloud.config.server.resource.ResourceControllerAdvice;
|
||||
import org.springframework.cloud.config.server.resource.ResourceRepository;
|
||||
import org.springframework.cloud.context.config.annotation.RefreshScope;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
* @author Roy Clarkson
|
||||
* @author Tim Ysewyn
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnWebApplication
|
||||
public class ConfigServerMvcConfiguration implements WebMvcConfigurer {
|
||||
|
||||
@Override
|
||||
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
|
||||
configurer.mediaType("properties", MediaType.valueOf("text/plain"));
|
||||
configurer.mediaType("yml", MediaType.valueOf("text/yaml"));
|
||||
configurer.mediaType("yaml", MediaType.valueOf("text/yaml"));
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnMissingBean(org.springframework.cloud.context.scope.refresh.RefreshScope.class)
|
||||
static class EnvironmentControllerConfiguration {
|
||||
|
||||
@Autowired(required = false)
|
||||
private List<EnvironmentEncryptor> environmentEncryptors;
|
||||
|
||||
@Autowired(required = false)
|
||||
private Map<String, ResourceEncryptor> resourceEncryptorMap = new HashMap<>();
|
||||
|
||||
@Autowired(required = false)
|
||||
private ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
@Autowired(required = false)
|
||||
private ObservationRegistry observationRegistry = ObservationRegistry.NOOP;
|
||||
|
||||
@Bean
|
||||
public EnvironmentController environmentController(EnvironmentRepository envRepository,
|
||||
ConfigServerProperties server) {
|
||||
return delegateController(envRepository, server);
|
||||
}
|
||||
|
||||
protected EnvironmentController delegateController(EnvironmentRepository envRepository,
|
||||
ConfigServerProperties server) {
|
||||
EnvironmentController controller = new EnvironmentController(encrypted(envRepository, server),
|
||||
this.objectMapper);
|
||||
controller.setStripDocumentFromYaml(server.isStripDocumentFromYaml());
|
||||
controller.setAcceptEmpty(server.isAcceptEmpty());
|
||||
return controller;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(ResourceRepository.class)
|
||||
public ResourceController resourceController(ResourceRepository repository, EnvironmentRepository envRepository,
|
||||
ConfigServerProperties server) {
|
||||
ResourceController controller = new ResourceController(repository, encrypted(envRepository, server),
|
||||
this.resourceEncryptorMap);
|
||||
controller.setEncryptEnabled(server.getEncrypt().isEnabled());
|
||||
controller.setPlainTextEncryptEnabled(server.getEncrypt().isPlainTextEncrypt());
|
||||
return controller;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(ResourceController.class)
|
||||
public ResourceControllerAdvice resourceControllerAdvice() {
|
||||
return new ResourceControllerAdvice();
|
||||
}
|
||||
|
||||
private EnvironmentRepository encrypted(EnvironmentRepository envRepository, ConfigServerProperties server) {
|
||||
EnvironmentEncryptorEnvironmentRepository encrypted = new EnvironmentEncryptorEnvironmentRepository(
|
||||
envRepository, this.environmentEncryptors, this.observationRegistry);
|
||||
encrypted.setOverrides(server.getOverrides());
|
||||
return encrypted;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnBean(org.springframework.cloud.context.scope.refresh.RefreshScope.class)
|
||||
static class RefreshableEnvironmentControllerConfiguration extends EnvironmentControllerConfiguration {
|
||||
|
||||
@Override
|
||||
@Bean
|
||||
@RefreshScope
|
||||
public EnvironmentController environmentController(EnvironmentRepository envRepository,
|
||||
ConfigServerProperties server) {
|
||||
return super.delegateController(envRepository, server);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,256 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2019 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.config;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
* @author Roy Clarkson
|
||||
*/
|
||||
@ConfigurationProperties(ConfigServerProperties.PREFIX)
|
||||
public class ConfigServerProperties {
|
||||
|
||||
/**
|
||||
* Config Server properties prefix.
|
||||
*/
|
||||
public static final String PREFIX = "spring.cloud.config.server";
|
||||
|
||||
/**
|
||||
* Flag indicating config server is enabled.
|
||||
*/
|
||||
private boolean enabled = true;
|
||||
|
||||
/**
|
||||
* Flag indicating that the config server should initialize its own Environment with
|
||||
* properties from the remote repository. Off by default because it delays startup but
|
||||
* can be useful when embedding the server in another application.
|
||||
*/
|
||||
private boolean bootstrap;
|
||||
|
||||
/**
|
||||
* Prefix for configuration resource paths (default is empty). Useful when embedding
|
||||
* in another application when you don't want to change the context path or servlet
|
||||
* path.
|
||||
*/
|
||||
private String prefix;
|
||||
|
||||
/**
|
||||
* Default repository label when incoming requests do not have a specific label.
|
||||
*/
|
||||
private String defaultLabel;
|
||||
|
||||
/**
|
||||
* Extra map for a property source to be sent to all clients unconditionally.
|
||||
*/
|
||||
private Map<String, String> overrides = new LinkedHashMap<String, String>();
|
||||
|
||||
/**
|
||||
* Flag to indicate that YAML documents that are text or collections (not a map)
|
||||
* should be returned in "native" form.
|
||||
*/
|
||||
private boolean stripDocumentFromYaml = true;
|
||||
|
||||
/**
|
||||
* Flag to indicate that If HTTP 404 needs to be sent if Application is not Found.
|
||||
*/
|
||||
private boolean acceptEmpty = true;
|
||||
|
||||
/**
|
||||
* Default application name when incoming requests do not have a specific one.
|
||||
*/
|
||||
private String defaultApplicationName = "application";
|
||||
|
||||
/**
|
||||
* Default application profile when incoming requests do not have a specific one.
|
||||
*/
|
||||
private String defaultProfile = "default";
|
||||
|
||||
/**
|
||||
* Flag indicating that if there are any errors reading properties from a subordinate
|
||||
* environment repository in a composite environment repository, then the entire
|
||||
* composite read should fail. Useful when set to false when a Vault repository is in
|
||||
* the composite to allow clients to still read properties from other repositories
|
||||
* without providing a valid Vault token.
|
||||
*
|
||||
* Defaults to true, resulting in a failure on any error.
|
||||
*/
|
||||
private boolean failOnCompositeError = true;
|
||||
|
||||
/**
|
||||
* By default the location order we use in GenericResourceRepository is the order in
|
||||
* which they are listed. Prior to Hoxton.SR11 the order used to be reverse. If this
|
||||
* property is set to true then we will reverse ther order like it used to be prior to
|
||||
* Hoxton.SR11.
|
||||
*/
|
||||
private boolean reverseLocationOrder = false;
|
||||
|
||||
/**
|
||||
* Decryption configuration for when server handles encrypted properties before
|
||||
* sending them to clients.
|
||||
*/
|
||||
private Encrypt encrypt = new Encrypt();
|
||||
|
||||
public boolean isEnabled() {
|
||||
return this.enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public Encrypt getEncrypt() {
|
||||
return this.encrypt;
|
||||
}
|
||||
|
||||
public String getDefaultLabel() {
|
||||
return this.defaultLabel;
|
||||
}
|
||||
|
||||
public void setDefaultLabel(String defaultLabel) {
|
||||
this.defaultLabel = defaultLabel;
|
||||
}
|
||||
|
||||
public boolean isBootstrap() {
|
||||
return this.bootstrap;
|
||||
}
|
||||
|
||||
public void setBootstrap(boolean bootstrap) {
|
||||
this.bootstrap = bootstrap;
|
||||
}
|
||||
|
||||
public String getPrefix() {
|
||||
return this.prefix;
|
||||
}
|
||||
|
||||
public void setPrefix(String prefix) {
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
public Map<String, String> getOverrides() {
|
||||
return this.overrides;
|
||||
}
|
||||
|
||||
public void setOverrides(Map<String, String> overrides) {
|
||||
this.overrides = overrides;
|
||||
}
|
||||
|
||||
public boolean isStripDocumentFromYaml() {
|
||||
return this.stripDocumentFromYaml;
|
||||
}
|
||||
|
||||
public void setStripDocumentFromYaml(boolean stripDocumentFromYaml) {
|
||||
this.stripDocumentFromYaml = stripDocumentFromYaml;
|
||||
}
|
||||
|
||||
public boolean isAcceptEmpty() {
|
||||
return this.acceptEmpty;
|
||||
}
|
||||
|
||||
public void setAcceptEmpty(boolean acceptEmpty) {
|
||||
this.acceptEmpty = acceptEmpty;
|
||||
}
|
||||
|
||||
public String getDefaultApplicationName() {
|
||||
return this.defaultApplicationName;
|
||||
}
|
||||
|
||||
public void setDefaultApplicationName(String defaultApplicationName) {
|
||||
this.defaultApplicationName = defaultApplicationName;
|
||||
}
|
||||
|
||||
public String getDefaultProfile() {
|
||||
return this.defaultProfile;
|
||||
}
|
||||
|
||||
public void setDefaultProfile(String defaultProfile) {
|
||||
this.defaultProfile = defaultProfile;
|
||||
}
|
||||
|
||||
public boolean isFailOnCompositeError() {
|
||||
return failOnCompositeError;
|
||||
}
|
||||
|
||||
public void setFailOnCompositeError(boolean failOnCompositeError) {
|
||||
this.failOnCompositeError = failOnCompositeError;
|
||||
}
|
||||
|
||||
public boolean isReverseLocationOrder() {
|
||||
return reverseLocationOrder;
|
||||
}
|
||||
|
||||
public void setReverseLocationOrder(boolean reverseLocationOrder) {
|
||||
this.reverseLocationOrder = reverseLocationOrder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("enabled", enabled).append("bootstrap", bootstrap)
|
||||
.append("prefix", prefix).append("defaultLabel", defaultLabel).append("overrides", overrides)
|
||||
.append("stripDocumentFromYaml", stripDocumentFromYaml).append("acceptEmpty", acceptEmpty)
|
||||
.append("defaultApplicationName", defaultApplicationName).append("defaultProfile", defaultProfile)
|
||||
.append("failOnCompositeError", failOnCompositeError).append("encrypt", encrypt)
|
||||
.append("reverseLocationOrder", reverseLocationOrder).toString();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Encryption properties.
|
||||
*/
|
||||
public static class Encrypt {
|
||||
|
||||
/**
|
||||
* Enable decryption of environment properties before sending to client.
|
||||
*/
|
||||
private boolean enabled = true;
|
||||
|
||||
/**
|
||||
* Enable decryption of environment properties served by plain text endpoint
|
||||
* {@link org.springframework.cloud.config.server.resource.ResourceController}.
|
||||
*/
|
||||
private boolean plainTextEncrypt = false;
|
||||
|
||||
public boolean isEnabled() {
|
||||
return this.enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public boolean isPlainTextEncrypt() {
|
||||
return plainTextEncrypt;
|
||||
}
|
||||
|
||||
public void setPlainTextEncrypt(boolean plainTextEncrypt) {
|
||||
this.plainTextEncrypt = plainTextEncrypt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("enabled", enabled).append("plainTextEncrypt", plainTextEncrypt)
|
||||
.toString();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2019 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.config;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.credhub.core.CredHubProperties;
|
||||
import org.springframework.credhub.support.ClientOptions;
|
||||
|
||||
/**
|
||||
* @author Alberto C. Ríos
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Profile("credhub")
|
||||
public class CredhubConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "spring.cloud.config.server.credhub")
|
||||
public CredHubProperties credHubProperties() {
|
||||
return new CredHubProperties();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "spring.cloud.config.server.credhub")
|
||||
public ClientOptions clientOptions() {
|
||||
return new ClientOptions();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2022 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.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.bootstrap.encrypt.KeyProperties;
|
||||
import org.springframework.cloud.config.server.encryption.LocatorTextEncryptor;
|
||||
import org.springframework.cloud.config.server.encryption.TextEncryptorLocator;
|
||||
import org.springframework.cloud.context.encrypt.EncryptorFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.crypto.encrypt.Encryptors;
|
||||
import org.springframework.security.crypto.encrypt.TextEncryptor;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Default text encryption auto-configuration.
|
||||
*
|
||||
* @author Olga Maciaszek-Sharma
|
||||
* @since 4.0.0
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@AutoConfigureAfter(RsaEncryptionAutoConfiguration.class)
|
||||
@EnableConfigurationProperties
|
||||
public class DefaultTextEncryptionAutoConfiguration {
|
||||
|
||||
@Autowired
|
||||
ApplicationContext context;
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public KeyProperties keyProperties() {
|
||||
return new KeyProperties();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(TextEncryptor.class)
|
||||
@ConditionalOnBean(TextEncryptorLocator.class)
|
||||
public TextEncryptor defaultLocatorBasedTextEncryptor(TextEncryptorLocator locator) {
|
||||
return new LocatorTextEncryptor(locator);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(TextEncryptor.class)
|
||||
public TextEncryptor defaultTextEncryptor(KeyProperties key) {
|
||||
if (StringUtils.hasText(key.getKey())) {
|
||||
return new EncryptorFactory(key.getSalt()).create(key.getKey());
|
||||
}
|
||||
return Encryptors.noOpText();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2019 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.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.cloud.config.server.encryption.CipherEnvironmentEncryptor;
|
||||
import org.springframework.cloud.config.server.encryption.EnvironmentEncryptor;
|
||||
import org.springframework.cloud.config.server.encryption.SingleTextEncryptorLocator;
|
||||
import org.springframework.cloud.config.server.encryption.TextEncryptorLocator;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.crypto.encrypt.TextEncryptor;
|
||||
|
||||
/**
|
||||
* Autoconfiguration for text encryptors and environment encryptors (non-web stuff). Users
|
||||
* can provide beans of the same type as any or all of the beans defined here in
|
||||
* application code to override the default behaviour.
|
||||
*
|
||||
* @author Bartosz Wojtkiewicz
|
||||
* @author Rafal Zukowski
|
||||
* @author Dave Syer
|
||||
* @author Olga Maciaszek-Sharma
|
||||
*
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@AutoConfigureAfter(DefaultTextEncryptionAutoConfiguration.class)
|
||||
public class EncryptionAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(TextEncryptor.class)
|
||||
@ConditionalOnMissingBean(TextEncryptorLocator.class)
|
||||
public SingleTextEncryptorLocator singleTextEncryptorLocator(TextEncryptor encryptor) {
|
||||
return new SingleTextEncryptorLocator(encryptor);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnProperty(value = "spring.cloud.config.server.encrypt.enabled", matchIfMissing = true)
|
||||
@ConditionalOnBean(TextEncryptorLocator.class)
|
||||
public EnvironmentEncryptor environmentEncryptor(@Autowired(required = false) TextEncryptorLocator locator,
|
||||
TextEncryptor encryptor) {
|
||||
if (locator == null) {
|
||||
locator = new SingleTextEncryptorLocator(encryptor);
|
||||
}
|
||||
return new CipherEnvironmentEncryptor(locator);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,572 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2020 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.config;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import com.google.cloud.secretmanager.v1.SecretManagerServiceClient;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.eclipse.jgit.api.TransportConfigCallback;
|
||||
import org.tmatesoft.svn.core.SVNException;
|
||||
import software.amazon.awssdk.services.s3.S3Client;
|
||||
import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient;
|
||||
import software.amazon.awssdk.services.ssm.SsmClient;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.config.server.composite.CompositeEnvironmentBeanFactoryPostProcessor;
|
||||
import org.springframework.cloud.config.server.composite.ConditionalOnMissingSearchPathLocator;
|
||||
import org.springframework.cloud.config.server.composite.ConditionalOnSearchPathLocator;
|
||||
import org.springframework.cloud.config.server.environment.AwsParameterStoreEnvironmentProperties;
|
||||
import org.springframework.cloud.config.server.environment.AwsParameterStoreEnvironmentRepository;
|
||||
import org.springframework.cloud.config.server.environment.AwsParameterStoreEnvironmentRepositoryFactory;
|
||||
import org.springframework.cloud.config.server.environment.AwsS3EnvironmentProperties;
|
||||
import org.springframework.cloud.config.server.environment.AwsS3EnvironmentRepository;
|
||||
import org.springframework.cloud.config.server.environment.AwsS3EnvironmentRepositoryFactory;
|
||||
import org.springframework.cloud.config.server.environment.AwsSecretsManagerEnvironmentProperties;
|
||||
import org.springframework.cloud.config.server.environment.AwsSecretsManagerEnvironmentRepository;
|
||||
import org.springframework.cloud.config.server.environment.AwsSecretsManagerEnvironmentRepositoryFactory;
|
||||
import org.springframework.cloud.config.server.environment.CompositeEnvironmentRepository;
|
||||
import org.springframework.cloud.config.server.environment.ConfigTokenProvider;
|
||||
import org.springframework.cloud.config.server.environment.ConfigurableHttpConnectionFactory;
|
||||
import org.springframework.cloud.config.server.environment.ConsulEnvironmentWatch;
|
||||
import org.springframework.cloud.config.server.environment.CredhubEnvironmentProperties;
|
||||
import org.springframework.cloud.config.server.environment.CredhubEnvironmentRepository;
|
||||
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.GoogleSecretManagerEnvironmentProperties;
|
||||
import org.springframework.cloud.config.server.environment.GoogleSecretManagerEnvironmentRepository;
|
||||
import org.springframework.cloud.config.server.environment.GoogleSecretManagerEnvironmentRepositoryFactory;
|
||||
import org.springframework.cloud.config.server.environment.HttpClientConfigurableHttpConnectionFactory;
|
||||
import org.springframework.cloud.config.server.environment.HttpClientVaultRestTemplateFactory;
|
||||
import org.springframework.cloud.config.server.environment.HttpRequestConfigTokenProvider;
|
||||
import org.springframework.cloud.config.server.environment.JdbcEnvironmentProperties;
|
||||
import org.springframework.cloud.config.server.environment.JdbcEnvironmentRepository;
|
||||
import org.springframework.cloud.config.server.environment.JdbcEnvironmentRepositoryFactory;
|
||||
import org.springframework.cloud.config.server.environment.MultipleJGitEnvironmentProperties;
|
||||
import org.springframework.cloud.config.server.environment.MultipleJGitEnvironmentRepository;
|
||||
import org.springframework.cloud.config.server.environment.MultipleJGitEnvironmentRepositoryFactory;
|
||||
import org.springframework.cloud.config.server.environment.NativeEnvironmentProperties;
|
||||
import org.springframework.cloud.config.server.environment.NativeEnvironmentRepository;
|
||||
import org.springframework.cloud.config.server.environment.NativeEnvironmentRepositoryFactory;
|
||||
import org.springframework.cloud.config.server.environment.RedisEnvironmentProperties;
|
||||
import org.springframework.cloud.config.server.environment.RedisEnvironmentRepository;
|
||||
import org.springframework.cloud.config.server.environment.RedisEnvironmentRepositoryFactory;
|
||||
import org.springframework.cloud.config.server.environment.SearchPathCompositeEnvironmentRepository;
|
||||
import org.springframework.cloud.config.server.environment.SvnEnvironmentRepositoryFactory;
|
||||
import org.springframework.cloud.config.server.environment.SvnKitEnvironmentProperties;
|
||||
import org.springframework.cloud.config.server.environment.SvnKitEnvironmentRepository;
|
||||
import org.springframework.cloud.config.server.environment.VaultEnvironmentProperties;
|
||||
import org.springframework.cloud.config.server.environment.VaultEnvironmentRepository;
|
||||
import org.springframework.cloud.config.server.environment.VaultEnvironmentRepositoryFactory;
|
||||
import org.springframework.cloud.config.server.environment.vault.SpringVaultClientConfiguration;
|
||||
import org.springframework.cloud.config.server.environment.vault.SpringVaultEnvironmentRepository;
|
||||
import org.springframework.cloud.config.server.environment.vault.SpringVaultEnvironmentRepositoryFactory;
|
||||
import org.springframework.cloud.config.server.support.GitCredentialsProviderFactory;
|
||||
import org.springframework.cloud.config.server.support.GoogleCloudSourceSupport;
|
||||
import org.springframework.cloud.config.server.support.TransportConfigCallbackFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.credhub.core.CredHubOperations;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.vault.core.VaultTemplate;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
* @author Ryan Baxter
|
||||
* @author Daniel Lavoie
|
||||
* @author Dylan Roberts
|
||||
* @author Alberto C. Ríos
|
||||
* @author Scott Frederick
|
||||
* @author Tejas Pandilwar
|
||||
* @author Iulian Antohe
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableConfigurationProperties({ SvnKitEnvironmentProperties.class, CredhubEnvironmentProperties.class,
|
||||
JdbcEnvironmentProperties.class, NativeEnvironmentProperties.class, VaultEnvironmentProperties.class,
|
||||
RedisEnvironmentProperties.class, AwsS3EnvironmentProperties.class,
|
||||
AwsSecretsManagerEnvironmentProperties.class, AwsParameterStoreEnvironmentProperties.class,
|
||||
GoogleSecretManagerEnvironmentProperties.class })
|
||||
@Import({ CompositeRepositoryConfiguration.class, JdbcRepositoryConfiguration.class, VaultConfiguration.class,
|
||||
VaultRepositoryConfiguration.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,
|
||||
// DefaultRepositoryConfiguration must be last
|
||||
DefaultRepositoryConfiguration.class })
|
||||
public class EnvironmentRepositoryConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(search = SearchStrategy.CURRENT)
|
||||
public MultipleJGitEnvironmentProperties multipleJGitEnvironmentProperties() {
|
||||
return new MultipleJGitEnvironmentProperties();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(ConfigTokenProvider.class)
|
||||
public ConfigTokenProvider defaultConfigTokenProvider(ObjectProvider<HttpServletRequest> httpRequest) {
|
||||
return new HttpRequestConfigTokenProvider(httpRequest);
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(AbstractHealthIndicator.class)
|
||||
@ConditionalOnProperty(value = "spring.cloud.config.server.health.enabled", matchIfMissing = true)
|
||||
protected static class ConfigServerActuatorConfiguration {
|
||||
|
||||
@Bean
|
||||
public ConfigServerHealthIndicator configServerHealthIndicator(EnvironmentRepository repository) {
|
||||
return new ConfigServerHealthIndicator(repository);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnProperty("spring.cloud.config.server.consul.watch.enabled")
|
||||
protected static class ConsulEnvironmentWatchConfiguration {
|
||||
|
||||
@Bean
|
||||
public EnvironmentWatch environmentWatch() {
|
||||
return new ConsulEnvironmentWatch();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnMissingBean(EnvironmentWatch.class)
|
||||
protected static class DefaultEnvironmentWatch {
|
||||
|
||||
@Bean
|
||||
public EnvironmentWatch environmentWatch() {
|
||||
return new EnvironmentWatch.Default();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(TransportConfigCallback.class)
|
||||
static class JGitFactoryConfig {
|
||||
|
||||
@Bean
|
||||
public MultipleJGitEnvironmentRepositoryFactory gitEnvironmentRepositoryFactory(
|
||||
ConfigurableEnvironment environment, ConfigServerProperties server,
|
||||
Optional<ConfigurableHttpConnectionFactory> jgitHttpConnectionFactory,
|
||||
Optional<TransportConfigCallback> customTransportConfigCallback,
|
||||
Optional<GoogleCloudSourceSupport> googleCloudSourceSupport,
|
||||
GitCredentialsProviderFactory gitCredentialsProviderFactory) {
|
||||
final TransportConfigCallbackFactory transportConfigCallbackFactory = new TransportConfigCallbackFactory(
|
||||
customTransportConfigCallback.orElse(null), googleCloudSourceSupport.orElse(null));
|
||||
return new MultipleJGitEnvironmentRepositoryFactory(environment, server, jgitHttpConnectionFactory,
|
||||
transportConfigCallbackFactory, gitCredentialsProviderFactory);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public GitCredentialsProviderFactory gitCredentialsProviderFactory() {
|
||||
return new GitCredentialsProviderFactory();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass({ HttpClient.class, TransportConfigCallback.class })
|
||||
static class JGitHttpClientConfig {
|
||||
|
||||
@Bean
|
||||
public ConfigurableHttpConnectionFactory httpClientConnectionFactory() {
|
||||
return new HttpClientConfigurableHttpConnectionFactory();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(S3Client.class)
|
||||
static class AwsS3FactoryConfig {
|
||||
|
||||
@Bean
|
||||
public AwsS3EnvironmentRepositoryFactory awsS3EnvironmentRepositoryFactory(ConfigServerProperties server) {
|
||||
return new AwsS3EnvironmentRepositoryFactory(server);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(SecretsManagerClient.class)
|
||||
static class AwsSecretsManagerFactoryConfig {
|
||||
|
||||
@Bean
|
||||
public AwsSecretsManagerEnvironmentRepositoryFactory awsSecretsManagerEnvironmentRepositoryFactory(
|
||||
ConfigServerProperties configServerProperties) {
|
||||
return new AwsSecretsManagerEnvironmentRepositoryFactory(configServerProperties);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(SsmClient.class)
|
||||
static class AwsParameterStoreFactoryConfig {
|
||||
|
||||
@Bean
|
||||
public AwsParameterStoreEnvironmentRepositoryFactory awsParameterStoreEnvironmentRepositoryFactory(
|
||||
ConfigServerProperties server) {
|
||||
return new AwsParameterStoreEnvironmentRepositoryFactory(server);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(SVNException.class)
|
||||
static class SvnFactoryConfig {
|
||||
|
||||
@Bean
|
||||
public SvnEnvironmentRepositoryFactory svnEnvironmentRepositoryFactory(ConfigurableEnvironment environment,
|
||||
ConfigServerProperties server, ObjectProvider<ObservationRegistry> observationRegistry) {
|
||||
return new SvnEnvironmentRepositoryFactory(environment, server,
|
||||
observationRegistry.getIfAvailable(() -> ObservationRegistry.NOOP));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnMissingClass("org.springframework.vault.core.VaultTemplate")
|
||||
@SuppressWarnings("deprecation")
|
||||
static class VaultFactoryConfig {
|
||||
|
||||
@Bean
|
||||
public VaultEnvironmentRepositoryFactory vaultEnvironmentRepositoryFactory(
|
||||
ObjectProvider<HttpServletRequest> request, EnvironmentWatch watch,
|
||||
Optional<VaultEnvironmentRepositoryFactory.VaultRestTemplateFactory> vaultRestTemplateFactory,
|
||||
ConfigTokenProvider tokenProvider) {
|
||||
return new VaultEnvironmentRepositoryFactory(request, watch, vaultRestTemplateFactory, tokenProvider);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(SecretManagerServiceClient.class)
|
||||
static class GoogleSecretManagerFactoryConfig {
|
||||
|
||||
@Bean
|
||||
public GoogleSecretManagerEnvironmentRepositoryFactory googleSecretManagerEnvironmentRepositoryFactory(
|
||||
ObjectProvider<HttpServletRequest> request) {
|
||||
return new GoogleSecretManagerEnvironmentRepositoryFactory(request);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(HttpClient.class)
|
||||
@ConditionalOnMissingClass("org.springframework.vault.core.VaultTemplate")
|
||||
@SuppressWarnings("deprecation")
|
||||
static class VaultHttpClientConfig {
|
||||
|
||||
@Bean
|
||||
public VaultEnvironmentRepositoryFactory.VaultRestTemplateFactory vaultRestTemplateFactory() {
|
||||
return new HttpClientVaultRestTemplateFactory();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(VaultTemplate.class)
|
||||
@Import(SpringVaultClientConfiguration.class)
|
||||
static class SpringVaultFactoryConfig {
|
||||
|
||||
@Bean
|
||||
public SpringVaultEnvironmentRepositoryFactory vaultEnvironmentRepositoryFactory(
|
||||
ObjectProvider<HttpServletRequest> request, EnvironmentWatch watch,
|
||||
SpringVaultClientConfiguration vaultClientConfiguration) {
|
||||
return new SpringVaultEnvironmentRepositoryFactory(request, watch, vaultClientConfiguration);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(JdbcTemplate.class)
|
||||
@ConditionalOnProperty(value = "spring.cloud.config.server.jdbc.enabled", matchIfMissing = true)
|
||||
static class JdbcFactoryConfig {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(JdbcTemplate.class)
|
||||
public JdbcEnvironmentRepositoryFactory jdbcEnvironmentRepositoryFactory(JdbcTemplate jdbc,
|
||||
JdbcEnvironmentRepository.PropertiesResultSetExtractor propertiesResultSetExtractor) {
|
||||
return new JdbcEnvironmentRepositoryFactory(jdbc, propertiesResultSetExtractor);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(JdbcEnvironmentRepository.PropertiesResultSetExtractor.class)
|
||||
public JdbcEnvironmentRepository.PropertiesResultSetExtractor propertiesResultSetExtractor() {
|
||||
return new JdbcEnvironmentRepository.PropertiesResultSetExtractor();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(StringRedisTemplate.class)
|
||||
static class RedisFactoryConfig {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(StringRedisTemplate.class)
|
||||
public RedisEnvironmentRepositoryFactory redisEnvironmentRepositoryFactory(StringRedisTemplate redis) {
|
||||
return new RedisEnvironmentRepositoryFactory(redis);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(CredHubOperations.class)
|
||||
static class CredhubFactoryConfig {
|
||||
|
||||
@Bean
|
||||
public CredhubEnvironmentRepositoryFactory credhubEnvironmentRepositoryFactory(
|
||||
Optional<CredHubOperations> credHubOperations) {
|
||||
return new CredhubEnvironmentRepositoryFactory(credHubOperations.orElse(null));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class NativeFactoryConfig {
|
||||
|
||||
@Bean
|
||||
public NativeEnvironmentRepositoryFactory nativeEnvironmentRepositoryFactory(
|
||||
ConfigurableEnvironment environment, ConfigServerProperties properties,
|
||||
ObjectProvider<ObservationRegistry> observationRegistry) {
|
||||
return new NativeEnvironmentRepositoryFactory(environment, properties,
|
||||
observationRegistry.getIfAvailable(() -> ObservationRegistry.NOOP));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnMissingBean(value = EnvironmentRepository.class, search = SearchStrategy.CURRENT)
|
||||
class DefaultRepositoryConfiguration {
|
||||
|
||||
@Bean
|
||||
public MultipleJGitEnvironmentRepository defaultEnvironmentRepository(
|
||||
MultipleJGitEnvironmentRepositoryFactory gitEnvironmentRepositoryFactory,
|
||||
MultipleJGitEnvironmentProperties environmentProperties) throws Exception {
|
||||
return gitEnvironmentRepositoryFactory.build(environmentProperties);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Profile("native")
|
||||
class NativeRepositoryConfiguration {
|
||||
|
||||
@Bean
|
||||
public NativeEnvironmentRepository nativeEnvironmentRepository(NativeEnvironmentRepositoryFactory factory,
|
||||
NativeEnvironmentProperties environmentProperties) {
|
||||
return factory.build(environmentProperties);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Profile("git")
|
||||
class GitRepositoryConfiguration extends DefaultRepositoryConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Profile("awss3")
|
||||
class AwsS3RepositoryConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(AwsS3EnvironmentRepository.class)
|
||||
public AwsS3EnvironmentRepository awsS3EnvironmentRepository(AwsS3EnvironmentRepositoryFactory factory,
|
||||
AwsS3EnvironmentProperties environmentProperties) {
|
||||
return factory.build(environmentProperties);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Profile("awsparamstore")
|
||||
class AwsParameterStoreRepositoryConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(AwsParameterStoreEnvironmentRepository.class)
|
||||
public AwsParameterStoreEnvironmentRepository awsParameterStoreEnvironmentRepository(
|
||||
AwsParameterStoreEnvironmentRepositoryFactory factory,
|
||||
AwsParameterStoreEnvironmentProperties environmentProperties) {
|
||||
return factory.build(environmentProperties);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Profile("awssecretsmanager")
|
||||
class AwsSecretsManagerRepositoryConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(AwsSecretsManagerEnvironmentRepository.class)
|
||||
public AwsSecretsManagerEnvironmentRepository awsSecretsManagerEnvironmentRepository(
|
||||
AwsSecretsManagerEnvironmentRepositoryFactory factory,
|
||||
AwsSecretsManagerEnvironmentProperties environmentProperties) {
|
||||
return factory.build(environmentProperties);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Profile("subversion")
|
||||
class SvnRepositoryConfiguration {
|
||||
|
||||
@Bean
|
||||
public SvnKitEnvironmentRepository svnKitEnvironmentRepository(SvnEnvironmentRepositoryFactory factory,
|
||||
SvnKitEnvironmentProperties environmentProperties) {
|
||||
return factory.build(environmentProperties);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnMissingClass("org.springframework.vault.core.VaultTemplate")
|
||||
@Profile("vault")
|
||||
@SuppressWarnings("deprecation")
|
||||
class VaultRepositoryConfiguration {
|
||||
|
||||
@Bean
|
||||
public VaultEnvironmentRepository vaultEnvironmentRepository(VaultEnvironmentRepositoryFactory factory,
|
||||
VaultEnvironmentProperties environmentProperties) throws Exception {
|
||||
return factory.build(environmentProperties);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(VaultTemplate.class)
|
||||
@Profile("vault")
|
||||
class SpringVaultRepositoryConfiguration {
|
||||
|
||||
@Bean
|
||||
public SpringVaultEnvironmentRepository vaultEnvironmentRepository(SpringVaultEnvironmentRepositoryFactory factory,
|
||||
VaultEnvironmentProperties environmentProperties) {
|
||||
return factory.build(environmentProperties);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Profile("credhub")
|
||||
class CredhubRepositoryConfiguration {
|
||||
|
||||
@Bean
|
||||
public CredhubEnvironmentRepository credhubEnvironmentRepository(CredhubEnvironmentRepositoryFactory factory,
|
||||
CredhubEnvironmentProperties environmentProperties) {
|
||||
return factory.build(environmentProperties);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Profile("jdbc")
|
||||
@ConditionalOnClass(JdbcTemplate.class)
|
||||
@ConditionalOnProperty(value = "spring.cloud.config.server.jdbc.enabled", matchIfMissing = true)
|
||||
class JdbcRepositoryConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(JdbcTemplate.class)
|
||||
public JdbcEnvironmentRepository jdbcEnvironmentRepository(JdbcEnvironmentRepositoryFactory factory,
|
||||
JdbcEnvironmentProperties environmentProperties) {
|
||||
return factory.build(environmentProperties);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Profile("redis")
|
||||
@ConditionalOnClass(StringRedisTemplate.class)
|
||||
class RedisRepositoryConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(StringRedisTemplate.class)
|
||||
public RedisEnvironmentRepository redisEnvironmentRepository(RedisEnvironmentRepositoryFactory factory,
|
||||
RedisEnvironmentProperties environmentProperties) {
|
||||
return factory.build(environmentProperties);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Profile("composite")
|
||||
class CompositeRepositoryConfiguration {
|
||||
|
||||
@Bean
|
||||
public static CompositeEnvironmentBeanFactoryPostProcessor compositeEnvironmentRepositoryBeanFactoryPostProcessor(
|
||||
Environment environment) {
|
||||
return new CompositeEnvironmentBeanFactoryPostProcessor(environment);
|
||||
}
|
||||
|
||||
@Primary
|
||||
@Bean
|
||||
@ConditionalOnSearchPathLocator
|
||||
public SearchPathCompositeEnvironmentRepository searchPathCompositeEnvironmentRepository(
|
||||
List<EnvironmentRepository> environmentRepositories, ConfigServerProperties properties,
|
||||
ObjectProvider<ObservationRegistry> observationRegistry) {
|
||||
return new SearchPathCompositeEnvironmentRepository(environmentRepositories,
|
||||
observationRegistry.getIfAvailable(() -> ObservationRegistry.NOOP),
|
||||
properties.isFailOnCompositeError());
|
||||
}
|
||||
|
||||
@Primary
|
||||
@Bean
|
||||
@ConditionalOnMissingSearchPathLocator
|
||||
public CompositeEnvironmentRepository compositeEnvironmentRepository(
|
||||
List<EnvironmentRepository> environmentRepositories, ConfigServerProperties properties,
|
||||
ObjectProvider<ObservationRegistry> observationRegistry) {
|
||||
return new CompositeEnvironmentRepository(environmentRepositories,
|
||||
observationRegistry.getIfAvailable(() -> ObservationRegistry.NOOP),
|
||||
properties.isFailOnCompositeError());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Profile("secret-manager")
|
||||
@ConditionalOnClass(SecretManagerServiceClient.class)
|
||||
class GoogleSecretManagerRepositoryConfiguration {
|
||||
|
||||
@Bean
|
||||
public GoogleSecretManagerEnvironmentRepository googleSecretManagerEnvironmentRepository(
|
||||
GoogleSecretManagerEnvironmentRepositoryFactory factory,
|
||||
GoogleSecretManagerEnvironmentProperties environmentProperties) throws Exception {
|
||||
return factory.build(environmentProperties);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2019 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.config;
|
||||
|
||||
import com.google.auth.oauth2.GoogleCredentials;
|
||||
import org.eclipse.jgit.api.TransportConfigCallback;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.cloud.config.server.support.GoogleCloudSourceSupport;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Adds Google Cloud Source OAuth2 support, if
|
||||
* com.google.auth:google-auth-library-oauth2-http library is on classpath.
|
||||
*
|
||||
* @author Eduard Wirch
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass({ GoogleCredentials.class, TransportConfigCallback.class })
|
||||
public class GoogleCloudSourceConfiguration {
|
||||
|
||||
@Bean
|
||||
public GoogleCloudSourceSupport createGoogleCloudSourceSupport() {
|
||||
return new GoogleCloudSourceSupport();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2019 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.config;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
|
||||
import org.springframework.cloud.config.server.encryption.CipherResourceJsonEncryptor;
|
||||
import org.springframework.cloud.config.server.encryption.CipherResourcePropertiesEncryptor;
|
||||
import org.springframework.cloud.config.server.encryption.CipherResourceYamlEncryptor;
|
||||
import org.springframework.cloud.config.server.encryption.ResourceEncryptor;
|
||||
import org.springframework.cloud.config.server.encryption.TextEncryptorLocator;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Adds configuration to decrypt plain text files served through
|
||||
* {@link org.springframework.cloud.config.server.resource.ResourceController}. Each
|
||||
* supported extension is added as a key with its associated @{link
|
||||
* org.springframework.cloud.config.server.encryption.ResourceEncryptor} implementation as
|
||||
* a value.
|
||||
*
|
||||
* @author Sean Stiglitz
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnExpression("${spring.cloud.config.server.encrypt.enabled:true} && ${spring.cloud.config.server.encrypt.plainTextEncrypt:true}")
|
||||
@ConditionalOnBean(TextEncryptorLocator.class)
|
||||
public class ResourceEncryptorConfiguration {
|
||||
|
||||
@Autowired
|
||||
private TextEncryptorLocator encryptor;
|
||||
|
||||
@Bean
|
||||
Map<String, ResourceEncryptor> resourceEncryptors() {
|
||||
Map<String, ResourceEncryptor> resourceEncryptorMap = new HashMap<>();
|
||||
addSupportedExtensionsToMap(resourceEncryptorMap, new CipherResourceJsonEncryptor(encryptor));
|
||||
addSupportedExtensionsToMap(resourceEncryptorMap, new CipherResourcePropertiesEncryptor(encryptor));
|
||||
addSupportedExtensionsToMap(resourceEncryptorMap, new CipherResourceYamlEncryptor(encryptor));
|
||||
return resourceEncryptorMap;
|
||||
}
|
||||
|
||||
private void addSupportedExtensionsToMap(Map<String, ResourceEncryptor> resourceEncryptorMap,
|
||||
ResourceEncryptor resourceEncryptor) {
|
||||
for (String ext : resourceEncryptor.getSupportedExtensions()) {
|
||||
resourceEncryptorMap.put(ext, resourceEncryptor);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2019 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.config;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.cloud.config.server.environment.SearchPathLocator;
|
||||
import org.springframework.cloud.config.server.resource.GenericResourceRepository;
|
||||
import org.springframework.cloud.config.server.resource.ResourceRepository;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
* @author Tim Ysewyn
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnMissingBean(ResourceRepository.class)
|
||||
public class ResourceRepositoryConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(SearchPathLocator.class)
|
||||
public ResourceRepository resourceRepository(SearchPathLocator service, ConfigServerProperties properties) {
|
||||
return new GenericResourceRepository(service, properties);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2022 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.config;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.bootstrap.encrypt.KeyProperties;
|
||||
import org.springframework.cloud.bootstrap.encrypt.RsaProperties;
|
||||
import org.springframework.cloud.config.server.encryption.KeyStoreTextEncryptorLocator;
|
||||
import org.springframework.cloud.config.server.encryption.TextEncryptorLocator;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.rsa.crypto.KeyStoreKeyFactory;
|
||||
import org.springframework.security.rsa.crypto.RsaAlgorithm;
|
||||
import org.springframework.security.rsa.crypto.RsaSecretEncryptor;
|
||||
|
||||
/**
|
||||
* Autoconfiguration for RSA encryption.
|
||||
*
|
||||
* @author Olga Maciaszek-Sharma
|
||||
* @since 4.0.0
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnProperty(prefix = "encrypt.key-store", value = "location")
|
||||
@ConditionalOnClass(RsaSecretEncryptor.class)
|
||||
@EnableConfigurationProperties
|
||||
public class RsaEncryptionAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public KeyProperties keyProperties() {
|
||||
return new KeyProperties();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public TextEncryptorLocator textEncryptorLocator(KeyProperties key, RsaProperties rsaProperties) {
|
||||
KeyProperties.KeyStore keyStore = key.getKeyStore();
|
||||
KeyStoreTextEncryptorLocator locator = new KeyStoreTextEncryptorLocator(
|
||||
new KeyStoreKeyFactory(keyStore.getLocation(), keyStore.getPassword().toCharArray(),
|
||||
key.getKeyStore().getType()),
|
||||
keyStore.getSecret(), keyStore.getAlias());
|
||||
RsaAlgorithm algorithm = rsaProperties.getAlgorithm();
|
||||
locator.setRsaAlgorithm(algorithm);
|
||||
locator.setSalt(rsaProperties.getSalt());
|
||||
locator.setStrong(rsaProperties.isStrong());
|
||||
return locator;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2019 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.config;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.cloud.config.server.environment.ConfigTokenProvider;
|
||||
import org.springframework.cloud.config.server.environment.EnvironmentConfigTokenProvider;
|
||||
import org.springframework.cloud.config.server.environment.vault.authentication.AppRoleClientAuthenticationProvider;
|
||||
import org.springframework.cloud.config.server.environment.vault.authentication.AwsEc2ClientAuthenticationProvider;
|
||||
import org.springframework.cloud.config.server.environment.vault.authentication.AwsIamClientAuthenticationProvider;
|
||||
import org.springframework.cloud.config.server.environment.vault.authentication.AzureMsiClientAuthenticationProvider;
|
||||
import org.springframework.cloud.config.server.environment.vault.authentication.CertificateClientAuthenticationProvider;
|
||||
import org.springframework.cloud.config.server.environment.vault.authentication.CubbyholeClientAuthenticationProvider;
|
||||
import org.springframework.cloud.config.server.environment.vault.authentication.GcpGceClientAuthenticationProvider;
|
||||
import org.springframework.cloud.config.server.environment.vault.authentication.GcpIamClientAuthenticationProvider;
|
||||
import org.springframework.cloud.config.server.environment.vault.authentication.KubernetesClientAuthenticationProvider;
|
||||
import org.springframework.cloud.config.server.environment.vault.authentication.PcfClientAuthenticationProvider;
|
||||
import org.springframework.cloud.config.server.environment.vault.authentication.TokenClientAuthenticationProvider;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.vault.core.VaultTemplate;
|
||||
|
||||
/**
|
||||
* @author Scott Frederick
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class VaultConfiguration {
|
||||
|
||||
private static final String VAULT_TOKEN_PROPERTY_NAME = "spring.cloud.config.server.vault.token";
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(VAULT_TOKEN_PROPERTY_NAME)
|
||||
public ConfigTokenProvider vaultConfigTokenProvider(Environment environment) {
|
||||
return new EnvironmentConfigTokenProvider(environment, VAULT_TOKEN_PROPERTY_NAME);
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(VaultTemplate.class)
|
||||
public static class VaultClientAuthenticationProviderConfiguration {
|
||||
|
||||
@Bean
|
||||
public AppRoleClientAuthenticationProvider appRoleClientAuthenticationProvider() {
|
||||
return new AppRoleClientAuthenticationProvider();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AwsEc2ClientAuthenticationProvider awsEc2ClientAuthenticationProvider() {
|
||||
return new AwsEc2ClientAuthenticationProvider();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AwsIamClientAuthenticationProvider awsIamClientAuthenticationProvider() {
|
||||
return new AwsIamClientAuthenticationProvider();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AzureMsiClientAuthenticationProvider azureMsiClientAuthenticationProvider() {
|
||||
return new AzureMsiClientAuthenticationProvider();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CertificateClientAuthenticationProvider certificateClientAuthenticationProvider() {
|
||||
return new CertificateClientAuthenticationProvider();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CubbyholeClientAuthenticationProvider cubbyholeClientAuthenticationProvider() {
|
||||
return new CubbyholeClientAuthenticationProvider();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GcpGceClientAuthenticationProvider gcpGceClientAuthenticationProvider() {
|
||||
return new GcpGceClientAuthenticationProvider();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GcpIamClientAuthenticationProvider gcpIamClientAuthenticationProvider() {
|
||||
return new GcpIamClientAuthenticationProvider();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public KubernetesClientAuthenticationProvider kubernetesClientAuthenticationProvider() {
|
||||
return new KubernetesClientAuthenticationProvider();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PcfClientAuthenticationProvider pcfClientAuthenticationProvider() {
|
||||
return new PcfClientAuthenticationProvider();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TokenClientAuthenticationProvider tokenClientAuthenticationProvider() {
|
||||
return new TokenClientAuthenticationProvider();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020-2020 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.config;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.cloud.config.server.encryption.vault.VaultEnvironmentEncryptor;
|
||||
import org.springframework.cloud.config.server.environment.vault.SpringVaultEnvironmentRepository;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.vault.core.VaultTemplate;
|
||||
|
||||
/**
|
||||
* Auto configuration for vault encryptor.
|
||||
*
|
||||
* @author Alexey Zhokhov
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(VaultTemplate.class)
|
||||
@Profile("vault")
|
||||
public class VaultEncryptionAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public VaultEnvironmentEncryptor vaultEnvironmentEncryptor(
|
||||
SpringVaultEnvironmentRepository vaultEnvironmentRepository) {
|
||||
return new VaultEnvironmentEncryptor(vaultEnvironmentRepository.getKeyValueTemplate());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2019 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.diagnostics;
|
||||
|
||||
import org.springframework.boot.diagnostics.AbstractFailureAnalyzer;
|
||||
import org.springframework.boot.diagnostics.FailureAnalysis;
|
||||
import org.springframework.cloud.config.server.environment.JGitEnvironmentRepository;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class GitUriFailureAnalyzer extends AbstractFailureAnalyzer<IllegalStateException> {
|
||||
|
||||
/**
|
||||
* Description of the failure.
|
||||
*/
|
||||
public static final String DESCRIPTION = "Invalid config server configuration.";
|
||||
|
||||
/**
|
||||
* Action to take for git failure.
|
||||
*/
|
||||
public static final String ACTION = "If you are using the git profile, you need to set a Git URI in your "
|
||||
+ "configuration. If you have set spring.cloud.config.server.bootstrap=true, "
|
||||
+ "you need to use a composite configuration.";
|
||||
|
||||
@Override
|
||||
protected FailureAnalysis analyze(Throwable rootFailure, IllegalStateException cause) {
|
||||
if (JGitEnvironmentRepository.MESSAGE.equalsIgnoreCase(cause.getMessage())) {
|
||||
return new FailureAnalysis(DESCRIPTION, ACTION, cause);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2019 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.encryption;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonFactory;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
|
||||
import org.springframework.cloud.config.environment.Environment;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Abstract base class for any @{link
|
||||
* org.springframework.cloud.config.server.encryption.ResourceEncryptor} implementations.
|
||||
* Meant to house shared configuration and logic.
|
||||
*
|
||||
* @author Sean Stiglitz
|
||||
*/
|
||||
abstract class AbstractCipherResourceEncryptor implements ResourceEncryptor {
|
||||
|
||||
protected final String CIPHER_MARKER = "{cipher}";
|
||||
|
||||
private final TextEncryptorLocator encryptor;
|
||||
|
||||
private EnvironmentPrefixHelper helper = new EnvironmentPrefixHelper();
|
||||
|
||||
AbstractCipherResourceEncryptor(TextEncryptorLocator encryptor) {
|
||||
this.encryptor = encryptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract List<String> getSupportedExtensions();
|
||||
|
||||
@Override
|
||||
public abstract String decrypt(String text, Environment environment) throws IOException;
|
||||
|
||||
protected String decryptWithJacksonParser(String text, String name, String[] profiles, JsonFactory factory)
|
||||
throws IOException {
|
||||
Set<String> valsToDecrpyt = new HashSet<String>();
|
||||
JsonParser parser = factory.createParser(text);
|
||||
JsonToken token;
|
||||
|
||||
while ((token = parser.nextToken()) != null) {
|
||||
if (token.equals(JsonToken.VALUE_STRING) && parser.getValueAsString().startsWith(CIPHER_MARKER)) {
|
||||
valsToDecrpyt.add(parser.getValueAsString().trim());
|
||||
}
|
||||
}
|
||||
|
||||
for (String value : valsToDecrpyt) {
|
||||
String decryptedValue = decryptValue(value.replace(CIPHER_MARKER, ""), name, profiles);
|
||||
text = text.replace(value, decryptedValue);
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
protected String decryptValue(String value, String name, String[] profiles) {
|
||||
return encryptor
|
||||
.locate(this.helper.getEncryptorKeys(name, StringUtils.arrayToCommaDelimitedString(profiles), value))
|
||||
.decrypt(this.helper.stripPrefix(value));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2019 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.encryption;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cloud.config.environment.Environment;
|
||||
import org.springframework.cloud.config.environment.PropertySource;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* EnvironmentEncryptor that can decrypt property values prefixed with {cipher} marker.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Bartosz Wojtkiewicz
|
||||
* @author Rafal Zukowski
|
||||
*
|
||||
*/
|
||||
@Component
|
||||
public class CipherEnvironmentEncryptor implements EnvironmentEncryptor {
|
||||
|
||||
private static Log logger = LogFactory.getLog(CipherEnvironmentEncryptor.class);
|
||||
|
||||
private final TextEncryptorLocator encryptor;
|
||||
|
||||
private EnvironmentPrefixHelper helper = new EnvironmentPrefixHelper();
|
||||
|
||||
@Autowired
|
||||
public CipherEnvironmentEncryptor(TextEncryptorLocator encryptor) {
|
||||
this.encryptor = encryptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Environment decrypt(Environment environment) {
|
||||
return this.encryptor != null ? decrypt(environment, this.encryptor) : environment;
|
||||
}
|
||||
|
||||
private Environment decrypt(Environment environment, TextEncryptorLocator encryptor) {
|
||||
Environment result = new Environment(environment);
|
||||
for (PropertySource source : environment.getPropertySources()) {
|
||||
Map<Object, Object> map = new LinkedHashMap<Object, Object>(source.getSource());
|
||||
for (Map.Entry<Object, Object> entry : new LinkedHashSet<>(map.entrySet())) {
|
||||
Object key = entry.getKey();
|
||||
String name = key.toString();
|
||||
if (entry.getValue() != null && entry.getValue().toString().startsWith("{cipher}")) {
|
||||
String value = entry.getValue().toString();
|
||||
map.remove(key);
|
||||
try {
|
||||
value = value.substring("{cipher}".length());
|
||||
value = encryptor
|
||||
.locate(this.helper.getEncryptorKeys(name,
|
||||
StringUtils.arrayToCommaDelimitedString(environment.getProfiles()), value))
|
||||
.decrypt(this.helper.stripPrefix(value));
|
||||
}
|
||||
catch (Exception e) {
|
||||
value = "<n/a>";
|
||||
name = "invalid." + name;
|
||||
String message = "Cannot decrypt key: " + key + " (" + e.getClass() + ": " + e.getMessage()
|
||||
+ ")";
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(message, e);
|
||||
}
|
||||
else if (logger.isWarnEnabled()) {
|
||||
logger.warn(message);
|
||||
}
|
||||
}
|
||||
map.put(name, value);
|
||||
}
|
||||
}
|
||||
result.add(new PropertySource(source.getName(), map));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2019 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.encryption;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonFactory;
|
||||
|
||||
import org.springframework.cloud.config.environment.Environment;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.cloud.config.server.encryption.ResourceEncryptor}
|
||||
* implementation that can decrypt property values prefixed with {cipher} marker in a JSON
|
||||
* file.
|
||||
*
|
||||
* @author Sean Stiglitz
|
||||
*/
|
||||
@Component
|
||||
public class CipherResourceJsonEncryptor extends AbstractCipherResourceEncryptor implements ResourceEncryptor {
|
||||
|
||||
private static final List<String> SUPPORTED_EXTENSIONS = Arrays.asList("json");
|
||||
|
||||
private final JsonFactory factory;
|
||||
|
||||
public CipherResourceJsonEncryptor(TextEncryptorLocator encryptor) {
|
||||
super(encryptor);
|
||||
this.factory = new JsonFactory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getSupportedExtensions() {
|
||||
return SUPPORTED_EXTENSIONS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String decrypt(String text, Environment environment) throws IOException {
|
||||
return decryptWithJacksonParser(text, environment.getName(), environment.getProfiles(), factory);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2019 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.encryption;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.cloud.config.environment.Environment;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.cloud.config.server.encryption.ResourceEncryptor}
|
||||
* implementation that can decrypt property values prefixed with {cipher} marker in a
|
||||
* Properties file.
|
||||
*
|
||||
* @author Sean Stiglitz
|
||||
*/
|
||||
@Component
|
||||
public class CipherResourcePropertiesEncryptor extends AbstractCipherResourceEncryptor implements ResourceEncryptor {
|
||||
|
||||
private static final List<String> SUPPORTED_EXTENSIONS = Arrays.asList("properties");
|
||||
|
||||
public CipherResourcePropertiesEncryptor(TextEncryptorLocator encryptor) {
|
||||
super(encryptor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getSupportedExtensions() {
|
||||
return SUPPORTED_EXTENSIONS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String decrypt(String text, Environment environment) throws IOException {
|
||||
Set<String> valsToDecrpyt = new HashSet<String>();
|
||||
Properties properties = new Properties();
|
||||
StringBuffer sb = new StringBuffer();
|
||||
properties.load(new ByteArrayInputStream(text.getBytes()));
|
||||
|
||||
for (Object value : properties.values()) {
|
||||
String valueStr = value.toString();
|
||||
if (valueStr.startsWith(CIPHER_MARKER)) {
|
||||
valsToDecrpyt.add(valueStr);
|
||||
}
|
||||
}
|
||||
|
||||
for (String value : valsToDecrpyt) {
|
||||
String decryptedValue = decryptValue(value.replace(CIPHER_MARKER, ""), environment.getName(),
|
||||
environment.getProfiles());
|
||||
text = text.replace(value, decryptedValue);
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2019 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.encryption;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
|
||||
|
||||
import org.springframework.cloud.config.environment.Environment;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.cloud.config.server.encryption.ResourceEncryptor}
|
||||
* implementation that can decrypt property values prefixed with {cipher} marker in a YAML
|
||||
* file.
|
||||
*
|
||||
* @author Sean Stiglitz
|
||||
*/
|
||||
@Component
|
||||
public class CipherResourceYamlEncryptor extends AbstractCipherResourceEncryptor implements ResourceEncryptor {
|
||||
|
||||
private static final List<String> SUPPORTED_EXTENSIONS = Arrays.asList("yml", "yaml");
|
||||
|
||||
private final YAMLFactory factory;
|
||||
|
||||
public CipherResourceYamlEncryptor(TextEncryptorLocator encryptor) {
|
||||
super(encryptor);
|
||||
this.factory = new YAMLFactory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getSupportedExtensions() {
|
||||
return SUPPORTED_EXTENSIONS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String decrypt(String text, Environment environment) throws IOException {
|
||||
return decryptWithJacksonParser(text, environment.getName(), environment.getProfiles(), factory);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,287 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2019 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.encryption;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.cloud.context.encrypt.KeyFormatException;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.crypto.codec.Hex;
|
||||
import org.springframework.security.crypto.encrypt.TextEncryptor;
|
||||
import org.springframework.security.rsa.crypto.RsaKeyHolder;
|
||||
import org.springframework.security.rsa.crypto.RsaSecretEncryptor;
|
||||
import org.springframework.util.Base64Utils;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
* @author Tim Ysewyn
|
||||
*
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping(path = "${spring.cloud.config.server.prefix:}")
|
||||
public class EncryptionController {
|
||||
|
||||
private static Log logger = LogFactory.getLog(EncryptionController.class);
|
||||
|
||||
volatile private TextEncryptorLocator encryptorLocator;
|
||||
|
||||
private EnvironmentPrefixHelper helper = new EnvironmentPrefixHelper();
|
||||
|
||||
private String defaultApplicationName = "application";
|
||||
|
||||
private String defaultProfile = "default";
|
||||
|
||||
public EncryptionController(TextEncryptorLocator encryptorLocator) {
|
||||
this.encryptorLocator = encryptorLocator;
|
||||
}
|
||||
|
||||
public void setDefaultApplicationName(String defaultApplicationName) {
|
||||
this.defaultApplicationName = defaultApplicationName;
|
||||
}
|
||||
|
||||
public void setDefaultProfile(String defaultProfile) {
|
||||
this.defaultProfile = defaultProfile;
|
||||
}
|
||||
|
||||
@GetMapping("/key")
|
||||
public String getPublicKey() {
|
||||
return getPublicKey(defaultApplicationName, defaultProfile);
|
||||
}
|
||||
|
||||
@GetMapping("/key/{name}/{profiles}")
|
||||
public String getPublicKey(@PathVariable String name, @PathVariable String profiles) {
|
||||
TextEncryptor encryptor = getEncryptor(name, profiles, "");
|
||||
if (!(encryptor instanceof RsaKeyHolder)) {
|
||||
throw new KeyNotAvailableException();
|
||||
}
|
||||
return ((RsaKeyHolder) encryptor).getPublicKey();
|
||||
}
|
||||
|
||||
@GetMapping("encrypt/status")
|
||||
public Map<String, Object> status() {
|
||||
TextEncryptor encryptor = getEncryptor(defaultApplicationName, defaultProfile, "");
|
||||
validateEncryptionWeakness(encryptor);
|
||||
return Collections.singletonMap("status", "OK");
|
||||
}
|
||||
|
||||
@PostMapping("encrypt")
|
||||
public String encrypt(@RequestBody String data, @RequestHeader("Content-Type") MediaType type) {
|
||||
return encrypt(defaultApplicationName, defaultProfile, data, type);
|
||||
}
|
||||
|
||||
@PostMapping("/encrypt/{name}/{profiles}")
|
||||
public String encrypt(@PathVariable String name, @PathVariable String profiles, @RequestBody String data,
|
||||
@RequestHeader("Content-Type") MediaType type) {
|
||||
String input = stripFormData(data, type, false);
|
||||
TextEncryptor encryptor = getEncryptor(name, profiles, input);
|
||||
validateEncryptionWeakness(encryptor);
|
||||
Map<String, String> keys = helper.getEncryptorKeys(name, profiles, input);
|
||||
String textToEncrypt = helper.stripPrefix(input);
|
||||
String encrypted = helper.addPrefix(keys, encryptor.encrypt(textToEncrypt));
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Encrypted data");
|
||||
}
|
||||
return encrypted;
|
||||
}
|
||||
|
||||
@PostMapping("decrypt")
|
||||
public String decrypt(@RequestBody String data, @RequestHeader("Content-Type") MediaType type) {
|
||||
return decrypt(defaultApplicationName, defaultProfile, data, type);
|
||||
}
|
||||
|
||||
@PostMapping("/decrypt/{name}/{profiles}")
|
||||
public String decrypt(@PathVariable String name, @PathVariable String profiles, @RequestBody String data,
|
||||
@RequestHeader("Content-Type") MediaType type) {
|
||||
try {
|
||||
TextEncryptor encryptor = getEncryptor(name, profiles, data);
|
||||
checkDecryptionPossible(encryptor);
|
||||
validateEncryptionWeakness(encryptor);
|
||||
String input = stripFormData(helper.stripPrefix(data), type, true);
|
||||
String decrypted = encryptor.decrypt(input);
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Decrypted cipher data");
|
||||
}
|
||||
return decrypted;
|
||||
}
|
||||
catch (IllegalArgumentException | IllegalStateException e) {
|
||||
if (logger.isErrorEnabled()) {
|
||||
logger.error("Cannot decrypt key:" + name + ", value:" + data, e);
|
||||
}
|
||||
throw new InvalidCipherException();
|
||||
}
|
||||
}
|
||||
|
||||
private TextEncryptor getEncryptor(String name, String profiles, String data) {
|
||||
if (encryptorLocator == null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Text encryptorLocator is null.");
|
||||
}
|
||||
throw new KeyNotInstalledException();
|
||||
}
|
||||
TextEncryptor encryptor = encryptorLocator.locate(helper.getEncryptorKeys(name, profiles, data));
|
||||
if (encryptor == null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("TextEncryptor is null.");
|
||||
}
|
||||
throw new KeyNotInstalledException();
|
||||
}
|
||||
return encryptor;
|
||||
}
|
||||
|
||||
private void validateEncryptionWeakness(TextEncryptor textEncryptor) {
|
||||
if (textEncryptor.encrypt("FOO").equals("FOO")) {
|
||||
throw new EncryptionTooWeakException();
|
||||
}
|
||||
}
|
||||
|
||||
private void checkDecryptionPossible(TextEncryptor textEncryptor) {
|
||||
if (textEncryptor instanceof RsaSecretEncryptor && !((RsaSecretEncryptor) textEncryptor).canDecrypt()) {
|
||||
throw new DecryptionNotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
private String stripFormData(String data, MediaType type, boolean cipher) {
|
||||
|
||||
if (data.endsWith("=") && !type.equals(MediaType.TEXT_PLAIN)) {
|
||||
try {
|
||||
data = URLDecoder.decode(data, "UTF-8");
|
||||
if (cipher) {
|
||||
data = data.replace(" ", "+");
|
||||
}
|
||||
}
|
||||
catch (UnsupportedEncodingException e) {
|
||||
// Really?
|
||||
}
|
||||
String candidate = data.substring(0, data.length() - 1);
|
||||
if (cipher) {
|
||||
if (data.endsWith("=")) {
|
||||
if (data.length() / 2 != (data.length() + 1) / 2) {
|
||||
try {
|
||||
Hex.decode(candidate);
|
||||
return candidate;
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
try {
|
||||
Base64Utils.decode(candidate.getBytes());
|
||||
return candidate;
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
// User posted data with content type form but meant it to be text/plain
|
||||
data = candidate;
|
||||
}
|
||||
|
||||
return data;
|
||||
|
||||
}
|
||||
|
||||
@ExceptionHandler(KeyFormatException.class)
|
||||
public ResponseEntity<Map<String, Object>> keyFormat() {
|
||||
Map<String, Object> body = new HashMap<>();
|
||||
body.put("status", "BAD_REQUEST");
|
||||
body.put("description", "Key data not in correct format (PEM or jks keystore)");
|
||||
return new ResponseEntity<>(body, HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
@ExceptionHandler(KeyNotAvailableException.class)
|
||||
public ResponseEntity<Map<String, Object>> keyUnavailable() {
|
||||
Map<String, Object> body = new HashMap<>();
|
||||
body.put("status", "NOT_FOUND");
|
||||
body.put("description", "No public key available");
|
||||
return new ResponseEntity<>(body, HttpStatus.NOT_FOUND);
|
||||
}
|
||||
|
||||
@ExceptionHandler(DecryptionNotSupportedException.class)
|
||||
public ResponseEntity<Map<String, Object>> decryptionDisabled() {
|
||||
Map<String, Object> body = new HashMap<>();
|
||||
body.put("status", "BAD_REQUEST");
|
||||
body.put("description", "Server-side decryption is not supported");
|
||||
return new ResponseEntity<>(body, HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
@ExceptionHandler(KeyNotInstalledException.class)
|
||||
public ResponseEntity<Map<String, Object>> notInstalled() {
|
||||
Map<String, Object> body = new HashMap<>();
|
||||
body.put("status", "NO_KEY");
|
||||
body.put("description", "No key was installed for encryption service");
|
||||
return new ResponseEntity<>(body, HttpStatus.NOT_FOUND);
|
||||
}
|
||||
|
||||
@ExceptionHandler(EncryptionTooWeakException.class)
|
||||
public ResponseEntity<Map<String, Object>> encryptionTooWeak() {
|
||||
Map<String, Object> body = new HashMap<>();
|
||||
body.put("status", "INVALID");
|
||||
body.put("description", "The encryption algorithm is not strong enough");
|
||||
return new ResponseEntity<>(body, HttpStatus.NOT_FOUND);
|
||||
}
|
||||
|
||||
@ExceptionHandler(InvalidCipherException.class)
|
||||
public ResponseEntity<Map<String, Object>> invalidCipher() {
|
||||
Map<String, Object> body = new HashMap<>();
|
||||
body.put("status", "INVALID");
|
||||
body.put("description", "Text not encrypted with this key");
|
||||
return new ResponseEntity<>(body, HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
class KeyNotInstalledException extends RuntimeException {
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
class KeyNotAvailableException extends RuntimeException {
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
class EncryptionTooWeakException extends RuntimeException {
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
class InvalidCipherException extends RuntimeException {
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
class DecryptionNotSupportedException extends RuntimeException {
|
||||
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2019 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.encryption;
|
||||
|
||||
import org.springframework.cloud.config.environment.Environment;
|
||||
|
||||
/**
|
||||
* Service interface for decrypting properties in Environment object.
|
||||
*
|
||||
* @author Bartosz Wojtkiewicz
|
||||
* @author Rafal Zukowski
|
||||
*
|
||||
*/
|
||||
public interface EnvironmentEncryptor {
|
||||
|
||||
Environment decrypt(Environment environment);
|
||||
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015-2019 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.encryption;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.security.crypto.encrypt.TextEncryptor;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Shared helper class for encryption and decryption concerns where the plain text and
|
||||
* cipher texts can optionally contain prefixes of <code>{name:value}</code> pairs.
|
||||
* Special treatment is given to the "name" and "profiles" keys, which can be provided by
|
||||
* the caller, explicitly instead of by the input text strings. This is to support
|
||||
* independent decryptions using different cryptographic keys for different applications
|
||||
* and profiles, if needed (this class does not have any crypto features, but it can be
|
||||
* used by components that do).
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
class EnvironmentPrefixHelper {
|
||||
|
||||
/**
|
||||
* key for the "profiles" prefix pair (usually a Spring profile or comma separated
|
||||
* value).
|
||||
*/
|
||||
private static final String PROFILES = "profiles";
|
||||
|
||||
/**
|
||||
* key for the "name" (Environment name or application name).
|
||||
*/
|
||||
private static final String NAME = "name";
|
||||
|
||||
/**
|
||||
* If plain text actually starts with text in the form <code>{name:value}</code>
|
||||
* prefix it with this to signal the start of the plain text.
|
||||
*/
|
||||
private static final String ESCAPE = "{plain}";
|
||||
|
||||
/**
|
||||
* Extract keys for looking up a {@link TextEncryptor} from the input text in the form
|
||||
* of a prefix of zero or many <code>{name:value}</code> pairs. The name and profiles
|
||||
* properties are always added to the keys (replacing any provided in the inputs).
|
||||
* @param name application name
|
||||
* @param profiles list of profiles
|
||||
* @param text text to cipher
|
||||
* @return encryptor keys
|
||||
*/
|
||||
public Map<String, String> getEncryptorKeys(String name, String profiles, String text) {
|
||||
|
||||
Map<String, String> keys = new LinkedHashMap<String, String>();
|
||||
|
||||
text = removeEnvironmentPrefix(text);
|
||||
keys.put(NAME, name);
|
||||
keys.put(PROFILES, profiles);
|
||||
|
||||
if (text.contains(ESCAPE)) {
|
||||
text = text.substring(0, text.indexOf(ESCAPE));
|
||||
}
|
||||
|
||||
String[] tokens = StringUtils.split(text, "}");
|
||||
while (tokens != null) {
|
||||
String token = tokens[0].trim();
|
||||
if (token.startsWith("{")) {
|
||||
String key = "";
|
||||
String value = "";
|
||||
if (token.contains(":") && !token.endsWith(":")) {
|
||||
key = token.substring(1, token.indexOf(":"));
|
||||
value = token.substring(token.indexOf(":") + 1);
|
||||
}
|
||||
else {
|
||||
key = token.substring(1);
|
||||
}
|
||||
keys.put(key, value);
|
||||
}
|
||||
text = tokens[1];
|
||||
tokens = StringUtils.split(text, "}");
|
||||
}
|
||||
|
||||
return keys;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a prefix to the input text (usually a cipher) consisting of the
|
||||
* <code>{name:value}</code> pairs. The "name" and "profiles" keys are special in that
|
||||
* they are stripped since that information is always available when deriving the keys
|
||||
* in {@link #getEncryptorKeys(String, String, String)}.
|
||||
* @param keys name, value pairs
|
||||
* @param input input to append
|
||||
* @return prefixed input
|
||||
*/
|
||||
public String addPrefix(Map<String, String> keys, String input) {
|
||||
keys.remove(NAME);
|
||||
keys.remove(PROFILES);
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (String key : keys.keySet()) {
|
||||
builder.append("{").append(key).append(":").append(keys.get(key)).append("}");
|
||||
}
|
||||
builder.append(input);
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public String stripPrefix(String value) {
|
||||
if (!value.contains("}")) {
|
||||
return value;
|
||||
}
|
||||
if (value.contains(ESCAPE)) {
|
||||
return value.substring(value.indexOf(ESCAPE) + ESCAPE.length());
|
||||
}
|
||||
return value.replaceFirst("^(\\{.*?:.*?\\})+", "");
|
||||
}
|
||||
|
||||
private String removeEnvironmentPrefix(String input) {
|
||||
return input.replaceFirst("\\{name:.*\\}", "").replaceFirst("\\{profiles:.*\\}", "");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015-2019 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.encryption;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.security.crypto.encrypt.TextEncryptor;
|
||||
import org.springframework.security.rsa.crypto.KeyStoreKeyFactory;
|
||||
import org.springframework.security.rsa.crypto.RsaAlgorithm;
|
||||
import org.springframework.security.rsa.crypto.RsaSecretEncryptor;
|
||||
|
||||
/**
|
||||
* A {@link TextEncryptorLocator} that pulls RSA key pairs out of a keystore. The input
|
||||
* map can contain entries for "key" or "secret" or both, or neither. The secret in the
|
||||
* input map is not, in general, the secret in the keystore, but is dereferenced through a
|
||||
* {@link SecretLocator} (so for example you can keep a table of encrypted secrets and
|
||||
* update it separately to the keystore).
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class KeyStoreTextEncryptorLocator implements TextEncryptorLocator {
|
||||
|
||||
private final static String KEY = "key";
|
||||
|
||||
private final static String SECRET = "secret";
|
||||
|
||||
private KeyStoreKeyFactory keys;
|
||||
|
||||
private String defaultSecret;
|
||||
|
||||
private String defaultAlias;
|
||||
|
||||
private RsaSecretEncryptor defaultEncryptor;
|
||||
|
||||
private SecretLocator secretLocator = new PassthruSecretLocator();
|
||||
|
||||
private RsaAlgorithm rsaAlgorithm = RsaAlgorithm.DEFAULT;
|
||||
|
||||
private boolean strong = false;
|
||||
|
||||
private String salt = "deadbeef";
|
||||
|
||||
public KeyStoreTextEncryptorLocator(KeyStoreKeyFactory keys, String defaultSecret, String defaultAlias) {
|
||||
this.keys = keys;
|
||||
this.defaultAlias = defaultAlias;
|
||||
this.defaultSecret = defaultSecret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param secretLocator the secretLocator to set
|
||||
*/
|
||||
public void setSecretLocator(SecretLocator secretLocator) {
|
||||
this.secretLocator = secretLocator;
|
||||
}
|
||||
|
||||
public void setRsaAlgorithm(RsaAlgorithm rsaAlgorithm) {
|
||||
this.rsaAlgorithm = rsaAlgorithm;
|
||||
}
|
||||
|
||||
public void setStrong(boolean strong) {
|
||||
this.strong = strong;
|
||||
}
|
||||
|
||||
public void setSalt(String salt) {
|
||||
this.salt = salt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextEncryptor locate(Map<String, String> keys) {
|
||||
String alias = keys.containsKey(KEY) ? keys.get(KEY) : this.defaultAlias;
|
||||
String secret = keys.containsKey(SECRET) ? keys.get(SECRET) : this.defaultSecret;
|
||||
if (alias.equals(this.defaultAlias) && secret.equals(this.defaultSecret)) {
|
||||
if (this.defaultEncryptor == null) {
|
||||
this.defaultEncryptor = rsaSecretEncryptor(alias, secret);
|
||||
}
|
||||
return this.defaultEncryptor;
|
||||
}
|
||||
else {
|
||||
return rsaSecretEncryptor(alias, secret);
|
||||
}
|
||||
}
|
||||
|
||||
private RsaSecretEncryptor rsaSecretEncryptor(String alias, String secret) {
|
||||
return new RsaSecretEncryptor(this.keys.getKeyPair(alias, this.secretLocator.locate(secret)), this.rsaAlgorithm,
|
||||
this.salt, this.strong);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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.encryption;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.security.crypto.encrypt.TextEncryptor;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class LocatorTextEncryptor implements TextEncryptor {
|
||||
|
||||
private EnvironmentPrefixHelper helper = new EnvironmentPrefixHelper();
|
||||
|
||||
private TextEncryptorLocator locator;
|
||||
|
||||
public LocatorTextEncryptor(TextEncryptorLocator locator) {
|
||||
this.locator = locator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encrypt(String text) {
|
||||
Map<String, String> keys = this.helper.getEncryptorKeys("configserver", "default", text);
|
||||
return getLocator().locate(keys).encrypt(this.helper.stripPrefix(text));
|
||||
}
|
||||
|
||||
private TextEncryptorLocator getLocator() {
|
||||
return this.locator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String decrypt(String encryptedText) {
|
||||
Map<String, String> keys = this.helper.getEncryptorKeys("configserver", "default", encryptedText);
|
||||
return getLocator().locate(keys).decrypt(this.helper.stripPrefix(encryptedText));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015-2019 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.encryption;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class PassthruSecretLocator implements SecretLocator {
|
||||
|
||||
@Override
|
||||
public char[] locate(String secret) {
|
||||
return secret == null ? new char[0] : secret.toCharArray();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2019 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.encryption;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.cloud.config.environment.Environment;
|
||||
|
||||
/**
|
||||
* Interface for decrypting values in plain text files served through
|
||||
* {@link org.springframework.cloud.config.server.resource.ResourceController}.
|
||||
*
|
||||
* @author Sean Stiglitz
|
||||
*/
|
||||
public interface ResourceEncryptor {
|
||||
|
||||
List<String> getSupportedExtensions();
|
||||
|
||||
String decrypt(String text, Environment environment) throws IOException;
|
||||
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015-2019 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.encryption;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public interface SecretLocator {
|
||||
|
||||
char[] locate(String secret);
|
||||
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015-2019 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.encryption;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.security.crypto.encrypt.Encryptors;
|
||||
import org.springframework.security.crypto.encrypt.TextEncryptor;
|
||||
|
||||
/**
|
||||
* @author Bartosz Wojtkiewicz
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class SingleTextEncryptorLocator implements TextEncryptorLocator {
|
||||
|
||||
private TextEncryptor encryptor;
|
||||
|
||||
public SingleTextEncryptorLocator(TextEncryptor encryptor) {
|
||||
this.encryptor = encryptor == null ? Encryptors.noOpText() : encryptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextEncryptor locate(Map<String, String> keys) {
|
||||
return this.encryptor;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015-2019 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.encryption;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.security.crypto.encrypt.TextEncryptor;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
* @author Bartosz Wojtkiewicz
|
||||
*
|
||||
*/
|
||||
public interface TextEncryptorLocator {
|
||||
|
||||
TextEncryptor locate(Map<String, String> keys);
|
||||
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2019 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.encryption.vault;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.cloud.config.environment.Environment;
|
||||
import org.springframework.cloud.config.environment.PropertySource;
|
||||
import org.springframework.cloud.config.server.encryption.CipherEnvironmentEncryptor;
|
||||
import org.springframework.cloud.config.server.encryption.EnvironmentEncryptor;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.vault.core.VaultKeyValueOperations;
|
||||
import org.springframework.vault.support.VaultResponse;
|
||||
|
||||
/**
|
||||
* VaultEnvironmentEncryptor that can decrypt property values prefixed with {vault}
|
||||
* marker.
|
||||
*
|
||||
* @author Alexey Zhokhov
|
||||
*/
|
||||
public class VaultEnvironmentEncryptor implements EnvironmentEncryptor {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(CipherEnvironmentEncryptor.class);
|
||||
|
||||
private final VaultKeyValueOperations keyValueTemplate;
|
||||
|
||||
public VaultEnvironmentEncryptor(VaultKeyValueOperations keyValueTemplate) {
|
||||
this.keyValueTemplate = keyValueTemplate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Environment decrypt(Environment environment) {
|
||||
Map<String, VaultResponse> loadedVaultKeys = new HashMap<>();
|
||||
|
||||
Environment result = new Environment(environment);
|
||||
for (PropertySource source : environment.getPropertySources()) {
|
||||
Map<Object, Object> map = new LinkedHashMap<>(source.getSource());
|
||||
for (Map.Entry<Object, Object> entry : new LinkedHashSet<>(map.entrySet())) {
|
||||
Object key = entry.getKey();
|
||||
String name = key.toString();
|
||||
if (entry.getValue() != null && entry.getValue().toString().startsWith("{vault}")) {
|
||||
String value = entry.getValue().toString();
|
||||
map.remove(key);
|
||||
try {
|
||||
value = value.substring("{vault}".length());
|
||||
|
||||
if (!value.startsWith(":")) {
|
||||
throw new RuntimeException("Wrong format");
|
||||
}
|
||||
|
||||
value = value.substring(1);
|
||||
|
||||
if (!value.contains("#")) {
|
||||
throw new RuntimeException("Wrong format");
|
||||
}
|
||||
|
||||
String[] parts = value.split("#");
|
||||
|
||||
if (parts.length == 1) {
|
||||
throw new RuntimeException("Wrong format");
|
||||
}
|
||||
|
||||
if (ObjectUtils.isEmpty(parts[0]) || ObjectUtils.isEmpty(parts[1])) {
|
||||
throw new RuntimeException("Wrong format");
|
||||
}
|
||||
|
||||
String vaultKey = parts[0];
|
||||
String vaultParamName = parts[1];
|
||||
|
||||
if (!loadedVaultKeys.containsKey(vaultKey)) {
|
||||
loadedVaultKeys.put(vaultKey, keyValueTemplate.get(vaultKey));
|
||||
}
|
||||
|
||||
VaultResponse vaultResponse = loadedVaultKeys.get(vaultKey);
|
||||
|
||||
if (vaultResponse == null || (vaultResponse.getData() == null
|
||||
|| !vaultResponse.getData().containsKey(vaultParamName))) {
|
||||
value = null;
|
||||
}
|
||||
else {
|
||||
value = vaultResponse.getData().get(vaultParamName).toString();
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
value = "<n/a>";
|
||||
name = "invalid." + name;
|
||||
String message = "Cannot resolve key: " + key + " (" + e.getClass() + ": " + e.getMessage()
|
||||
+ ")";
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(message, e);
|
||||
}
|
||||
else if (logger.isWarnEnabled()) {
|
||||
logger.warn(message);
|
||||
}
|
||||
}
|
||||
map.put(name, value);
|
||||
}
|
||||
}
|
||||
result.add(new PropertySource(source.getName(), map));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015-2019 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 io.micrometer.observation.ObservationRegistry;
|
||||
|
||||
import org.springframework.cloud.config.environment.Environment;
|
||||
import org.springframework.cloud.config.server.support.AbstractScmAccessor;
|
||||
import org.springframework.cloud.config.server.support.AbstractScmAccessorProperties;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public abstract class AbstractScmEnvironmentRepository extends AbstractScmAccessor
|
||||
implements EnvironmentRepository, SearchPathLocator, Ordered {
|
||||
|
||||
private EnvironmentCleaner cleaner = new EnvironmentCleaner();
|
||||
|
||||
private int order = Ordered.LOWEST_PRECEDENCE;
|
||||
|
||||
private final ObservationRegistry observationRegistry;
|
||||
|
||||
public AbstractScmEnvironmentRepository(ConfigurableEnvironment environment,
|
||||
ObservationRegistry observationRegistry) {
|
||||
super(environment);
|
||||
this.observationRegistry = observationRegistry;
|
||||
}
|
||||
|
||||
public AbstractScmEnvironmentRepository(ConfigurableEnvironment environment,
|
||||
AbstractScmAccessorProperties properties, ObservationRegistry observationRegistry) {
|
||||
super(environment, properties);
|
||||
this.order = properties.getOrder();
|
||||
this.observationRegistry = observationRegistry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Environment findOne(String application, String profile, String label) {
|
||||
return findOne(application, profile, label, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Environment findOne(String application, String profile, String label, boolean includeOrigin) {
|
||||
NativeEnvironmentRepository delegate = new NativeEnvironmentRepository(getEnvironment(),
|
||||
new NativeEnvironmentProperties(), this.observationRegistry);
|
||||
Locations locations = getLocations(application, profile, label);
|
||||
delegate.setSearchLocations(locations.getLocations());
|
||||
Environment result = delegate.findOne(application, profile, "", includeOrigin);
|
||||
result.setVersion(locations.getVersion());
|
||||
result.setLabel(label);
|
||||
return this.cleaner.clean(result, getWorkingDirectory().toURI().toString(), getUri());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return this.order;
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,171 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2019 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.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
|
||||
import org.springframework.cloud.config.environment.Environment;
|
||||
import org.springframework.cloud.config.environment.PropertySource;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.io.ByteArrayResource;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static org.springframework.cloud.config.client.ConfigClientProperties.STATE_HEADER;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
* @author Mark Paluch
|
||||
* @author Haroun Pacquee
|
||||
* @author Haytham Mohamed
|
||||
* @author Scott Frederick
|
||||
*/
|
||||
public abstract class AbstractVaultEnvironmentRepository implements EnvironmentRepository, Ordered {
|
||||
|
||||
private static Log log = LogFactory.getLog(AbstractVaultEnvironmentRepository.class);
|
||||
|
||||
// TODO: move to watchState:String on findOne?
|
||||
protected final ObjectProvider<HttpServletRequest> request;
|
||||
|
||||
protected final EnvironmentWatch watch;
|
||||
|
||||
/**
|
||||
* The key in vault shared by all applications. Defaults to application. Set to empty
|
||||
* to disable.
|
||||
*/
|
||||
protected String defaultKey;
|
||||
|
||||
/**
|
||||
* Vault profile separator. Defaults to comma.
|
||||
*/
|
||||
@NotEmpty
|
||||
protected String profileSeparator;
|
||||
|
||||
protected int order;
|
||||
|
||||
public AbstractVaultEnvironmentRepository(ObjectProvider<HttpServletRequest> request, EnvironmentWatch watch,
|
||||
VaultEnvironmentProperties properties) {
|
||||
this.defaultKey = properties.getDefaultKey();
|
||||
this.profileSeparator = properties.getProfileSeparator();
|
||||
this.order = properties.getOrder();
|
||||
this.request = request;
|
||||
this.watch = watch;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Environment findOne(String application, String profile, String label) {
|
||||
String[] profiles = StringUtils.commaDelimitedListToStringArray(profile);
|
||||
List<String> scrubbedProfiles = scrubProfiles(profiles);
|
||||
|
||||
List<String> keys = findKeys(application, scrubbedProfiles);
|
||||
|
||||
Environment environment = new Environment(application, profiles, label, null, getWatchState());
|
||||
|
||||
for (String key : keys) {
|
||||
// read raw 'data' key from vault
|
||||
String data = read(key);
|
||||
if (data != null) {
|
||||
// data is in json format of which, yaml is a superset, so parse
|
||||
final YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean();
|
||||
yaml.setResources(new ByteArrayResource(data.getBytes()));
|
||||
Properties properties = yaml.getObject();
|
||||
|
||||
if (!properties.isEmpty()) {
|
||||
environment.add(new PropertySource("vault:" + key, properties));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return environment;
|
||||
}
|
||||
|
||||
protected abstract String read(String key);
|
||||
|
||||
private String getWatchState() {
|
||||
HttpServletRequest servletRequest = this.request.getIfAvailable();
|
||||
if (servletRequest != null) {
|
||||
try {
|
||||
String state = servletRequest.getHeader(STATE_HEADER);
|
||||
return this.watch.watch(state);
|
||||
}
|
||||
catch (IllegalStateException e) {
|
||||
log.debug("Could not get state.", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<String> findKeys(String application, List<String> profiles) {
|
||||
List<String> keys = new ArrayList<>();
|
||||
|
||||
if (StringUtils.hasText(this.defaultKey) && !this.defaultKey.equals(application)) {
|
||||
keys.add(this.defaultKey);
|
||||
addProfiles(keys, this.defaultKey, profiles);
|
||||
}
|
||||
|
||||
// application may have comma-separated list of names
|
||||
String[] applications = StringUtils.commaDelimitedListToStringArray(application);
|
||||
for (String app : applications) {
|
||||
keys.add(app);
|
||||
addProfiles(keys, app, profiles);
|
||||
}
|
||||
|
||||
Collections.reverse(keys);
|
||||
return keys;
|
||||
}
|
||||
|
||||
private List<String> scrubProfiles(String[] profiles) {
|
||||
List<String> scrubbedProfiles = new ArrayList<>(Arrays.asList(profiles));
|
||||
scrubbedProfiles.remove("default");
|
||||
return scrubbedProfiles;
|
||||
}
|
||||
|
||||
private void addProfiles(List<String> contexts, String baseContext, List<String> profiles) {
|
||||
for (String profile : profiles) {
|
||||
contexts.add(baseContext + this.profileSeparator + profile);
|
||||
}
|
||||
}
|
||||
|
||||
public void setDefaultKey(String defaultKey) {
|
||||
this.defaultKey = defaultKey;
|
||||
}
|
||||
|
||||
public void setProfileSeparator(String profileSeparator) {
|
||||
this.profileSeparator = profileSeparator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return this.order;
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2020 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.net.URI;
|
||||
|
||||
import software.amazon.awssdk.awscore.client.builder.AwsClientBuilder;
|
||||
import software.amazon.awssdk.regions.Region;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
abstract class AwsClientBuilderConfigurer {
|
||||
|
||||
private AwsClientBuilderConfigurer() {
|
||||
}
|
||||
|
||||
static void configureClientBuilder(AwsClientBuilder<?, ?> clientBuilder, String region, String endpoint) {
|
||||
if (StringUtils.hasText(region)) {
|
||||
clientBuilder.region(Region.of(region));
|
||||
if (StringUtils.hasText(endpoint)) {
|
||||
clientBuilder.endpointOverride(URI.create(endpoint));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,177 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2020 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 jakarta.validation.constraints.Max;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.cloud.config.server.support.EnvironmentRepositoryProperties;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
/**
|
||||
* @author Iulian Antohe
|
||||
*/
|
||||
@Validated
|
||||
@ConfigurationProperties("spring.cloud.config.server.awsparamstore")
|
||||
public class AwsParameterStoreEnvironmentProperties implements EnvironmentRepositoryProperties {
|
||||
|
||||
static final String DEFAULT_PATH_SEPARATOR = "/";
|
||||
|
||||
private static final String DEFAULT_ORIGIN = "aws:ssm:parameter:";
|
||||
|
||||
private static final String DEFAULT_PREFIX = DEFAULT_PATH_SEPARATOR + "config";
|
||||
|
||||
private static final String DEFAULT_PROFILE_SEPARATOR = "-";
|
||||
|
||||
/**
|
||||
* The order of the environment repository.
|
||||
*/
|
||||
private int order = Ordered.LOWEST_PRECEDENCE;
|
||||
|
||||
/**
|
||||
* The region to be used by the AWS Parameter Store client.
|
||||
*/
|
||||
private String region;
|
||||
|
||||
/**
|
||||
* The service endpoint to be used by the AWS Parameter Store client.
|
||||
*/
|
||||
private String endpoint;
|
||||
|
||||
/**
|
||||
* Prefix indicating the property's origin. Defaults to "aws:ssm:parameter:".
|
||||
*/
|
||||
@NotNull
|
||||
private String origin = DEFAULT_ORIGIN;
|
||||
|
||||
/**
|
||||
* Prefix indicating first level for every property loaded from the AWS Parameter
|
||||
* Store. Value must start with a forward slash followed by one or more valid path
|
||||
* segments or be empty. Defaults to "/config".
|
||||
*/
|
||||
@NotNull
|
||||
@Pattern(regexp = "(/[a-zA-Z0-9.\\-_]+)*")
|
||||
private String prefix = DEFAULT_PREFIX;
|
||||
|
||||
/**
|
||||
* String that separates an appended profile from the context name. Note that an AWS
|
||||
* parameter name can only contain dots, dashes and underscores next to alphanumeric
|
||||
* characters. Defaults to "-".
|
||||
*/
|
||||
@NotBlank
|
||||
@Pattern(regexp = "[a-zA-Z0-9.\\-_/]+")
|
||||
private String profileSeparator = DEFAULT_PROFILE_SEPARATOR;
|
||||
|
||||
/**
|
||||
* Flag to indicate the retrieval of all AWS parameters within a hierarchy. Defaults
|
||||
* to "true".
|
||||
*/
|
||||
private boolean recursive = true;
|
||||
|
||||
/**
|
||||
* Flag to indicate the retrieval of all AWS parameters in a hierarchy with their
|
||||
* value decrypted. Defaults to "true".
|
||||
*/
|
||||
private boolean decryptValues = true;
|
||||
|
||||
/**
|
||||
* The maximum number of items to return for an AWS Parameter Store API call. Defaults
|
||||
* to "10".
|
||||
*/
|
||||
@Min(1)
|
||||
@Max(10)
|
||||
private int maxResults = 10;
|
||||
|
||||
public int getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
public String getRegion() {
|
||||
return region;
|
||||
}
|
||||
|
||||
public void setRegion(String region) {
|
||||
this.region = region;
|
||||
}
|
||||
|
||||
public String getEndpoint() {
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
public void setEndpoint(String endpoint) {
|
||||
this.endpoint = endpoint;
|
||||
}
|
||||
|
||||
public String getOrigin() {
|
||||
return origin;
|
||||
}
|
||||
|
||||
public void setOrigin(String origin) {
|
||||
this.origin = origin;
|
||||
}
|
||||
|
||||
public String getPrefix() {
|
||||
return prefix;
|
||||
}
|
||||
|
||||
public void setPrefix(String prefix) {
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
public String getProfileSeparator() {
|
||||
return profileSeparator;
|
||||
}
|
||||
|
||||
public void setProfileSeparator(String profileSeparator) {
|
||||
this.profileSeparator = profileSeparator;
|
||||
}
|
||||
|
||||
public boolean isRecursive() {
|
||||
return recursive;
|
||||
}
|
||||
|
||||
public void setRecursive(boolean recursive) {
|
||||
this.recursive = recursive;
|
||||
}
|
||||
|
||||
public boolean isDecryptValues() {
|
||||
return decryptValues;
|
||||
}
|
||||
|
||||
public void setDecryptValues(boolean decryptValues) {
|
||||
this.decryptValues = decryptValues;
|
||||
}
|
||||
|
||||
public int getMaxResults() {
|
||||
return maxResults;
|
||||
}
|
||||
|
||||
public void setMaxResults(int maxResults) {
|
||||
this.maxResults = maxResults;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,176 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2020 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.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import software.amazon.awssdk.services.ssm.SsmClient;
|
||||
import software.amazon.awssdk.services.ssm.model.GetParametersByPathRequest;
|
||||
import software.amazon.awssdk.services.ssm.model.GetParametersByPathResponse;
|
||||
import software.amazon.awssdk.services.ssm.model.Parameter;
|
||||
|
||||
import org.springframework.cloud.config.environment.Environment;
|
||||
import org.springframework.cloud.config.environment.PropertySource;
|
||||
import org.springframework.cloud.config.server.config.ConfigServerProperties;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static org.springframework.cloud.config.server.environment.AwsParameterStoreEnvironmentProperties.DEFAULT_PATH_SEPARATOR;
|
||||
|
||||
/**
|
||||
* @author Iulian Antohe
|
||||
*/
|
||||
public class AwsParameterStoreEnvironmentRepository implements EnvironmentRepository, Ordered {
|
||||
|
||||
private final SsmClient awsSsmClient;
|
||||
|
||||
private final ConfigServerProperties configServerProperties;
|
||||
|
||||
private final AwsParameterStoreEnvironmentProperties environmentProperties;
|
||||
|
||||
private final int order;
|
||||
|
||||
public AwsParameterStoreEnvironmentRepository(SsmClient awsSsmClient, ConfigServerProperties configServerProperties,
|
||||
AwsParameterStoreEnvironmentProperties environmentProperties) {
|
||||
this.awsSsmClient = awsSsmClient;
|
||||
this.configServerProperties = configServerProperties;
|
||||
this.environmentProperties = environmentProperties;
|
||||
this.order = environmentProperties.getOrder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Environment findOne(String application, String profile, String label) {
|
||||
if (!StringUtils.hasLength(application)) {
|
||||
application = configServerProperties.getDefaultApplicationName();
|
||||
}
|
||||
|
||||
if (!StringUtils.hasLength(profile)) {
|
||||
profile = configServerProperties.getDefaultProfile();
|
||||
}
|
||||
|
||||
String[] profiles = StringUtils.commaDelimitedListToStringArray(profile);
|
||||
|
||||
Environment result = new Environment(application, profiles, label, null, null);
|
||||
|
||||
Set<String> paths = buildParameterPaths(application, profiles);
|
||||
List<PropertySource> propertySources = getPropertySources(paths);
|
||||
|
||||
result.addAll(propertySources);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private Set<String> buildParameterPaths(String application, String[] profiles) {
|
||||
Set<String> result = new LinkedHashSet<>();
|
||||
|
||||
String prefix = environmentProperties.getPrefix();
|
||||
String defaultApplication = configServerProperties.getDefaultApplicationName();
|
||||
String profileSeparator = environmentProperties.getProfileSeparator();
|
||||
String defaultProfile = configServerProperties.getDefaultProfile();
|
||||
|
||||
List<String> orderedProfiles = Stream.concat(Arrays.stream(profiles).filter(p -> !p.equals(defaultProfile)),
|
||||
Arrays.stream(new String[] { defaultProfile })).collect(Collectors.toList());
|
||||
|
||||
if (application.equals(defaultApplication)) {
|
||||
for (String profile : orderedProfiles) {
|
||||
result.add(prefix + DEFAULT_PATH_SEPARATOR + defaultApplication + profileSeparator + profile
|
||||
+ DEFAULT_PATH_SEPARATOR);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (String profile : orderedProfiles) {
|
||||
result.add(prefix + DEFAULT_PATH_SEPARATOR + application + profileSeparator + profile
|
||||
+ DEFAULT_PATH_SEPARATOR);
|
||||
|
||||
result.add(prefix + DEFAULT_PATH_SEPARATOR + defaultApplication + profileSeparator + profile
|
||||
+ DEFAULT_PATH_SEPARATOR);
|
||||
}
|
||||
|
||||
result.add(prefix + DEFAULT_PATH_SEPARATOR + application + DEFAULT_PATH_SEPARATOR);
|
||||
}
|
||||
|
||||
result.add(prefix + DEFAULT_PATH_SEPARATOR + defaultApplication + DEFAULT_PATH_SEPARATOR);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<PropertySource> getPropertySources(Set<String> parameterPaths) {
|
||||
List<PropertySource> result = new ArrayList<>();
|
||||
|
||||
for (String path : parameterPaths) {
|
||||
String name = environmentProperties.getOrigin() + path;
|
||||
Map<String, String> source = getPropertiesByParameterPath(path);
|
||||
|
||||
if (!source.isEmpty()) {
|
||||
result.add(new PropertySource(name, source));
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, String> overrides = configServerProperties.getOverrides();
|
||||
|
||||
if (!overrides.isEmpty()) {
|
||||
result.add(0, new PropertySource("overrides", overrides));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private Map<String, String> getPropertiesByParameterPath(String path) {
|
||||
Map<String, String> result = new HashMap<>();
|
||||
|
||||
GetParametersByPathRequest request = GetParametersByPathRequest.builder().path(path)
|
||||
.recursive(environmentProperties.isRecursive()).withDecryption(environmentProperties.isDecryptValues())
|
||||
.maxResults(environmentProperties.getMaxResults()).build();
|
||||
|
||||
GetParametersByPathResponse response = awsSsmClient.getParametersByPath(request);
|
||||
|
||||
if (response != null) {
|
||||
addParametersToProperties(path, response.parameters(), result);
|
||||
|
||||
while (StringUtils.hasLength(response.nextToken())) {
|
||||
response = awsSsmClient
|
||||
.getParametersByPath(request.toBuilder().nextToken(response.nextToken()).build());
|
||||
|
||||
addParametersToProperties(path, response.parameters(), result);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void addParametersToProperties(String path, List<Parameter> parameters, Map<String, String> properties) {
|
||||
for (Parameter parameter : parameters) {
|
||||
String name = StringUtils.delete(parameter.name(), path).replace(DEFAULT_PATH_SEPARATOR, ".");
|
||||
|
||||
properties.put(name, parameter.value());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2020 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 software.amazon.awssdk.services.ssm.SsmClient;
|
||||
import software.amazon.awssdk.services.ssm.SsmClientBuilder;
|
||||
|
||||
import org.springframework.cloud.config.server.config.ConfigServerProperties;
|
||||
|
||||
import static org.springframework.cloud.config.server.environment.AwsClientBuilderConfigurer.configureClientBuilder;
|
||||
|
||||
/**
|
||||
* @author Iulian Antohe
|
||||
*/
|
||||
public class AwsParameterStoreEnvironmentRepositoryFactory implements
|
||||
EnvironmentRepositoryFactory<AwsParameterStoreEnvironmentRepository, AwsParameterStoreEnvironmentProperties> {
|
||||
|
||||
private final ConfigServerProperties configServerProperties;
|
||||
|
||||
public AwsParameterStoreEnvironmentRepositoryFactory(ConfigServerProperties configServerProperties) {
|
||||
this.configServerProperties = configServerProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AwsParameterStoreEnvironmentRepository build(AwsParameterStoreEnvironmentProperties environmentProperties) {
|
||||
SsmClientBuilder clientBuilder = SsmClient.builder();
|
||||
|
||||
configureClientBuilder(clientBuilder, environmentProperties.getRegion(), environmentProperties.getEndpoint());
|
||||
|
||||
SsmClient client = clientBuilder.build();
|
||||
|
||||
return new AwsParameterStoreEnvironmentRepository(client, configServerProperties, environmentProperties);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2019 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 Clay McCoy
|
||||
*/
|
||||
@ConfigurationProperties("spring.cloud.config.server.awss3")
|
||||
public class AwsS3EnvironmentProperties implements EnvironmentRepositoryProperties {
|
||||
|
||||
/**
|
||||
* AWS region that contains config.
|
||||
*/
|
||||
private String region;
|
||||
|
||||
/**
|
||||
* Adds the ability to override the baseUrl of the s3 client.
|
||||
*/
|
||||
private String endpoint;
|
||||
|
||||
/**
|
||||
* Name of the S3 bucket that contains config.
|
||||
*/
|
||||
private String bucket;
|
||||
|
||||
private int order;
|
||||
|
||||
public String getRegion() {
|
||||
return region;
|
||||
}
|
||||
|
||||
public void setRegion(String region) {
|
||||
this.region = region;
|
||||
}
|
||||
|
||||
public String getEndpoint() {
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
public void setEndpoint(String endpoint) {
|
||||
this.endpoint = endpoint;
|
||||
}
|
||||
|
||||
public String getBucket() {
|
||||
return bucket;
|
||||
}
|
||||
|
||||
public void setBucket(String bucket) {
|
||||
this.bucket = bucket;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,296 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2019 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.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import software.amazon.awssdk.core.ResponseInputStream;
|
||||
import software.amazon.awssdk.services.s3.S3Client;
|
||||
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
|
||||
import software.amazon.awssdk.services.s3.model.GetObjectResponse;
|
||||
|
||||
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
|
||||
import org.springframework.cloud.config.environment.Environment;
|
||||
import org.springframework.cloud.config.environment.PropertySource;
|
||||
import org.springframework.cloud.config.server.config.ConfigServerProperties;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.io.InputStreamResource;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* @author Clay McCoy
|
||||
* @author Scott Frederick
|
||||
* @author Daniel Aiken
|
||||
*/
|
||||
public class AwsS3EnvironmentRepository implements EnvironmentRepository, Ordered, SearchPathLocator {
|
||||
|
||||
private static final Log LOG = LogFactory.getLog(AwsS3EnvironmentRepository.class);
|
||||
|
||||
private static final String AWS_S3_RESOURCE_SCHEME = "s3://";
|
||||
|
||||
private static final String PATH_SEPARATOR = "/";
|
||||
|
||||
private final S3Client s3Client;
|
||||
|
||||
private final String bucketName;
|
||||
|
||||
private final ConfigServerProperties serverProperties;
|
||||
|
||||
protected int order = Ordered.LOWEST_PRECEDENCE;
|
||||
|
||||
public AwsS3EnvironmentRepository(S3Client s3Client, String bucketName, ConfigServerProperties server) {
|
||||
this.s3Client = s3Client;
|
||||
this.bucketName = bucketName;
|
||||
this.serverProperties = server;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return this.order;
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Environment findOne(String specifiedApplication, String specifiedProfiles, String specifiedLabel) {
|
||||
final String application = ObjectUtils.isEmpty(specifiedApplication)
|
||||
? serverProperties.getDefaultApplicationName() : specifiedApplication;
|
||||
final String profiles = ObjectUtils.isEmpty(specifiedProfiles) ? serverProperties.getDefaultProfile()
|
||||
: specifiedProfiles;
|
||||
final String label = ObjectUtils.isEmpty(specifiedLabel) ? serverProperties.getDefaultLabel() : specifiedLabel;
|
||||
|
||||
String[] profileArray = parseProfiles(profiles);
|
||||
List<String> apps = Arrays.asList(StringUtils.commaDelimitedListToStringArray(application.replace(" ", "")));
|
||||
if (!apps.contains(serverProperties.getDefaultApplicationName())) {
|
||||
apps = new ArrayList<>(apps);
|
||||
apps.add(serverProperties.getDefaultApplicationName());
|
||||
}
|
||||
|
||||
final Environment environment = new Environment(application, profileArray);
|
||||
environment.setLabel(label);
|
||||
|
||||
for (String profile : profileArray) {
|
||||
for (String app : apps) {
|
||||
addPropertySource(environment, app, profile, label);
|
||||
}
|
||||
}
|
||||
|
||||
// Add propertysources without profiles as well
|
||||
for (String app : apps) {
|
||||
addPropertySource(environment, app, null, label);
|
||||
}
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Returning Environment: " + environment);
|
||||
}
|
||||
|
||||
return environment;
|
||||
}
|
||||
|
||||
private void addPropertySource(Environment environment, String app, String profile, String label) {
|
||||
S3ConfigFile s3ConfigFile = getS3ConfigFile(app, profile, label);
|
||||
if (s3ConfigFile != null) {
|
||||
environment.setVersion(s3ConfigFile.getVersion());
|
||||
|
||||
final Properties config = s3ConfigFile.read();
|
||||
config.putAll(serverProperties.getOverrides());
|
||||
StringBuilder propertySourceName = new StringBuilder().append("s3:").append(app);
|
||||
if (profile != null) {
|
||||
propertySourceName.append("-").append(profile);
|
||||
}
|
||||
PropertySource propertySource = new PropertySource(propertySourceName.toString(), config);
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Adding property source to environment " + propertySource);
|
||||
}
|
||||
environment.add(propertySource);
|
||||
}
|
||||
}
|
||||
|
||||
private String[] parseProfiles(String profiles) {
|
||||
return StringUtils.commaDelimitedListToStringArray(profiles);
|
||||
}
|
||||
|
||||
private S3ConfigFile getS3ConfigFile(String application, String profile, String label) {
|
||||
String objectKeyPrefix = buildObjectKeyPrefix(application, profile, label);
|
||||
return getS3ConfigFile(objectKeyPrefix);
|
||||
}
|
||||
|
||||
private String buildObjectKeyPrefix(String application, String profile, String label) {
|
||||
StringBuilder objectKeyPrefix = new StringBuilder();
|
||||
if (!ObjectUtils.isEmpty(label)) {
|
||||
objectKeyPrefix.append(label).append(PATH_SEPARATOR);
|
||||
}
|
||||
objectKeyPrefix.append(application);
|
||||
if (!ObjectUtils.isEmpty(profile)) {
|
||||
objectKeyPrefix.append("-").append(profile);
|
||||
}
|
||||
return objectKeyPrefix.toString();
|
||||
}
|
||||
|
||||
private S3ConfigFile getS3ConfigFile(String keyPrefix) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Getting S3 config file for prefix " + keyPrefix);
|
||||
}
|
||||
try {
|
||||
final ResponseInputStream<GetObjectResponse> responseInputStream = getObject(keyPrefix + ".properties");
|
||||
return new PropertyS3ConfigFile(responseInputStream.response().versionId(), responseInputStream);
|
||||
}
|
||||
catch (Exception eProperties) {
|
||||
try {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Did not find " + keyPrefix + ".properties. Trying yml extension", eProperties);
|
||||
}
|
||||
final ResponseInputStream<GetObjectResponse> responseInputStream = getObject(keyPrefix + ".yml");
|
||||
return new YamlS3ConfigFile(responseInputStream.response().versionId(), responseInputStream);
|
||||
}
|
||||
catch (Exception eYml) {
|
||||
try {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Did not find " + keyPrefix + ".yml. Trying yaml extension", eYml);
|
||||
}
|
||||
final ResponseInputStream<GetObjectResponse> responseInputStream = getObject(keyPrefix + ".yaml");
|
||||
return new YamlS3ConfigFile(responseInputStream.response().versionId(), responseInputStream);
|
||||
}
|
||||
catch (Exception eYaml) {
|
||||
try {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Did not find " + keyPrefix + ".yaml. Trying json extension", eYaml);
|
||||
}
|
||||
final ResponseInputStream<GetObjectResponse> responseInputStream = getObject(
|
||||
keyPrefix + ".json");
|
||||
return new JsonS3ConfigFile(responseInputStream.response().versionId(), responseInputStream);
|
||||
}
|
||||
catch (Exception eJson) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Did not find S3 config file with properties, yml, yaml, or json extension for "
|
||||
+ keyPrefix, eJson);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ResponseInputStream<GetObjectResponse> getObject(String key) throws Exception {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Getting object with key " + key);
|
||||
}
|
||||
return s3Client.getObject(GetObjectRequest.builder().bucket(bucketName).key(key).build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locations getLocations(String application, String profiles, String label) {
|
||||
StringBuilder baseLocation = new StringBuilder(AWS_S3_RESOURCE_SCHEME + bucketName + PATH_SEPARATOR);
|
||||
if (!StringUtils.hasText(label) && StringUtils.hasText(serverProperties.getDefaultLabel())) {
|
||||
label = serverProperties.getDefaultLabel();
|
||||
}
|
||||
// both the passed in label and the default label property could be null
|
||||
if (StringUtils.hasText(label)) {
|
||||
baseLocation.append(label);
|
||||
}
|
||||
|
||||
return new Locations(application, profiles, label, null, new String[] { baseLocation.toString() });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
abstract class S3ConfigFile {
|
||||
|
||||
protected static final Log LOG = LogFactory.getLog(S3ConfigFile.class);
|
||||
|
||||
private final String version;
|
||||
|
||||
protected S3ConfigFile(String version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
abstract Properties read();
|
||||
|
||||
}
|
||||
|
||||
class PropertyS3ConfigFile extends S3ConfigFile {
|
||||
|
||||
final InputStream inputStream;
|
||||
|
||||
PropertyS3ConfigFile(String version, InputStream inputStream) {
|
||||
super(version);
|
||||
this.inputStream = inputStream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Properties read() {
|
||||
Properties props = new Properties();
|
||||
try (InputStream in = inputStream) {
|
||||
props.load(in);
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.warn("Exception thrown when reading property file", e);
|
||||
throw new IllegalStateException("Cannot load environment", e);
|
||||
}
|
||||
return props;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class YamlS3ConfigFile extends S3ConfigFile {
|
||||
|
||||
final InputStream inputStream;
|
||||
|
||||
YamlS3ConfigFile(String version, InputStream inputStream) {
|
||||
super(version);
|
||||
this.inputStream = inputStream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Properties read() {
|
||||
final YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean();
|
||||
try (InputStream in = inputStream) {
|
||||
yaml.setResources(new InputStreamResource(in));
|
||||
return yaml.getObject();
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.warn("Could not read YAML file", e);
|
||||
throw new IllegalStateException("Cannot load environment", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class JsonS3ConfigFile extends YamlS3ConfigFile {
|
||||
|
||||
// YAML is a superset of JSON, which means you can parse JSON with a YAML parser
|
||||
|
||||
JsonS3ConfigFile(String version, InputStream inputStream) {
|
||||
super(version, inputStream);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2019 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 software.amazon.awssdk.services.s3.S3Client;
|
||||
import software.amazon.awssdk.services.s3.S3ClientBuilder;
|
||||
|
||||
import org.springframework.cloud.config.server.config.ConfigServerProperties;
|
||||
|
||||
import static org.springframework.cloud.config.server.environment.AwsClientBuilderConfigurer.configureClientBuilder;
|
||||
|
||||
public class AwsS3EnvironmentRepositoryFactory
|
||||
implements EnvironmentRepositoryFactory<AwsS3EnvironmentRepository, AwsS3EnvironmentProperties> {
|
||||
|
||||
final private ConfigServerProperties server;
|
||||
|
||||
public AwsS3EnvironmentRepositoryFactory(ConfigServerProperties server) {
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AwsS3EnvironmentRepository build(AwsS3EnvironmentProperties environmentProperties) {
|
||||
final S3ClientBuilder clientBuilder = S3Client.builder();
|
||||
configureClientBuilder(clientBuilder, environmentProperties.getRegion(), environmentProperties.getEndpoint());
|
||||
final S3Client client = clientBuilder.build();
|
||||
|
||||
AwsS3EnvironmentRepository repository = new AwsS3EnvironmentRepository(client,
|
||||
environmentProperties.getBucket(), server);
|
||||
repository.setOrder(environmentProperties.getOrder());
|
||||
return repository;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2020 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 jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.cloud.config.server.support.EnvironmentRepositoryProperties;
|
||||
import org.springframework.core.Ordered;
|
||||
|
||||
/**
|
||||
* @author Tejas Pandilwar
|
||||
*/
|
||||
@ConfigurationProperties("spring.cloud.config.server.aws-secretsmanager")
|
||||
public class AwsSecretsManagerEnvironmentProperties implements EnvironmentRepositoryProperties {
|
||||
|
||||
static final String DEFAULT_PATH_SEPARATOR = "/";
|
||||
|
||||
private static final String DEFAULT_PREFIX = DEFAULT_PATH_SEPARATOR + "secret";
|
||||
|
||||
private static final String DEFAULT_PROFILE_SEPARATOR = "-";
|
||||
|
||||
private static final String DEFAULT_ORIGIN = "aws:secrets:";
|
||||
|
||||
/**
|
||||
* The region to be used by AWS Secrets Manager client.
|
||||
*/
|
||||
private String region;
|
||||
|
||||
/**
|
||||
* The endpoint to be used by AWS Secrets Manager client. This can be used to specify
|
||||
* an alternate endpoint for the API requests.
|
||||
*/
|
||||
private String endpoint;
|
||||
|
||||
/**
|
||||
* The default staging label to be used to fetch the secret values. If unset, an
|
||||
* active version of the secret will be fetched (AWSCURRENT).
|
||||
*/
|
||||
private String defaultLabel;
|
||||
|
||||
/**
|
||||
* The order of the environment repository.
|
||||
*/
|
||||
private int order = Ordered.LOWEST_PRECEDENCE;
|
||||
|
||||
/**
|
||||
* Prefix indicating first level for every property loaded from AWS Secrets Manager.
|
||||
* Value must start with a forward slash followed by a valid path segment or be empty.
|
||||
* Defaults to "/secret".
|
||||
*/
|
||||
@NotNull
|
||||
@Pattern(regexp = "(/[a-zA-Z0-9.\\-_]+)*")
|
||||
private String prefix = DEFAULT_PREFIX;
|
||||
|
||||
/**
|
||||
* String that separates profile from the application name.
|
||||
*/
|
||||
@NotNull
|
||||
@Pattern(regexp = "[a-zA-Z0-9.\\-_]+")
|
||||
private String profileSeparator = DEFAULT_PROFILE_SEPARATOR;
|
||||
|
||||
/**
|
||||
* Prefix which indicates the origin of the property. Defaults to "aws:secrets:".
|
||||
*/
|
||||
@NotNull
|
||||
private String origin = DEFAULT_ORIGIN;
|
||||
|
||||
public String getRegion() {
|
||||
return region;
|
||||
}
|
||||
|
||||
public void setRegion(String region) {
|
||||
this.region = region;
|
||||
}
|
||||
|
||||
public String getEndpoint() {
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
public void setEndpoint(String endpoint) {
|
||||
this.endpoint = endpoint;
|
||||
}
|
||||
|
||||
public String getDefaultLabel() {
|
||||
return defaultLabel;
|
||||
}
|
||||
|
||||
public void setDefaultLabel(String defaultLabel) {
|
||||
this.defaultLabel = defaultLabel;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
public String getPrefix() {
|
||||
return prefix;
|
||||
}
|
||||
|
||||
public void setPrefix(String prefix) {
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
public String getProfileSeparator() {
|
||||
return profileSeparator;
|
||||
}
|
||||
|
||||
public void setProfileSeparator(String profileSeparator) {
|
||||
this.profileSeparator = profileSeparator;
|
||||
}
|
||||
|
||||
public String getOrigin() {
|
||||
return origin;
|
||||
}
|
||||
|
||||
public void setOrigin(String origin) {
|
||||
this.origin = origin;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,172 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2020 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.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient;
|
||||
import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueRequest;
|
||||
import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueResponse;
|
||||
import software.amazon.awssdk.services.secretsmanager.model.ResourceNotFoundException;
|
||||
|
||||
import org.springframework.cloud.config.environment.Environment;
|
||||
import org.springframework.cloud.config.environment.PropertySource;
|
||||
import org.springframework.cloud.config.server.config.ConfigServerProperties;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static org.springframework.cloud.config.server.environment.AwsSecretsManagerEnvironmentProperties.DEFAULT_PATH_SEPARATOR;
|
||||
|
||||
/**
|
||||
* @author Tejas Pandilwar
|
||||
* @author KNV Srinivas
|
||||
*/
|
||||
public class AwsSecretsManagerEnvironmentRepository implements EnvironmentRepository, Ordered {
|
||||
|
||||
private static final Log log = LogFactory.getLog(AwsSecretsManagerEnvironmentRepository.class);
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
private final SecretsManagerClient awsSmClient;
|
||||
|
||||
private final ConfigServerProperties configServerProperties;
|
||||
|
||||
private final AwsSecretsManagerEnvironmentProperties environmentProperties;
|
||||
|
||||
private final int order;
|
||||
|
||||
public AwsSecretsManagerEnvironmentRepository(SecretsManagerClient awsSmClient,
|
||||
ConfigServerProperties configServerProperties,
|
||||
AwsSecretsManagerEnvironmentProperties environmentProperties) {
|
||||
this.awsSmClient = awsSmClient;
|
||||
this.configServerProperties = configServerProperties;
|
||||
this.environmentProperties = environmentProperties;
|
||||
this.order = environmentProperties.getOrder();
|
||||
this.objectMapper = new ObjectMapper();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Environment findOne(String application, String profileList, String label) {
|
||||
final String defaultApplication = configServerProperties.getDefaultApplicationName();
|
||||
final String defaultProfile = configServerProperties.getDefaultProfile();
|
||||
final String defaultLabel = environmentProperties.getDefaultLabel();
|
||||
|
||||
if (ObjectUtils.isEmpty(application)) {
|
||||
application = defaultApplication;
|
||||
}
|
||||
|
||||
if (ObjectUtils.isEmpty(profileList)) {
|
||||
profileList = defaultProfile;
|
||||
}
|
||||
|
||||
if (StringUtils.isEmpty(label)) {
|
||||
label = defaultLabel;
|
||||
}
|
||||
|
||||
String[] profiles = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(profileList));
|
||||
Environment environment = new Environment(application, profiles, label, null, null);
|
||||
|
||||
Map<String, String> overrides = configServerProperties.getOverrides();
|
||||
if (!overrides.isEmpty()) {
|
||||
environment.add(new PropertySource("overrides", overrides));
|
||||
}
|
||||
|
||||
for (String profile : profiles) {
|
||||
addPropertySource(environment, application, profile, label);
|
||||
if (!defaultApplication.equals(application)) {
|
||||
addPropertySource(environment, defaultApplication, profile, label);
|
||||
}
|
||||
}
|
||||
|
||||
if (!Arrays.asList(profiles).contains(defaultProfile)) {
|
||||
addPropertySource(environment, application, defaultProfile, label);
|
||||
}
|
||||
|
||||
if (!Arrays.asList(profiles).contains(defaultProfile) && !defaultApplication.equals(application)) {
|
||||
addPropertySource(environment, defaultApplication, defaultProfile, label);
|
||||
}
|
||||
|
||||
if (!defaultApplication.equals(application)) {
|
||||
addPropertySource(environment, application, null, label);
|
||||
}
|
||||
|
||||
addPropertySource(environment, defaultApplication, null, label);
|
||||
|
||||
return environment;
|
||||
}
|
||||
|
||||
private void addPropertySource(Environment environment, String application, String profile, String label) {
|
||||
String path = buildPath(application, profile);
|
||||
|
||||
Map<Object, Object> properties = findProperties(path, label);
|
||||
if (!properties.isEmpty()) {
|
||||
environment.add(new PropertySource(environmentProperties.getOrigin() + path, properties));
|
||||
}
|
||||
}
|
||||
|
||||
private String buildPath(String application, String profile) {
|
||||
String prefix = environmentProperties.getPrefix();
|
||||
String profileSeparator = environmentProperties.getProfileSeparator();
|
||||
|
||||
if (profile == null || profile.isEmpty()) {
|
||||
return prefix + DEFAULT_PATH_SEPARATOR + application + DEFAULT_PATH_SEPARATOR;
|
||||
}
|
||||
else {
|
||||
return prefix + DEFAULT_PATH_SEPARATOR + application + profileSeparator + profile + DEFAULT_PATH_SEPARATOR;
|
||||
}
|
||||
}
|
||||
|
||||
private Map<Object, Object> findProperties(String path, String label) {
|
||||
Map<Object, Object> properties = new HashMap<>();
|
||||
|
||||
GetSecretValueRequest request = GetSecretValueRequest.builder().secretId(path).versionStage(label).build();
|
||||
try {
|
||||
GetSecretValueResponse response = awsSmClient.getSecretValue(request);
|
||||
|
||||
if (response != null) {
|
||||
Map<String, Object> secretMap = objectMapper.readValue(response.secretString(),
|
||||
new TypeReference<Map<String, Object>>() {
|
||||
});
|
||||
|
||||
for (Map.Entry<String, Object> secretEntry : secretMap.entrySet()) {
|
||||
properties.put(secretEntry.getKey(), secretEntry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ResourceNotFoundException | IOException e) {
|
||||
log.debug(String.format(
|
||||
"Skip adding propertySource. Unable to load secrets from AWS Secrets Manager for secretId=%s",
|
||||
path), e);
|
||||
}
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2020 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 software.amazon.awssdk.services.secretsmanager.SecretsManagerClient;
|
||||
import software.amazon.awssdk.services.secretsmanager.SecretsManagerClientBuilder;
|
||||
|
||||
import org.springframework.cloud.config.server.config.ConfigServerProperties;
|
||||
|
||||
import static org.springframework.cloud.config.server.environment.AwsClientBuilderConfigurer.configureClientBuilder;
|
||||
|
||||
/**
|
||||
* @author Tejas Pandilwar
|
||||
*/
|
||||
public class AwsSecretsManagerEnvironmentRepositoryFactory implements
|
||||
EnvironmentRepositoryFactory<AwsSecretsManagerEnvironmentRepository, AwsSecretsManagerEnvironmentProperties> {
|
||||
|
||||
private final ConfigServerProperties configServerProperties;
|
||||
|
||||
public AwsSecretsManagerEnvironmentRepositoryFactory(ConfigServerProperties configServerProperties) {
|
||||
this.configServerProperties = configServerProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AwsSecretsManagerEnvironmentRepository build(AwsSecretsManagerEnvironmentProperties environmentProperties) {
|
||||
SecretsManagerClientBuilder clientBuilder = SecretsManagerClient.builder();
|
||||
|
||||
configureClientBuilder(clientBuilder, environmentProperties.getRegion(), environmentProperties.getEndpoint());
|
||||
|
||||
SecretsManagerClient client = clientBuilder.build();
|
||||
return new AwsSecretsManagerEnvironmentRepository(client, configServerProperties, environmentProperties);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2019 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.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.cloud.config.environment.Environment;
|
||||
import org.springframework.core.OrderComparator;
|
||||
|
||||
/**
|
||||
* An {@link EnvironmentRepository} composed of multiple ordered
|
||||
* {@link EnvironmentRepository}s.
|
||||
*
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class CompositeEnvironmentRepository implements EnvironmentRepository {
|
||||
|
||||
Log log = LogFactory.getLog(getClass());
|
||||
|
||||
protected List<EnvironmentRepository> environmentRepositories;
|
||||
|
||||
protected boolean failOnError;
|
||||
|
||||
/**
|
||||
* Creates a new {@link CompositeEnvironmentRepository}.
|
||||
* @param environmentRepositories The list of {@link EnvironmentRepository}s to create
|
||||
* the composite from.
|
||||
* @param observationRegistry observation registry
|
||||
* @param failOnError whether to throw an exception if there is an error.
|
||||
*/
|
||||
public CompositeEnvironmentRepository(List<EnvironmentRepository> environmentRepositories,
|
||||
ObservationRegistry observationRegistry, boolean failOnError) {
|
||||
// Sort the environment repositories by the priority
|
||||
Collections.sort(environmentRepositories, OrderComparator.INSTANCE);
|
||||
this.environmentRepositories = observationRegistry.isNoop() ? environmentRepositories
|
||||
: environmentRepositories.stream()
|
||||
.map(e -> ObservationEnvironmentRepositoryWrapper.wrap(observationRegistry, e))
|
||||
.collect(Collectors.toList());
|
||||
this.failOnError = failOnError;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link CompositeEnvironmentRepository}.
|
||||
* @param environmentRepositories The list of {@link EnvironmentRepository}s to create
|
||||
* the composite from.
|
||||
* @param failOnError whether to throw an exception if there is an error.
|
||||
*/
|
||||
public CompositeEnvironmentRepository(List<EnvironmentRepository> environmentRepositories, boolean failOnError) {
|
||||
// Sort the environment repositories by the priority
|
||||
this(environmentRepositories, ObservationRegistry.NOOP, failOnError);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Environment findOne(String application, String profile, String label) {
|
||||
return findOne(application, profile, label, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Environment findOne(String application, String profile, String label, boolean includeOrigin) {
|
||||
Environment env = new Environment(application, new String[] { profile }, label, null, null);
|
||||
if (this.environmentRepositories.size() == 1) {
|
||||
Environment envRepo = this.environmentRepositories.get(0).findOne(application, profile, label,
|
||||
includeOrigin);
|
||||
env.addAll(envRepo.getPropertySources());
|
||||
env.setVersion(envRepo.getVersion());
|
||||
env.setState(envRepo.getState());
|
||||
}
|
||||
else {
|
||||
for (EnvironmentRepository repo : environmentRepositories) {
|
||||
try {
|
||||
env.addAll(repo.findOne(application, profile, label, includeOrigin).getPropertySources());
|
||||
}
|
||||
catch (Exception e) {
|
||||
if (failOnError) {
|
||||
throw e;
|
||||
}
|
||||
else {
|
||||
log.info("Error adding environment for " + repo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return env;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2019 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;
|
||||
|
||||
/**
|
||||
* @author Scott Frederick
|
||||
*/
|
||||
public interface ConfigTokenProvider {
|
||||
|
||||
String getToken();
|
||||
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2019 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.eclipse.jgit.transport.http.HttpConnectionFactory;
|
||||
|
||||
/**
|
||||
* @author Dylan Roberts
|
||||
*/
|
||||
public interface ConfigurableHttpConnectionFactory extends HttpConnectionFactory {
|
||||
|
||||
void addConfiguration(MultipleJGitEnvironmentProperties environmentProperties) throws Exception;
|
||||
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2019 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.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.client.HttpStatusCodeException;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@ConfigurationProperties("spring.cloud.config.server.consul.watch")
|
||||
public class ConsulEnvironmentWatch implements EnvironmentWatch {
|
||||
|
||||
/**
|
||||
* Consul index header name.
|
||||
*/
|
||||
public static final String CONSUL_INDEX = "X-Consul-Index";
|
||||
|
||||
/**
|
||||
* Consul token header name.
|
||||
*/
|
||||
public static final String CONSUL_TOKEN = "X-Consul-Token";
|
||||
|
||||
/**
|
||||
* Response types.
|
||||
*/
|
||||
public static final ParameterizedTypeReference<List<String>> RESPONSE_TYPE = new ParameterizedTypeReference<List<String>>() {
|
||||
};
|
||||
|
||||
private static final String WATCH_URL = "{scheme}://{host}:{port}/v1/kv/{path}?keys&recurse&wait={wait}&index={index}";
|
||||
|
||||
private static Log LOG = LogFactory.getLog(ConsulEnvironmentWatch.class);
|
||||
|
||||
private RestTemplate restTemplate = new RestTemplate();
|
||||
|
||||
/** Consul agent scheme. Defaults to 'http'. */
|
||||
@NotNull
|
||||
private String scheme = "http";
|
||||
|
||||
/** Consul agent hostname. Defaults to 'localhost'. */
|
||||
@NotNull
|
||||
private String host = "localhost";
|
||||
|
||||
/** Consul agent port. Defaults to '8500'. */
|
||||
@NotNull
|
||||
private int port = 8500;
|
||||
|
||||
/** Path to watch in consul key/value store. */
|
||||
@NotNull
|
||||
private String path;
|
||||
|
||||
/** Consul wait value (eg, 3m or 30s). */
|
||||
@NotNull
|
||||
private String wait = "3m";
|
||||
|
||||
/** Consul ACL token. */
|
||||
private String token;
|
||||
|
||||
@Override
|
||||
public String watch(String state) {
|
||||
ArrayList<String> params = new ArrayList<>();
|
||||
params.add(this.scheme);
|
||||
params.add(this.host);
|
||||
params.add(String.valueOf(this.port));
|
||||
params.add(this.path);
|
||||
params.add(this.wait);
|
||||
params.add(StringUtils.hasText(state) ? state : "");
|
||||
|
||||
try {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
if (StringUtils.hasText(this.token)) {
|
||||
headers.add(CONSUL_TOKEN, this.token);
|
||||
}
|
||||
HttpEntity<Object> request = new HttpEntity<>(headers);
|
||||
ResponseEntity<List<String>> response = this.restTemplate.exchange(WATCH_URL, HttpMethod.GET, request,
|
||||
RESPONSE_TYPE, params.toArray());
|
||||
|
||||
if (response.getStatusCode().is2xxSuccessful()) {
|
||||
String consulIndex = response.getHeaders().getFirst(CONSUL_INDEX);
|
||||
return consulIndex;
|
||||
}
|
||||
}
|
||||
catch (HttpStatusCodeException e) {
|
||||
if (!e.getStatusCode().equals(HttpStatus.NOT_FOUND)) {
|
||||
LOG.error("Unable to watch consul path " + this.path, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
// TODO: error handling?
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setRestTemplate(RestTemplate restTemplate) {
|
||||
this.restTemplate = restTemplate;
|
||||
}
|
||||
|
||||
public void setScheme(String scheme) {
|
||||
this.scheme = scheme;
|
||||
}
|
||||
|
||||
public void setHost(String host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public void setPath(String path) {
|
||||
this.path = path;
|
||||
if (this.path.startsWith("/")) {
|
||||
this.path = this.path.substring(1);
|
||||
}
|
||||
}
|
||||
|
||||
public void setWait(String wait) {
|
||||
this.wait = wait;
|
||||
}
|
||||
|
||||
public void setToken(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2019 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;
|
||||
import org.springframework.core.Ordered;
|
||||
|
||||
/**
|
||||
* @author Alberto C. Ríos
|
||||
*/
|
||||
@ConfigurationProperties("spring.cloud.config.server.credhub")
|
||||
public class CredhubEnvironmentProperties implements EnvironmentRepositoryProperties {
|
||||
|
||||
private int order = Ordered.LOWEST_PRECEDENCE;
|
||||
|
||||
public int getOrder() {
|
||||
return this.order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2019 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.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.config.environment.Environment;
|
||||
import org.springframework.cloud.config.environment.PropertySource;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.credhub.core.CredHubOperations;
|
||||
import org.springframework.credhub.support.CredentialDetails;
|
||||
import org.springframework.credhub.support.SimpleCredentialName;
|
||||
import org.springframework.credhub.support.json.JsonCredential;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static java.util.stream.Collectors.toMap;
|
||||
|
||||
/**
|
||||
* @author Alberto C. Ríos
|
||||
* @author KNV Srinivas
|
||||
*/
|
||||
public class CredhubEnvironmentRepository implements EnvironmentRepository, Ordered {
|
||||
|
||||
private CredHubOperations credHubOperations;
|
||||
|
||||
private static final String DEFAULT_PROFILE = "default";
|
||||
|
||||
private static final String DEFAULT_LABEL = "master";
|
||||
|
||||
private static final String DEFAULT_APPLICATION = "application";
|
||||
|
||||
private int order = Ordered.LOWEST_PRECEDENCE;
|
||||
|
||||
public CredhubEnvironmentRepository(CredHubOperations credHubOperations) {
|
||||
this.credHubOperations = credHubOperations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Environment findOne(String application, String profilesList, String label) {
|
||||
if (ObjectUtils.isEmpty(profilesList)) {
|
||||
profilesList = DEFAULT_PROFILE;
|
||||
}
|
||||
if (ObjectUtils.isEmpty(label)) {
|
||||
label = DEFAULT_LABEL;
|
||||
}
|
||||
|
||||
String[] profiles = StringUtils.commaDelimitedListToStringArray(profilesList);
|
||||
|
||||
Environment environment = new Environment(application, profiles, label, null, null);
|
||||
for (String profile : profiles) {
|
||||
environment.add(new PropertySource("credhub-" + application + "-" + profile + "-" + label,
|
||||
findProperties(application, profile, label)));
|
||||
if (!DEFAULT_APPLICATION.equals(application)) {
|
||||
addDefaultPropertySource(environment, DEFAULT_APPLICATION, profile, label);
|
||||
}
|
||||
}
|
||||
|
||||
if (!Arrays.asList(profiles).contains(DEFAULT_PROFILE)) {
|
||||
addDefaultPropertySource(environment, application, DEFAULT_PROFILE, label);
|
||||
}
|
||||
|
||||
if (!Arrays.asList(profiles).contains(DEFAULT_PROFILE) && !DEFAULT_APPLICATION.equals(application)) {
|
||||
addDefaultPropertySource(environment, DEFAULT_APPLICATION, DEFAULT_PROFILE, label);
|
||||
}
|
||||
|
||||
return environment;
|
||||
}
|
||||
|
||||
private void addDefaultPropertySource(Environment environment, String application, String profile, String label) {
|
||||
Map<Object, Object> properties = findProperties(application, profile, label);
|
||||
if (!properties.isEmpty()) {
|
||||
PropertySource propertySource = new PropertySource("credhub-" + application + "-" + profile + "-" + label,
|
||||
properties);
|
||||
environment.add(propertySource);
|
||||
}
|
||||
}
|
||||
|
||||
private Map<Object, Object> findProperties(String application, String profile, String label) {
|
||||
String path = "/" + application + "/" + profile + "/" + label;
|
||||
|
||||
return this.credHubOperations.credentials().findByPath(path).stream()
|
||||
.map(credentialSummary -> credentialSummary.getName().getName())
|
||||
.map(name -> this.credHubOperations.credentials().getByName(new SimpleCredentialName(name),
|
||||
JsonCredential.class))
|
||||
.map(CredentialDetails::getValue).flatMap(jsonCredential -> jsonCredential.entrySet().stream())
|
||||
.collect(toMap(Map.Entry::getKey, Map.Entry::getValue, (a, b) -> b));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2019 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.credhub.core.CredHubOperations;
|
||||
|
||||
/**
|
||||
* @author Alberto C. Ríos
|
||||
* @author KNV Srinivas
|
||||
*/
|
||||
public class CredhubEnvironmentRepositoryFactory
|
||||
implements EnvironmentRepositoryFactory<CredhubEnvironmentRepository, CredhubEnvironmentProperties> {
|
||||
|
||||
private CredHubOperations credhubOperations;
|
||||
|
||||
public CredhubEnvironmentRepositoryFactory(CredHubOperations credhubOperations) {
|
||||
this.credhubOperations = credhubOperations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CredhubEnvironmentRepository build(CredhubEnvironmentProperties environmentProperties) {
|
||||
CredhubEnvironmentRepository repository = new CredhubEnvironmentRepository(this.credhubOperations);
|
||||
repository.setOrder(environmentProperties.getOrder());
|
||||
return repository;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2021 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 io.micrometer.common.docs.KeyName;
|
||||
import io.micrometer.observation.Observation;
|
||||
import io.micrometer.observation.ObservationConvention;
|
||||
import io.micrometer.observation.docs.ObservationDocumentation;
|
||||
|
||||
enum DocumentedConfigObservation implements ObservationDocumentation {
|
||||
|
||||
/**
|
||||
* Observation created around an EnvironmentRepository.
|
||||
*/
|
||||
ENVIRONMENT_REPOSITORY {
|
||||
@Override
|
||||
public Class<? extends ObservationConvention<? extends Observation.Context>> getDefaultConvention() {
|
||||
return ObservationEnvironmentRepositoryObservationConvention.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyName[] getLowCardinalityKeyNames() {
|
||||
return LowCardinalityTags.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefix() {
|
||||
return "spring.cloud.config.environment";
|
||||
}
|
||||
};
|
||||
|
||||
enum LowCardinalityTags implements KeyName {
|
||||
|
||||
/**
|
||||
* Implementation of the EnvironmentRepository.
|
||||
*/
|
||||
ENVIRONMENT_CLASS {
|
||||
@Override
|
||||
public String asString() {
|
||||
return "spring.cloud.config.environment.class";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Application name for which properties are being queried for.
|
||||
*/
|
||||
PROFILE {
|
||||
@Override
|
||||
public String asString() {
|
||||
return "spring.cloud.config.environment.profile";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Label for which properties are being queried for.
|
||||
*/
|
||||
LABEL {
|
||||
@Override
|
||||
public String asString() {
|
||||
return "spring.cloud.config.environment.label";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Application name for which properties are being queried for.
|
||||
*/
|
||||
APPLICATION {
|
||||
@Override
|
||||
public String asString() {
|
||||
return "spring.cloud.config.environment.application";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2019 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.Map;
|
||||
|
||||
import org.springframework.cloud.config.environment.Environment;
|
||||
import org.springframework.cloud.config.environment.PropertySource;
|
||||
import org.springframework.cloud.config.environment.PropertyValueDescriptor;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
* @author Michael Prankl
|
||||
*/
|
||||
public class EnvironmentCleaner {
|
||||
|
||||
public Environment clean(Environment value, String workingDir, String uri) {
|
||||
Environment result = new Environment(value);
|
||||
for (PropertySource source : value.getPropertySources()) {
|
||||
String name = source.getName().replace(workingDir, "");
|
||||
name = name.replace("applicationConfig: [", "");
|
||||
name = uri + "/" + name.replace("]", "");
|
||||
result.add(new PropertySource(name, clean(source.getSource(), uri)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected Map<?, ?> clean(Map<?, ?> source, String uri) {
|
||||
for (Map.Entry<?, ?> entry : source.entrySet()) {
|
||||
if (entry.getValue() instanceof PropertyValueDescriptor) {
|
||||
PropertyValueDescriptor descriptor = (PropertyValueDescriptor) entry.getValue();
|
||||
if (!uri.endsWith("/")) {
|
||||
uri = uri + "/";
|
||||
}
|
||||
String updated = descriptor.getOrigin().replace("[", "[" + uri);
|
||||
descriptor.setOrigin(updated);
|
||||
}
|
||||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2019 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.core.env.Environment;
|
||||
|
||||
/**
|
||||
* @author Scott Frederick
|
||||
*/
|
||||
public class EnvironmentConfigTokenProvider implements ConfigTokenProvider {
|
||||
|
||||
private final Environment environment;
|
||||
|
||||
private final String propertyName;
|
||||
|
||||
public EnvironmentConfigTokenProvider(Environment environment, String propertyName) {
|
||||
this.environment = environment;
|
||||
this.propertyName = propertyName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getToken() {
|
||||
return environment.getProperty(propertyName);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,517 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2022 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.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.yaml.snakeyaml.DumperOptions.FlowStyle;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
import org.yaml.snakeyaml.nodes.Tag;
|
||||
|
||||
import org.springframework.cloud.config.environment.Environment;
|
||||
import org.springframework.cloud.config.environment.EnvironmentMediaType;
|
||||
import org.springframework.cloud.config.environment.PropertySource;
|
||||
import org.springframework.cloud.config.server.support.PathUtils;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import static org.springframework.cloud.config.server.support.EnvironmentPropertySource.prepareEnvironment;
|
||||
import static org.springframework.cloud.config.server.support.EnvironmentPropertySource.resolvePlaceholders;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
* @author Spencer Gibb
|
||||
* @author Roy Clarkson
|
||||
* @author Bartosz Wojtkiewicz
|
||||
* @author Rafal Zukowski
|
||||
* @author Ivan Corrales Solera
|
||||
* @author Daniel Frey
|
||||
* @author Ian Bondoc
|
||||
* @author Chen Li
|
||||
*
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping(method = RequestMethod.GET, path = "${spring.cloud.config.server.prefix:}")
|
||||
public class EnvironmentController {
|
||||
|
||||
private static final Log LOG = LogFactory.getLog(EnvironmentController.class);
|
||||
|
||||
private EnvironmentRepository repository;
|
||||
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
private boolean stripDocument = true;
|
||||
|
||||
private boolean acceptEmpty = true;
|
||||
|
||||
public EnvironmentController(EnvironmentRepository repository) {
|
||||
this(repository, new ObjectMapper());
|
||||
}
|
||||
|
||||
public EnvironmentController(EnvironmentRepository repository, ObjectMapper objectMapper) {
|
||||
this.repository = repository;
|
||||
this.objectMapper = objectMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag to indicate that YAML documents which are not a map should be stripped of the
|
||||
* "document" prefix that is added by Spring (to facilitate conversion to Properties).
|
||||
* @param stripDocument the flag to set
|
||||
*/
|
||||
public void setStripDocumentFromYaml(boolean stripDocument) {
|
||||
this.stripDocument = stripDocument;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag to indicate that If HTTP 404 needs to be sent if Application is not Found.
|
||||
* @param acceptEmpty the flag to set
|
||||
*/
|
||||
public void setAcceptEmpty(boolean acceptEmpty) {
|
||||
this.acceptEmpty = acceptEmpty;
|
||||
}
|
||||
|
||||
@GetMapping(path = "/{name}/{profiles:(?!.*\\b\\.(?:ya?ml|properties|json)\\b).*}",
|
||||
produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public Environment defaultLabel(@PathVariable String name, @PathVariable String profiles) {
|
||||
return getEnvironment(name, profiles, null, false);
|
||||
}
|
||||
|
||||
@GetMapping(path = "/{name}/{profiles:(?!.*\\b\\.(?:ya?ml|properties|json)\\b).*}",
|
||||
produces = EnvironmentMediaType.V2_JSON)
|
||||
public Environment defaultLabelIncludeOrigin(@PathVariable String name, @PathVariable String profiles) {
|
||||
return getEnvironment(name, profiles, null, true);
|
||||
}
|
||||
|
||||
@GetMapping(path = "/{name}/{profiles}/{label:.*}", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public Environment labelled(@PathVariable String name, @PathVariable String profiles, @PathVariable String label) {
|
||||
return getEnvironment(name, profiles, label, false);
|
||||
}
|
||||
|
||||
@GetMapping(path = "/{name}/{profiles}/{label:.*}", produces = EnvironmentMediaType.V2_JSON)
|
||||
public Environment labelledIncludeOrigin(@PathVariable String name, @PathVariable String profiles,
|
||||
@PathVariable String label) {
|
||||
return getEnvironment(name, profiles, label, true);
|
||||
}
|
||||
|
||||
public Environment getEnvironment(String name, String profiles, String label, boolean includeOrigin) {
|
||||
try {
|
||||
name = normalize(name);
|
||||
label = normalize(label);
|
||||
Environment environment = this.repository.findOne(name, profiles, label, includeOrigin);
|
||||
if (!this.acceptEmpty && (environment == null || environment.getPropertySources().isEmpty())) {
|
||||
throw new EnvironmentNotFoundException("Profile Not found");
|
||||
}
|
||||
return environment;
|
||||
}
|
||||
catch (Exception e) {
|
||||
LOG.warn(String.format("Error getting the Environment with name=%s profiles=%s label=%s includeOrigin=%b",
|
||||
name, profiles, label, includeOrigin), e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private String normalize(String part) {
|
||||
if (PathUtils.isInvalidEncodedLocation(part)) {
|
||||
throw new InvalidEnvironmentRequestException("Invalid request");
|
||||
}
|
||||
return Environment.normalize(part);
|
||||
}
|
||||
|
||||
@GetMapping("/{name}-{profiles}.properties")
|
||||
public ResponseEntity<String> properties(@PathVariable String name, @PathVariable String profiles,
|
||||
@RequestParam(defaultValue = "true") boolean resolvePlaceholders) throws IOException {
|
||||
return labelledProperties(name, profiles, null, resolvePlaceholders);
|
||||
}
|
||||
|
||||
@GetMapping("/{label}/{name}-{profiles}.properties")
|
||||
public ResponseEntity<String> labelledProperties(@PathVariable String name, @PathVariable String profiles,
|
||||
@PathVariable String label, @RequestParam(defaultValue = "true") boolean resolvePlaceholders)
|
||||
throws IOException {
|
||||
validateProfiles(profiles);
|
||||
Environment environment = labelled(name, profiles, label);
|
||||
Map<String, Object> properties = convertToProperties(environment);
|
||||
String propertiesString = getPropertiesString(properties);
|
||||
if (resolvePlaceholders) {
|
||||
propertiesString = resolvePlaceholders(prepareEnvironment(environment), propertiesString);
|
||||
}
|
||||
return getSuccess(propertiesString);
|
||||
}
|
||||
|
||||
@GetMapping("{name}-{profiles}.json")
|
||||
public ResponseEntity<String> jsonProperties(@PathVariable String name, @PathVariable String profiles,
|
||||
@RequestParam(defaultValue = "true") boolean resolvePlaceholders) throws Exception {
|
||||
return labelledJsonProperties(name, profiles, null, resolvePlaceholders);
|
||||
}
|
||||
|
||||
@GetMapping("/{label}/{name}-{profiles}.json")
|
||||
public ResponseEntity<String> labelledJsonProperties(@PathVariable String name, @PathVariable String profiles,
|
||||
@PathVariable String label, @RequestParam(defaultValue = "true") boolean resolvePlaceholders)
|
||||
throws Exception {
|
||||
validateProfiles(profiles);
|
||||
Environment environment = labelled(name, profiles, label);
|
||||
Map<String, Object> properties = convertToMap(environment);
|
||||
String json = this.objectMapper.writeValueAsString(properties);
|
||||
if (resolvePlaceholders) {
|
||||
json = resolvePlaceholders(prepareEnvironment(environment), json);
|
||||
}
|
||||
return getSuccess(json, MediaType.APPLICATION_JSON);
|
||||
}
|
||||
|
||||
private String getPropertiesString(Map<String, Object> properties) {
|
||||
StringBuilder output = new StringBuilder();
|
||||
for (Entry<String, Object> entry : properties.entrySet()) {
|
||||
if (output.length() > 0) {
|
||||
output.append("\n");
|
||||
}
|
||||
output.append(entry.getKey()).append(": ").append(entry.getValue());
|
||||
}
|
||||
return output.toString();
|
||||
}
|
||||
|
||||
@GetMapping({ "/{name}-{profiles}.yml", "/{name}-{profiles}.yaml" })
|
||||
public ResponseEntity<String> yaml(@PathVariable String name, @PathVariable String profiles,
|
||||
@RequestParam(defaultValue = "true") boolean resolvePlaceholders) throws Exception {
|
||||
return labelledYaml(name, profiles, null, resolvePlaceholders);
|
||||
}
|
||||
|
||||
@GetMapping({ "/{label}/{name}-{profiles}.yml", "/{label}/{name}-{profiles}.yaml" })
|
||||
public ResponseEntity<String> labelledYaml(@PathVariable String name, @PathVariable String profiles,
|
||||
@PathVariable String label, @RequestParam(defaultValue = "true") boolean resolvePlaceholders)
|
||||
throws Exception {
|
||||
validateProfiles(profiles);
|
||||
Environment environment = labelled(name, profiles, label);
|
||||
Map<String, Object> result = convertToMap(environment);
|
||||
if (this.stripDocument && result.size() == 1 && result.keySet().iterator().next().equals("document")) {
|
||||
Object value = result.get("document");
|
||||
if (value instanceof Collection) {
|
||||
return getSuccess(new Yaml().dumpAs(value, Tag.SEQ, FlowStyle.BLOCK));
|
||||
}
|
||||
else {
|
||||
return getSuccess(new Yaml().dumpAs(value, Tag.STR, FlowStyle.BLOCK));
|
||||
}
|
||||
}
|
||||
String yaml = new Yaml().dumpAsMap(result);
|
||||
|
||||
if (resolvePlaceholders) {
|
||||
yaml = resolvePlaceholders(prepareEnvironment(environment), yaml);
|
||||
}
|
||||
|
||||
return getSuccess(yaml);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method {@code convertToMap} converts an {@code Environment} to a nested Map which
|
||||
* represents a yml/json structure.
|
||||
* @param input the environment to be converted
|
||||
* @return the nested map containing the environment's properties
|
||||
*/
|
||||
private Map<String, Object> convertToMap(Environment input) {
|
||||
// First use the current convertToProperties to get a flat Map from the
|
||||
// environment
|
||||
Map<String, Object> properties = convertToProperties(input);
|
||||
|
||||
// The root map which holds all the first level properties
|
||||
Map<String, Object> rootMap = new LinkedHashMap<>();
|
||||
for (Map.Entry<String, Object> entry : properties.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
PropertyNavigator nav = new PropertyNavigator(key);
|
||||
nav.setMapValue(rootMap, value);
|
||||
}
|
||||
return rootMap;
|
||||
}
|
||||
|
||||
@ExceptionHandler(RepositoryException.class)
|
||||
public void noSuchLabel(HttpServletResponse response) throws IOException {
|
||||
response.sendError(HttpStatus.NOT_FOUND.value());
|
||||
}
|
||||
|
||||
@ExceptionHandler(IllegalArgumentException.class)
|
||||
public void illegalArgument(HttpServletResponse response) throws IOException {
|
||||
response.sendError(HttpStatus.BAD_REQUEST.value());
|
||||
}
|
||||
|
||||
@ExceptionHandler(EnvironmentException.class)
|
||||
public void environmentException(HttpServletResponse response, EnvironmentException e) throws IOException {
|
||||
response.sendError(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage());
|
||||
}
|
||||
|
||||
private void validateProfiles(String profiles) {
|
||||
if (profiles.contains("-")) {
|
||||
throw new IllegalArgumentException(
|
||||
"Properties output not supported for name or profiles containing hyphens");
|
||||
}
|
||||
}
|
||||
|
||||
private HttpHeaders getHttpHeaders(MediaType mediaType) {
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
httpHeaders.setContentType(mediaType);
|
||||
return httpHeaders;
|
||||
}
|
||||
|
||||
private ResponseEntity<String> getSuccess(String body) {
|
||||
return new ResponseEntity<>(body, getHttpHeaders(MediaType.TEXT_PLAIN), HttpStatus.OK);
|
||||
}
|
||||
|
||||
private ResponseEntity<String> getSuccess(String body, MediaType mediaType) {
|
||||
return new ResponseEntity<>(body, getHttpHeaders(mediaType), HttpStatus.OK);
|
||||
}
|
||||
|
||||
private Map<String, Object> convertToProperties(Environment profiles) {
|
||||
List<PropertySource> sources = new ArrayList<>(profiles.getPropertySources());
|
||||
Collections.reverse(sources);
|
||||
Map<String, Object> combinedMap = new LinkedHashMap<>();
|
||||
Map<String, Map<String, Object>> arrayMap = new LinkedHashMap<>();
|
||||
for (PropertySource source : sources) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> value = (Map<String, Object>) source.getSource();
|
||||
Map<String, Map<String, Object>> currentArrayMap = new LinkedHashMap<>();
|
||||
|
||||
for (Entry<String, Object> entry : value.entrySet()) {
|
||||
if (!entry.getKey().contains("[")) {
|
||||
combinedMap.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
else {
|
||||
String prefixKey = entry.getKey().substring(0, entry.getKey().indexOf("["));
|
||||
currentArrayMap.computeIfAbsent(prefixKey, k -> new LinkedHashMap<>()).put(entry.getKey(),
|
||||
entry.getValue());
|
||||
}
|
||||
}
|
||||
// Override array properties by prefix key
|
||||
arrayMap.putAll(currentArrayMap);
|
||||
}
|
||||
|
||||
// Combine all unique keys for array values into the combined map
|
||||
for (Entry<String, Map<String, Object>> entry : arrayMap.entrySet()) {
|
||||
combinedMap.putAll(entry.getValue());
|
||||
}
|
||||
|
||||
postProcessProperties(combinedMap);
|
||||
return combinedMap;
|
||||
}
|
||||
|
||||
private void postProcessProperties(Map<String, Object> propertiesMap) {
|
||||
propertiesMap.keySet().removeIf(key -> key.equals("spring.profiles"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Class {@code PropertyNavigator} is used to navigate through the property key and
|
||||
* create necessary Maps and Lists making up the nested structure to finally set the
|
||||
* property value at the leaf node.
|
||||
* <p>
|
||||
* The following rules in yml/json are implemented: <pre>
|
||||
* 1. an array element can be:
|
||||
* - a value (leaf)
|
||||
* - a map
|
||||
* - a nested array
|
||||
* 2. a map value can be:
|
||||
* - a value (leaf)
|
||||
* - a nested map
|
||||
* - an array
|
||||
* </pre>
|
||||
*/
|
||||
private static final class PropertyNavigator {
|
||||
|
||||
private final String propertyKey;
|
||||
|
||||
// Supports keys like org.x and org.x.y like in boot logging
|
||||
private String prefix = "";
|
||||
|
||||
private int currentPos;
|
||||
|
||||
private NodeType valueType;
|
||||
|
||||
private PropertyNavigator(String propertyKey) {
|
||||
this.propertyKey = propertyKey;
|
||||
this.currentPos = -1;
|
||||
this.valueType = NodeType.MAP;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void setMapValue(Map<String, Object> map, Object value) {
|
||||
String key = getKey();
|
||||
if (NodeType.MAP.equals(this.valueType)) {
|
||||
Map<String, Object> nestedMap;
|
||||
if (map.get(key) instanceof Map) {
|
||||
nestedMap = (Map<String, Object>) map.get(key);
|
||||
}
|
||||
else if (map.get(key) != null) {
|
||||
// not an object, set prefix for later
|
||||
prefix = key + ".";
|
||||
nestedMap = map;
|
||||
}
|
||||
else {
|
||||
// value of key is null
|
||||
nestedMap = new LinkedHashMap<>();
|
||||
map.put(key, nestedMap);
|
||||
}
|
||||
setMapValue(nestedMap, value);
|
||||
}
|
||||
else if (NodeType.ARRAY.equals(this.valueType)) {
|
||||
List<Object> list = (List<Object>) map.get(key);
|
||||
if (list == null) {
|
||||
list = new ArrayList<>();
|
||||
map.put(key, list);
|
||||
}
|
||||
setListValue(list, value);
|
||||
}
|
||||
else {
|
||||
// use compound prefix
|
||||
map.put(prefix + key, value);
|
||||
}
|
||||
}
|
||||
|
||||
private void setListValue(List<Object> list, Object value) {
|
||||
int index = getIndex();
|
||||
// Fill missing elements if needed
|
||||
while (list.size() <= index) {
|
||||
list.add(null);
|
||||
}
|
||||
if (NodeType.MAP.equals(this.valueType)) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> map = (Map<String, Object>) list.get(index);
|
||||
if (map == null) {
|
||||
map = new LinkedHashMap<>();
|
||||
list.set(index, map);
|
||||
}
|
||||
setMapValue(map, value);
|
||||
}
|
||||
else if (NodeType.ARRAY.equals(this.valueType)) {
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Object> nestedList = (List<Object>) list.get(index);
|
||||
if (nestedList == null) {
|
||||
nestedList = new ArrayList<>();
|
||||
list.set(index, nestedList);
|
||||
}
|
||||
setListValue(nestedList, value);
|
||||
}
|
||||
else {
|
||||
list.set(index, value);
|
||||
}
|
||||
}
|
||||
|
||||
private int getIndex() {
|
||||
// Consider [
|
||||
int start = this.currentPos + 1;
|
||||
|
||||
for (int i = start; i < this.propertyKey.length(); i++) {
|
||||
char c = this.propertyKey.charAt(i);
|
||||
if (c == ']') {
|
||||
this.currentPos = i;
|
||||
break;
|
||||
}
|
||||
else if (!Character.isDigit(c)) {
|
||||
throw new IllegalArgumentException("Invalid key: " + this.propertyKey);
|
||||
}
|
||||
}
|
||||
// If no closing ] or if '[]'
|
||||
if (this.currentPos < start || this.currentPos == start) {
|
||||
throw new IllegalArgumentException("Invalid key: " + this.propertyKey);
|
||||
}
|
||||
else {
|
||||
int index = Integer.parseInt(this.propertyKey.substring(start, this.currentPos));
|
||||
// Skip the closing ]
|
||||
this.currentPos++;
|
||||
if (this.currentPos == this.propertyKey.length()) {
|
||||
this.valueType = NodeType.LEAF;
|
||||
}
|
||||
else {
|
||||
switch (this.propertyKey.charAt(this.currentPos)) {
|
||||
case '.':
|
||||
this.valueType = NodeType.MAP;
|
||||
break;
|
||||
case '[':
|
||||
this.valueType = NodeType.ARRAY;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid key: " + this.propertyKey);
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
private String getKey() {
|
||||
// Consider initial value or previous char '.' or '['
|
||||
int start = this.currentPos + 1;
|
||||
int openingBracketPosition = -1;
|
||||
for (int i = start; i < this.propertyKey.length(); i++) {
|
||||
char currentChar = this.propertyKey.charAt(i);
|
||||
if (currentChar == '.') {
|
||||
this.valueType = NodeType.MAP;
|
||||
this.currentPos = i;
|
||||
break;
|
||||
}
|
||||
else if (currentChar == '[') {
|
||||
openingBracketPosition = i;
|
||||
}
|
||||
else if (currentChar == ']') {
|
||||
String bracketContents = this.propertyKey.substring(openingBracketPosition + 1, i);
|
||||
try {
|
||||
Integer.parseInt(bracketContents);
|
||||
this.valueType = NodeType.ARRAY;
|
||||
this.currentPos = openingBracketPosition;
|
||||
break;
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
// This means the key contains a [ and a ] but the contents were
|
||||
// not an integer so it's not an array
|
||||
}
|
||||
}
|
||||
}
|
||||
// If there's no delimiter then it's a key of a leaf
|
||||
if (this.currentPos < start) {
|
||||
this.currentPos = this.propertyKey.length();
|
||||
this.valueType = NodeType.LEAF;
|
||||
// Else if we encounter '..' or '.[' or start of the property is . or [
|
||||
// then it's invalid
|
||||
}
|
||||
else if (this.currentPos == start) {
|
||||
throw new IllegalArgumentException("Invalid key: " + this.propertyKey);
|
||||
}
|
||||
return this.propertyKey.substring(start, this.currentPos);
|
||||
}
|
||||
|
||||
private enum NodeType {
|
||||
|
||||
LEAF, MAP, ARRAY
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015-2019 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.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
|
||||
import org.springframework.cloud.config.environment.Environment;
|
||||
import org.springframework.cloud.config.environment.PropertySource;
|
||||
import org.springframework.cloud.config.environment.PropertyValueDescriptor;
|
||||
import org.springframework.cloud.config.server.encryption.EnvironmentEncryptor;
|
||||
|
||||
/**
|
||||
* A delegating {@link EnvironmentRepository} that can decrypt the properties if an
|
||||
* {@link EnvironmentEncryptor} is provided.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class EnvironmentEncryptorEnvironmentRepository implements EnvironmentRepository {
|
||||
|
||||
private EnvironmentRepository delegate;
|
||||
|
||||
private final List<EnvironmentEncryptor> environmentEncryptors;
|
||||
|
||||
private Map<String, String> overrides = new LinkedHashMap<>();
|
||||
|
||||
public EnvironmentEncryptorEnvironmentRepository(EnvironmentRepository delegate,
|
||||
ObservationRegistry observationRegistry) {
|
||||
this(delegate, null, observationRegistry);
|
||||
}
|
||||
|
||||
public EnvironmentEncryptorEnvironmentRepository(EnvironmentRepository delegate,
|
||||
List<EnvironmentEncryptor> environmentEncryptors, ObservationRegistry observationRegistry) {
|
||||
this.delegate = ObservationEnvironmentRepositoryWrapper.wrap(observationRegistry, delegate);
|
||||
this.environmentEncryptors = environmentEncryptors;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Environment findOne(String name, String profiles, String label) {
|
||||
return findOne(name, profiles, label, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Environment findOne(String name, String profiles, String label, boolean includeOrigin) {
|
||||
Environment environment = this.delegate.findOne(name, profiles, label, includeOrigin);
|
||||
if (this.environmentEncryptors != null) {
|
||||
for (EnvironmentEncryptor environmentEncryptor : environmentEncryptors) {
|
||||
environment = environmentEncryptor.decrypt(environment);
|
||||
}
|
||||
}
|
||||
if (!this.overrides.isEmpty()) {
|
||||
environment.addFirst(new PropertySource("overrides", getOverridesMap(includeOrigin)));
|
||||
}
|
||||
return environment;
|
||||
}
|
||||
|
||||
private Map<?, ?> getOverridesMap(boolean includeOrigin) {
|
||||
if (!includeOrigin) {
|
||||
return this.overrides;
|
||||
}
|
||||
Map<Object, Object> map = new LinkedHashMap<>();
|
||||
for (Map.Entry entry : this.overrides.entrySet()) {
|
||||
map.put(entry.getKey(), new PropertyValueDescriptor(entry.getValue(), "Config server overrides"));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param overrides the overrides to set
|
||||
*/
|
||||
public void setOverrides(Map<String, String> overrides) {
|
||||
this.overrides = new HashMap<>(overrides);
|
||||
for (String key : overrides.keySet()) {
|
||||
if (overrides.get(key).contains("\\{")) {
|
||||
this.overrides.put(key, overrides.get(key).replace("\\{", "{"));
|
||||
}
|
||||
if (overrides.get(key).contains("\\${")) {
|
||||
this.overrides.put(key, overrides.get(key).replace("\\${", "${"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015-2019 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;
|
||||
|
||||
/**
|
||||
* @author Nicolas Homble
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class EnvironmentException extends RuntimeException {
|
||||
|
||||
public EnvironmentException(String string) {
|
||||
super(string);
|
||||
}
|
||||
|
||||
public EnvironmentException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2019 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.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
/**
|
||||
* @author Chidambaram Sundaram
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
@ResponseStatus(code = HttpStatus.NOT_FOUND, reason = "Application Not Found")
|
||||
public class EnvironmentNotFoundException extends RuntimeException {
|
||||
|
||||
public EnvironmentNotFoundException(String string) {
|
||||
super(string);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2019 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.cloud.config.environment.Environment;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
* @author Roy Clarkson
|
||||
*/
|
||||
public interface EnvironmentRepository {
|
||||
|
||||
Environment findOne(String application, String profile, String label);
|
||||
|
||||
default Environment findOne(String application, String profile, String label, boolean includeOrigin) {
|
||||
return findOne(application, profile, label);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2019 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.cloud.config.server.support.EnvironmentRepositoryProperties;
|
||||
|
||||
/**
|
||||
* @param <T> {@link EnvironmentRepository} type
|
||||
* @param <P> {@link EnvironmentRepositoryProperties} type
|
||||
* @author Dylan Roberts
|
||||
*/
|
||||
public interface EnvironmentRepositoryFactory<T extends EnvironmentRepository, P extends EnvironmentRepositoryProperties> {
|
||||
|
||||
T build(P environmentProperties) throws Exception;
|
||||
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2019 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.Map;
|
||||
|
||||
import org.springframework.cloud.bootstrap.config.PropertySourceLocator;
|
||||
import org.springframework.cloud.config.environment.PropertySource;
|
||||
import org.springframework.core.env.CompositePropertySource;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
|
||||
/**
|
||||
* A PropertySourceLocator that reads from an EnvironmentRepository.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class EnvironmentRepositoryPropertySourceLocator implements PropertySourceLocator {
|
||||
|
||||
private EnvironmentRepository repository;
|
||||
|
||||
private String name;
|
||||
|
||||
private String profiles;
|
||||
|
||||
private String label;
|
||||
|
||||
public EnvironmentRepositoryPropertySourceLocator(EnvironmentRepository repository, String name, String profiles,
|
||||
String label) {
|
||||
this.repository = repository;
|
||||
this.name = name;
|
||||
this.profiles = profiles;
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.springframework.core.env.PropertySource<?> locate(Environment environment) {
|
||||
CompositePropertySource composite = new CompositePropertySource("configService");
|
||||
for (PropertySource source : this.repository.findOne(this.name, this.profiles, this.label, false)
|
||||
.getPropertySources()) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> map = (Map<String, Object>) source.getSource();
|
||||
composite.addPropertySource(new MapPropertySource(source.getName(), map));
|
||||
}
|
||||
return composite;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2019 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;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public interface EnvironmentWatch {
|
||||
|
||||
String watch(String state);
|
||||
|
||||
/**
|
||||
* Default implementation of the {@link EnvironmentWatch}.
|
||||
*/
|
||||
class Default implements EnvironmentWatch {
|
||||
|
||||
@Override
|
||||
public String watch(String state) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015-2019 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;
|
||||
|
||||
/**
|
||||
* @author Nicolas Homble
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class FailedToConstructEnvironmentException extends EnvironmentException {
|
||||
|
||||
public FailedToConstructEnvironmentException(String string) {
|
||||
super(string);
|
||||
}
|
||||
|
||||
public FailedToConstructEnvironmentException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2020 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;
|
||||
import org.springframework.core.Ordered;
|
||||
|
||||
/**
|
||||
* @author Jose Maria Alvarez
|
||||
*/
|
||||
@ConfigurationProperties("spring.cloud.config.server.gcp-secret-manager")
|
||||
public class GoogleSecretManagerEnvironmentProperties implements EnvironmentRepositoryProperties {
|
||||
|
||||
private int order = Ordered.LOWEST_PRECEDENCE;
|
||||
|
||||
private String applicationLabel = "application";
|
||||
|
||||
private String profileLabel = "profile";
|
||||
|
||||
private String serviceAccount = null;
|
||||
|
||||
private boolean tokenMandatory = true;
|
||||
|
||||
private Integer version = 1;
|
||||
|
||||
/**
|
||||
* The metadata URL to get the project ID from.
|
||||
*/
|
||||
public static final String GOOGLE_METADATA_PROJECT_URL = "http://metadata.google.internal/computeMetadata/v1/project/project-id";
|
||||
|
||||
@Override
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
public Boolean getTokenMandatory() {
|
||||
return tokenMandatory;
|
||||
}
|
||||
|
||||
public void setTokenMandatory(Boolean tokenMandatory) {
|
||||
this.tokenMandatory = tokenMandatory;
|
||||
}
|
||||
|
||||
public String getApplicationLabel() {
|
||||
return applicationLabel;
|
||||
}
|
||||
|
||||
public void setApplicationLabel(String applicationLabel) {
|
||||
this.applicationLabel = applicationLabel;
|
||||
}
|
||||
|
||||
public String getProfileLabel() {
|
||||
return profileLabel;
|
||||
}
|
||||
|
||||
public void setProfileLabel(String profileLabel) {
|
||||
this.profileLabel = profileLabel;
|
||||
}
|
||||
|
||||
public Integer getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(Integer version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public String getServiceAccount() {
|
||||
return serviceAccount;
|
||||
}
|
||||
|
||||
public void setServiceAccount(String serviceAccount) {
|
||||
this.serviceAccount = serviceAccount;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2020 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.secretmanager.v1.Secret;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.cloud.config.environment.Environment;
|
||||
import org.springframework.cloud.config.environment.PropertySource;
|
||||
import org.springframework.cloud.config.server.environment.secretmanager.GoogleConfigProvider;
|
||||
import org.springframework.cloud.config.server.environment.secretmanager.GoogleSecretComparatorByVersion;
|
||||
import org.springframework.cloud.config.server.environment.secretmanager.GoogleSecretManagerAccessStrategy;
|
||||
import org.springframework.cloud.config.server.environment.secretmanager.GoogleSecretManagerAccessStrategyFactory;
|
||||
import org.springframework.cloud.config.server.environment.secretmanager.HttpHeaderGoogleConfigProvider;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* @author Jose Maria Alvarez
|
||||
* @author KNV Srinivas
|
||||
*/
|
||||
public class GoogleSecretManagerEnvironmentRepository implements EnvironmentRepository, Ordered {
|
||||
|
||||
private String applicationLabel;
|
||||
|
||||
private String profileLabel;
|
||||
|
||||
private GoogleSecretManagerAccessStrategy accessStrategy;
|
||||
|
||||
private boolean tokenMandatory;
|
||||
|
||||
private GoogleConfigProvider configProvider;
|
||||
|
||||
private final int order;
|
||||
|
||||
public GoogleSecretManagerEnvironmentRepository(ObjectProvider<HttpServletRequest> request, RestTemplate rest,
|
||||
GoogleSecretManagerEnvironmentProperties properties) {
|
||||
this.applicationLabel = properties.getApplicationLabel();
|
||||
this.profileLabel = properties.getProfileLabel();
|
||||
this.configProvider = new HttpHeaderGoogleConfigProvider(request);
|
||||
this.accessStrategy = GoogleSecretManagerAccessStrategyFactory.forVersion(rest, configProvider, properties);
|
||||
this.tokenMandatory = properties.getTokenMandatory();
|
||||
this.order = properties.getOrder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Environment findOne(String application, String profile, String label) {
|
||||
if (StringUtils.isEmpty(label)) {
|
||||
label = "master";
|
||||
}
|
||||
if (StringUtils.isEmpty(profile)) {
|
||||
profile = "default";
|
||||
}
|
||||
if (!profile.startsWith("default")) {
|
||||
profile = "default," + profile;
|
||||
}
|
||||
String[] profiles = org.springframework.util.StringUtils
|
||||
.trimArrayElements(org.springframework.util.StringUtils.commaDelimitedListToStringArray(profile));
|
||||
Environment result = new Environment(application, profile, label, null, null);
|
||||
if (tokenMandatory) {
|
||||
if (accessStrategy.checkRemotePermissions()) {
|
||||
addPropertySource(application, profiles, result);
|
||||
}
|
||||
}
|
||||
else {
|
||||
addPropertySource(application, profiles, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void addPropertySource(String application, String[] profiles, Environment result) {
|
||||
for (String profileUnit : profiles) {
|
||||
Map<?, ?> secrets = getSecrets(application, profileUnit);
|
||||
if (!secrets.isEmpty()) {
|
||||
result.add(new PropertySource("gsm:" + application + "-" + profileUnit, secrets));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param application the application name
|
||||
* @param profile the profile name
|
||||
* @return the properties to add into the environment
|
||||
*/
|
||||
private Map<?, ?> getSecrets(String application, String profile) {
|
||||
Map<String, String> result = new HashMap<>();
|
||||
String prefix = configProvider.getValue(HttpHeaderGoogleConfigProvider.PREFIX_HEADER, false);
|
||||
for (Secret secret : accessStrategy.getSecrets()) {
|
||||
if (secret.getLabelsOrDefault(applicationLabel, "application").equalsIgnoreCase(application)
|
||||
&& secret.getLabelsOrDefault(profileLabel, "profile").equalsIgnoreCase(profile)) {
|
||||
result.put(accessStrategy.getSecretName(secret),
|
||||
accessStrategy.getSecretValue(secret, new GoogleSecretComparatorByVersion()));
|
||||
}
|
||||
else if (StringUtils.isNotBlank(prefix) && accessStrategy.getSecretName(secret).startsWith(prefix)) {
|
||||
result.put(StringUtils.removeStart(accessStrategy.getSecretName(secret), prefix),
|
||||
accessStrategy.getSecretValue(secret, new GoogleSecretComparatorByVersion()));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2020 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 jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* @author Jose Maria Alvarez
|
||||
*/
|
||||
public class GoogleSecretManagerEnvironmentRepositoryFactory implements
|
||||
EnvironmentRepositoryFactory<GoogleSecretManagerEnvironmentRepository, GoogleSecretManagerEnvironmentProperties> {
|
||||
|
||||
private final ObjectProvider<HttpServletRequest> request;
|
||||
|
||||
public GoogleSecretManagerEnvironmentRepositoryFactory(ObjectProvider<HttpServletRequest> request) {
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GoogleSecretManagerEnvironmentRepository build(
|
||||
GoogleSecretManagerEnvironmentProperties environmentProperties) throws Exception {
|
||||
return new GoogleSecretManagerEnvironmentRepository(request, new RestTemplate(), environmentProperties);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,201 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2019 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.io.IOException;
|
||||
import java.net.Proxy;
|
||||
import java.net.URL;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.eclipse.jgit.transport.http.HttpConnection;
|
||||
import org.eclipse.jgit.transport.http.apache.HttpClientConnection;
|
||||
|
||||
import org.springframework.cloud.config.server.support.HttpClient4Support;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import static java.util.stream.Collectors.toMap;
|
||||
|
||||
/**
|
||||
* @author Dylan Roberts
|
||||
*/
|
||||
public class HttpClientConfigurableHttpConnectionFactory implements ConfigurableHttpConnectionFactory {
|
||||
|
||||
private static final String PLACEHOLDER_PATTERN_STRING = "\\{(\\w+)}";
|
||||
|
||||
private static final Pattern PLACEHOLDER_PATTERN = Pattern.compile(PLACEHOLDER_PATTERN_STRING);
|
||||
|
||||
Log log = LogFactory.getLog(getClass());
|
||||
|
||||
Map<String, HttpClientBuilder> httpClientBuildersByUri = new LinkedHashMap<>();
|
||||
|
||||
@Override
|
||||
public void addConfiguration(MultipleJGitEnvironmentProperties environmentProperties)
|
||||
throws GeneralSecurityException {
|
||||
addHttpClient(environmentProperties);
|
||||
for (JGitEnvironmentProperties repo : environmentProperties.getRepos().values()) {
|
||||
addHttpClient(repo);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpConnection create(URL url) throws IOException {
|
||||
return create(url, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpConnection create(URL url, Proxy proxy) throws IOException {
|
||||
HttpClientBuilder builder = lookupHttpClientBuilder(url);
|
||||
if (builder != null) {
|
||||
return new HttpClientConnection(url.toString(), null, builder.build());
|
||||
}
|
||||
else {
|
||||
/*
|
||||
* No matching builder found: let jGit handle the creation of the HttpClient
|
||||
*/
|
||||
return new HttpClientConnection(url.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private void addHttpClient(JGitEnvironmentProperties properties) throws GeneralSecurityException {
|
||||
if (properties.getUri() != null && properties.getUri().startsWith("http")) {
|
||||
this.httpClientBuildersByUri.put(properties.getUri(), HttpClient4Support.builder(properties));
|
||||
}
|
||||
}
|
||||
|
||||
private HttpClientBuilder lookupHttpClientBuilder(final URL url) {
|
||||
Map<String, HttpClientBuilder> builderMap = this.httpClientBuildersByUri.entrySet().stream().filter(entry -> {
|
||||
String key = entry.getKey();
|
||||
String spec = getUrlWithPlaceholders(url, key);
|
||||
if (spec.equals(key)) {
|
||||
return true;
|
||||
}
|
||||
int index = spec.lastIndexOf("/");
|
||||
while (index != -1) {
|
||||
spec = spec.substring(0, index);
|
||||
if (spec.equals(key)) {
|
||||
return true;
|
||||
}
|
||||
index = spec.lastIndexOf("/");
|
||||
}
|
||||
return false;
|
||||
}).collect(toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
|
||||
if (builderMap.isEmpty()) {
|
||||
this.log.warn(String.format("No custom http config found for URL: %s", url));
|
||||
return null;
|
||||
}
|
||||
if (builderMap.size() > 1) {
|
||||
/*
|
||||
* Try to determine if there is an exact match URL or not. So if there is a
|
||||
* placeholder in the URL, filter it out. We should be left with only URLs
|
||||
* which have no placeholders. That is the one we want to use in the case
|
||||
* there are multiple matches.
|
||||
*/
|
||||
List<String> keys = builderMap.keySet().stream().filter(key -> !PLACEHOLDER_PATTERN.matcher(key).find())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (keys.size() == 1) {
|
||||
return builderMap.get(keys.get(0));
|
||||
}
|
||||
this.log.error(String.format(
|
||||
"More than one git repo URL template matched URL:"
|
||||
+ " %s, proxy and skipSslValidation config won't be applied. Matched templates: %s",
|
||||
url, builderMap.keySet().stream().collect(Collectors.joining(", "))));
|
||||
return null;
|
||||
}
|
||||
return new ArrayList<>(builderMap.values()).get(0);
|
||||
}
|
||||
|
||||
private String getUrlWithPlaceholders(URL url, String key) {
|
||||
String spec = url.toString();
|
||||
String[] tokens = key.split(PLACEHOLDER_PATTERN_STRING);
|
||||
// if token[0] equals url then there was no placeholder in the the url, so
|
||||
// matching needed
|
||||
if (tokens.length >= 1 && !tokens[0].equals(url.toString())) {
|
||||
List<Placeholder> placeholders = getPlaceholders(key);
|
||||
List<String> values = getValues(spec, tokens);
|
||||
if (placeholders.size() == values.size()) {
|
||||
for (int i = 0; i < values.size(); i++) {
|
||||
// if the key does not start with first part of the spec before the
|
||||
// place holder then its
|
||||
// not a match
|
||||
String specBeforePlaceholder = spec.substring(0, placeholders.get(i).start);
|
||||
if (key.startsWith(specBeforePlaceholder)) {
|
||||
spec = specBeforePlaceholder + "{" + placeholders.get(i).group + "}"
|
||||
+ spec.substring(placeholders.get(i).start + values.get(i).length());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return spec;
|
||||
}
|
||||
|
||||
private List<String> getValues(String spec, String[] tokens) {
|
||||
List<String> values = new LinkedList<>();
|
||||
for (String token : tokens) {
|
||||
String[] valueTokens = spec.split(token);
|
||||
if (!ObjectUtils.isEmpty(valueTokens[0])) {
|
||||
values.add(valueTokens[0]);
|
||||
}
|
||||
if (valueTokens.length > 1) {
|
||||
spec = valueTokens[1];
|
||||
}
|
||||
}
|
||||
if (tokens.length == 1 && !ObjectUtils.isEmpty(spec)) {
|
||||
values.add(spec);
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
private List<Placeholder> getPlaceholders(String key) {
|
||||
Pattern pattern = Pattern.compile(PLACEHOLDER_PATTERN_STRING);
|
||||
Matcher matcher = pattern.matcher(key);
|
||||
List<Placeholder> placeholders = new LinkedList<>();
|
||||
while (matcher.find()) {
|
||||
placeholders.add(new Placeholder(matcher.group(1), matcher.start(), matcher.end()));
|
||||
}
|
||||
return placeholders;
|
||||
}
|
||||
|
||||
private static class Placeholder {
|
||||
|
||||
String group;
|
||||
|
||||
int start;
|
||||
|
||||
int end;
|
||||
|
||||
Placeholder(String group, int start, int end) {
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
this.group = group;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2019 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.security.GeneralSecurityException;
|
||||
|
||||
import org.apache.hc.client5.http.classic.HttpClient;
|
||||
|
||||
import org.springframework.cloud.config.server.support.HttpClientSupport;
|
||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* @author Dylan Roberts
|
||||
*/
|
||||
public class HttpClientVaultRestTemplateFactory implements VaultEnvironmentRepositoryFactory.VaultRestTemplateFactory {
|
||||
|
||||
@Override
|
||||
public RestTemplate build(VaultEnvironmentProperties environmentProperties) throws GeneralSecurityException {
|
||||
HttpClient httpClient = HttpClientSupport.builder(environmentProperties).build();
|
||||
return new RestTemplate(new HttpComponentsClientHttpRequestFactory(httpClient));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2019 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 jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.cloud.config.client.ConfigClientProperties;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* @author Scott Frederick
|
||||
*/
|
||||
public class HttpRequestConfigTokenProvider implements ConfigTokenProvider {
|
||||
|
||||
private ObjectProvider<HttpServletRequest> httpRequest;
|
||||
|
||||
public HttpRequestConfigTokenProvider(ObjectProvider<HttpServletRequest> httpRequest) {
|
||||
this.httpRequest = httpRequest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getToken() {
|
||||
HttpServletRequest request = httpRequest.getIfAvailable();
|
||||
if (request == null) {
|
||||
throw new IllegalStateException("No HttpServletRequest available");
|
||||
}
|
||||
|
||||
String token = request.getHeader(ConfigClientProperties.TOKEN_HEADER);
|
||||
if (!StringUtils.hasLength(token)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Missing required header in HttpServletRequest: " + ConfigClientProperties.TOKEN_HEADER);
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2019 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.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
/**
|
||||
* @author Chidambaram Sundaram
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
@ResponseStatus(code = HttpStatus.BAD_REQUEST, reason = "Invalid Request")
|
||||
public class InvalidEnvironmentRequestException extends RuntimeException {
|
||||
|
||||
public InvalidEnvironmentRequestException(String string) {
|
||||
super(string);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,276 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2019 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 jakarta.validation.constraints.Pattern;
|
||||
|
||||
import org.springframework.cloud.config.server.proxy.ProxyHostProperties;
|
||||
import org.springframework.cloud.config.server.support.AbstractScmAccessorProperties;
|
||||
import org.springframework.cloud.config.server.support.HttpEnvironmentRepositoryProperties;
|
||||
|
||||
/**
|
||||
* @author Dylan Roberts
|
||||
* @author Gareth Clay
|
||||
*/
|
||||
public class JGitEnvironmentProperties extends AbstractScmAccessorProperties
|
||||
implements HttpEnvironmentRepositoryProperties {
|
||||
|
||||
/**
|
||||
* Master label.
|
||||
*/
|
||||
public static final String MASTER_LABEL = "master";
|
||||
|
||||
/**
|
||||
* Main label.
|
||||
*/
|
||||
public static final String MAIN_LABEL = "main";
|
||||
|
||||
private static final String DEFAULT_LABEL = MAIN_LABEL;
|
||||
|
||||
/**
|
||||
* To maintain compatibility we will try the master branch in addition to main when we
|
||||
* try to fetch the default branch.
|
||||
*/
|
||||
private boolean tryMasterBranch = true;
|
||||
|
||||
/**
|
||||
* Flag to indicate that the repository should be cloned on startup (not on demand).
|
||||
* Generally leads to slower startup but faster first query.
|
||||
*/
|
||||
private boolean cloneOnStart = false;
|
||||
|
||||
/**
|
||||
* Flag to indicate that the submodules in the repository should be cloned.
|
||||
*/
|
||||
private boolean cloneSubmodules = false;
|
||||
|
||||
/**
|
||||
* Flag to indicate that the repository should force pull. If true discard any local
|
||||
* changes and take from remote repository.
|
||||
*/
|
||||
private boolean forcePull;
|
||||
|
||||
/**
|
||||
* Timeout (in seconds) for obtaining HTTP or SSH connection (if applicable), defaults
|
||||
* to 5 seconds.
|
||||
*/
|
||||
private int timeout = 5;
|
||||
|
||||
/**
|
||||
* Flag to indicate that the branch should be deleted locally if it's origin tracked
|
||||
* branch was removed.
|
||||
*/
|
||||
private boolean deleteUntrackedBranches = false;
|
||||
|
||||
/**
|
||||
* Flag to indicate that SSL certificate validation should be bypassed when
|
||||
* communicating with a repository served over an HTTPS connection.
|
||||
*/
|
||||
private boolean skipSslValidation = false;
|
||||
|
||||
/**
|
||||
* Time (in seconds) between refresh of the git repository.
|
||||
*/
|
||||
private int refreshRate = 0;
|
||||
|
||||
/**
|
||||
* Valid SSH private key. Must be set if ignoreLocalSshSettings is true and Git URI is
|
||||
* SSH format.
|
||||
*/
|
||||
private String privateKey;
|
||||
|
||||
/**
|
||||
* One of ssh-dss, ssh-rsa, ssh-ed25519, ecdsa-sha2-nistp256, ecdsa-sha2-nistp384, or
|
||||
* ecdsa-sha2-nistp521. Must be set if hostKey is also set.
|
||||
*/
|
||||
private String hostKeyAlgorithm;
|
||||
|
||||
/**
|
||||
* Valid SSH host key. Must be set if hostKeyAlgorithm is also set.
|
||||
*/
|
||||
private String hostKey;
|
||||
|
||||
/**
|
||||
* Location of custom .known_hosts file.
|
||||
*/
|
||||
private String knownHostsFile;
|
||||
|
||||
/**
|
||||
* Override server authentication method order. This should allow for evading login
|
||||
* prompts if server has keyboard-interactive authentication before the publickey
|
||||
* method.
|
||||
*/
|
||||
@Pattern(regexp = "([\\w -]+,)*([\\w -]+)")
|
||||
private String preferredAuthentications;
|
||||
|
||||
/**
|
||||
* If true, use property-based instead of file-based SSH config.
|
||||
*/
|
||||
private boolean ignoreLocalSshSettings;
|
||||
|
||||
/**
|
||||
* If false, ignore errors with host key.
|
||||
*/
|
||||
private boolean strictHostKeyChecking = true;
|
||||
|
||||
/**
|
||||
* HTTP proxy configuration.
|
||||
*/
|
||||
private Map<ProxyHostProperties.ProxyForScheme, ProxyHostProperties> proxy = new HashMap<>();
|
||||
|
||||
public JGitEnvironmentProperties() {
|
||||
super();
|
||||
setDefaultLabel(DEFAULT_LABEL);
|
||||
}
|
||||
|
||||
public boolean isCloneOnStart() {
|
||||
return this.cloneOnStart;
|
||||
}
|
||||
|
||||
public void setCloneOnStart(boolean cloneOnStart) {
|
||||
this.cloneOnStart = cloneOnStart;
|
||||
}
|
||||
|
||||
public boolean isCloneSubmodules() {
|
||||
return this.cloneSubmodules;
|
||||
}
|
||||
|
||||
public void setCloneSubmodules(boolean cloneSubmodules) {
|
||||
this.cloneSubmodules = cloneSubmodules;
|
||||
}
|
||||
|
||||
public boolean isForcePull() {
|
||||
return this.forcePull;
|
||||
}
|
||||
|
||||
public void setForcePull(boolean forcePull) {
|
||||
this.forcePull = forcePull;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTimeout() {
|
||||
return this.timeout;
|
||||
}
|
||||
|
||||
public void setTimeout(int timeout) {
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
public boolean isDeleteUntrackedBranches() {
|
||||
return this.deleteUntrackedBranches;
|
||||
}
|
||||
|
||||
public void setDeleteUntrackedBranches(boolean deleteUntrackedBranches) {
|
||||
this.deleteUntrackedBranches = deleteUntrackedBranches;
|
||||
}
|
||||
|
||||
public int getRefreshRate() {
|
||||
return this.refreshRate;
|
||||
}
|
||||
|
||||
public void setRefreshRate(int refreshRate) {
|
||||
this.refreshRate = refreshRate;
|
||||
}
|
||||
|
||||
public String getPrivateKey() {
|
||||
return this.privateKey;
|
||||
}
|
||||
|
||||
public void setPrivateKey(String privateKey) {
|
||||
this.privateKey = privateKey;
|
||||
}
|
||||
|
||||
public String getHostKeyAlgorithm() {
|
||||
return this.hostKeyAlgorithm;
|
||||
}
|
||||
|
||||
public void setHostKeyAlgorithm(String hostKeyAlgorithm) {
|
||||
this.hostKeyAlgorithm = hostKeyAlgorithm;
|
||||
}
|
||||
|
||||
public String getHostKey() {
|
||||
return this.hostKey;
|
||||
}
|
||||
|
||||
public void setHostKey(String hostKey) {
|
||||
this.hostKey = hostKey;
|
||||
}
|
||||
|
||||
public String getKnownHostsFile() {
|
||||
return this.knownHostsFile;
|
||||
}
|
||||
|
||||
public void setKnownHostsFile(String knownHostsFile) {
|
||||
this.knownHostsFile = knownHostsFile;
|
||||
}
|
||||
|
||||
public String getPreferredAuthentications() {
|
||||
return this.preferredAuthentications;
|
||||
}
|
||||
|
||||
public void setPreferredAuthentications(String preferredAuthentications) {
|
||||
this.preferredAuthentications = preferredAuthentications;
|
||||
}
|
||||
|
||||
public boolean isIgnoreLocalSshSettings() {
|
||||
return this.ignoreLocalSshSettings;
|
||||
}
|
||||
|
||||
public void setIgnoreLocalSshSettings(boolean ignoreLocalSshSettings) {
|
||||
this.ignoreLocalSshSettings = ignoreLocalSshSettings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStrictHostKeyChecking() {
|
||||
return this.strictHostKeyChecking;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStrictHostKeyChecking(boolean strictHostKeyChecking) {
|
||||
this.strictHostKeyChecking = strictHostKeyChecking;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSkipSslValidation() {
|
||||
return this.skipSslValidation;
|
||||
}
|
||||
|
||||
public void setSkipSslValidation(boolean skipSslValidation) {
|
||||
this.skipSslValidation = skipSslValidation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<ProxyHostProperties.ProxyForScheme, ProxyHostProperties> getProxy() {
|
||||
return this.proxy;
|
||||
}
|
||||
|
||||
public void setProxy(Map<ProxyHostProperties.ProxyForScheme, ProxyHostProperties> proxy) {
|
||||
this.proxy = proxy;
|
||||
}
|
||||
|
||||
public boolean isTryMasterBranch() {
|
||||
return tryMasterBranch;
|
||||
}
|
||||
|
||||
public void setTryMasterBranch(boolean tryMasterBranch) {
|
||||
this.tryMasterBranch = tryMasterBranch;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,780 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2019 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.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import org.eclipse.jgit.api.CheckoutCommand;
|
||||
import org.eclipse.jgit.api.CloneCommand;
|
||||
import org.eclipse.jgit.api.CreateBranchCommand.SetupUpstreamMode;
|
||||
import org.eclipse.jgit.api.DeleteBranchCommand;
|
||||
import org.eclipse.jgit.api.FetchCommand;
|
||||
import org.eclipse.jgit.api.Git;
|
||||
import org.eclipse.jgit.api.ListBranchCommand;
|
||||
import org.eclipse.jgit.api.ListBranchCommand.ListMode;
|
||||
import org.eclipse.jgit.api.MergeCommand;
|
||||
import org.eclipse.jgit.api.MergeResult;
|
||||
import org.eclipse.jgit.api.ResetCommand;
|
||||
import org.eclipse.jgit.api.ResetCommand.ResetType;
|
||||
import org.eclipse.jgit.api.Status;
|
||||
import org.eclipse.jgit.api.StatusCommand;
|
||||
import org.eclipse.jgit.api.TransportCommand;
|
||||
import org.eclipse.jgit.api.TransportConfigCallback;
|
||||
import org.eclipse.jgit.api.errors.GitAPIException;
|
||||
import org.eclipse.jgit.api.errors.JGitInternalException;
|
||||
import org.eclipse.jgit.api.errors.RefNotFoundException;
|
||||
import org.eclipse.jgit.errors.NoRemoteRepositoryException;
|
||||
import org.eclipse.jgit.lib.BranchTrackingStatus;
|
||||
import org.eclipse.jgit.lib.Ref;
|
||||
import org.eclipse.jgit.transport.CredentialsProvider;
|
||||
import org.eclipse.jgit.transport.FetchResult;
|
||||
import org.eclipse.jgit.transport.ReceiveCommand;
|
||||
import org.eclipse.jgit.transport.TagOpt;
|
||||
import org.eclipse.jgit.transport.TrackingRefUpdate;
|
||||
import org.eclipse.jgit.util.FileUtils;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.cloud.config.server.support.GitCredentialsProviderFactory;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.io.UrlResource;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static org.eclipse.jgit.transport.ReceiveCommand.Type.DELETE;
|
||||
|
||||
/**
|
||||
* An {@link EnvironmentRepository} backed by a single git repository.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Roy Clarkson
|
||||
* @author Marcos Barbero
|
||||
* @author Daniel Lavoie
|
||||
* @author Ryan Lynch
|
||||
* @author Gareth Clay
|
||||
* @author ChaoDong Xi
|
||||
*/
|
||||
public class JGitEnvironmentRepository extends AbstractScmEnvironmentRepository
|
||||
implements EnvironmentRepository, SearchPathLocator, InitializingBean {
|
||||
|
||||
/**
|
||||
* Error message for URI for git repo.
|
||||
*/
|
||||
public static final String MESSAGE = "You need to configure a uri for the git repository.";
|
||||
|
||||
private static final String FILE_URI_PREFIX = "file:";
|
||||
|
||||
private static final String LOCAL_BRANCH_REF_PREFIX = "refs/remotes/origin/";
|
||||
|
||||
/**
|
||||
* Timeout (in seconds) for obtaining HTTP or SSH connection (if applicable). Default
|
||||
* 5 seconds.
|
||||
*/
|
||||
private int timeout;
|
||||
|
||||
/**
|
||||
* Time (in seconds) between refresh of the git repository.
|
||||
*/
|
||||
private int refreshRate = 0;
|
||||
|
||||
/**
|
||||
* Time of the last refresh of the git repository.
|
||||
*/
|
||||
private long lastRefresh;
|
||||
|
||||
/**
|
||||
* Flag to indicate that the repository should be cloned on startup (not on demand).
|
||||
* Generally leads to slower startup but faster first query.
|
||||
*/
|
||||
private boolean cloneOnStart;
|
||||
|
||||
private JGitEnvironmentRepository.JGitFactory gitFactory;
|
||||
|
||||
private String defaultLabel;
|
||||
|
||||
/**
|
||||
* Factory used to create the credentials provider to use to connect to the Git
|
||||
* repository.
|
||||
*/
|
||||
private GitCredentialsProviderFactory gitCredentialsProviderFactory = new GitCredentialsProviderFactory();
|
||||
|
||||
/**
|
||||
* Transport configuration callback for JGit commands.
|
||||
*/
|
||||
private TransportConfigCallback transportConfigCallback;
|
||||
|
||||
/**
|
||||
* Flag to indicate that the repository should force pull. If true discard any local
|
||||
* changes and take from remote repository.
|
||||
*/
|
||||
private boolean forcePull;
|
||||
|
||||
/**
|
||||
* Flag to indicate that the branch should be deleted locally if it's origin tracked
|
||||
* branch was removed.
|
||||
*/
|
||||
private boolean deleteUntrackedBranches;
|
||||
|
||||
/**
|
||||
* Flag to indicate that SSL certificate validation should be bypassed when
|
||||
* communicating with a repository served over an HTTPS connection.
|
||||
*/
|
||||
private boolean skipSslValidation;
|
||||
|
||||
private boolean tryMasterBranch;
|
||||
|
||||
private final ObservationRegistry observationRegistry;
|
||||
|
||||
public JGitEnvironmentRepository(ConfigurableEnvironment environment, JGitEnvironmentProperties properties,
|
||||
ObservationRegistry observationRegistry) {
|
||||
super(environment, properties, observationRegistry);
|
||||
this.cloneOnStart = properties.isCloneOnStart();
|
||||
this.defaultLabel = properties.getDefaultLabel();
|
||||
this.forcePull = properties.isForcePull();
|
||||
this.timeout = properties.getTimeout();
|
||||
this.deleteUntrackedBranches = properties.isDeleteUntrackedBranches();
|
||||
this.refreshRate = properties.getRefreshRate();
|
||||
this.skipSslValidation = properties.isSkipSslValidation();
|
||||
this.gitFactory = new JGitFactory(properties.isCloneSubmodules());
|
||||
this.tryMasterBranch = properties.isTryMasterBranch();
|
||||
this.observationRegistry = observationRegistry;
|
||||
}
|
||||
|
||||
public boolean isTryMasterBranch() {
|
||||
return tryMasterBranch;
|
||||
}
|
||||
|
||||
public void setTryMasterBranch(boolean tryMasterBranch) {
|
||||
this.tryMasterBranch = tryMasterBranch;
|
||||
}
|
||||
|
||||
public boolean isCloneOnStart() {
|
||||
return this.cloneOnStart;
|
||||
}
|
||||
|
||||
public void setCloneOnStart(boolean cloneOnStart) {
|
||||
this.cloneOnStart = cloneOnStart;
|
||||
}
|
||||
|
||||
public int getTimeout() {
|
||||
return this.timeout;
|
||||
}
|
||||
|
||||
public void setTimeout(int timeout) {
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
public int getRefreshRate() {
|
||||
return this.refreshRate;
|
||||
}
|
||||
|
||||
public void setRefreshRate(int refreshRate) {
|
||||
this.refreshRate = refreshRate;
|
||||
}
|
||||
|
||||
public TransportConfigCallback getTransportConfigCallback() {
|
||||
return this.transportConfigCallback;
|
||||
}
|
||||
|
||||
public void setTransportConfigCallback(TransportConfigCallback transportConfigCallback) {
|
||||
this.transportConfigCallback = transportConfigCallback;
|
||||
}
|
||||
|
||||
public JGitFactory getGitFactory() {
|
||||
return this.gitFactory;
|
||||
}
|
||||
|
||||
public void setGitFactory(JGitFactory gitFactory) {
|
||||
this.gitFactory = gitFactory;
|
||||
}
|
||||
|
||||
public void setGitCredentialsProviderFactory(GitCredentialsProviderFactory gitCredentialsProviderFactory) {
|
||||
this.gitCredentialsProviderFactory = gitCredentialsProviderFactory;
|
||||
}
|
||||
|
||||
GitCredentialsProviderFactory getGitCredentialsProviderFactory() {
|
||||
return gitCredentialsProviderFactory;
|
||||
}
|
||||
|
||||
public String getDefaultLabel() {
|
||||
return this.defaultLabel;
|
||||
}
|
||||
|
||||
public void setDefaultLabel(String defaultLabel) {
|
||||
this.defaultLabel = defaultLabel;
|
||||
}
|
||||
|
||||
public boolean isForcePull() {
|
||||
return this.forcePull;
|
||||
}
|
||||
|
||||
public void setForcePull(boolean forcePull) {
|
||||
this.forcePull = forcePull;
|
||||
}
|
||||
|
||||
public boolean isDeleteUntrackedBranches() {
|
||||
return this.deleteUntrackedBranches;
|
||||
}
|
||||
|
||||
public void setDeleteUntrackedBranches(boolean deleteUntrackedBranches) {
|
||||
this.deleteUntrackedBranches = deleteUntrackedBranches;
|
||||
}
|
||||
|
||||
public boolean isSkipSslValidation() {
|
||||
return this.skipSslValidation;
|
||||
}
|
||||
|
||||
public void setSkipSslValidation(boolean skipSslValidation) {
|
||||
this.skipSslValidation = skipSslValidation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Locations getLocations(String application, String profile, String label) {
|
||||
if (label == null) {
|
||||
label = this.defaultLabel;
|
||||
}
|
||||
String version;
|
||||
try {
|
||||
version = refresh(label);
|
||||
}
|
||||
catch (Exception e) {
|
||||
if (this.defaultLabel.equals(label) && JGitEnvironmentProperties.MAIN_LABEL.equals(this.defaultLabel)
|
||||
&& tryMasterBranch) {
|
||||
logger.info("Could not refresh default label " + label, e);
|
||||
logger.info("Will try to refresh master label instead.");
|
||||
version = refresh(JGitEnvironmentProperties.MASTER_LABEL);
|
||||
}
|
||||
else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
return new Locations(application, profile, label, version,
|
||||
getSearchLocations(getWorkingDirectory(), application, profile, label));
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void afterPropertiesSet() throws Exception {
|
||||
Assert.state(getUri() != null, MESSAGE);
|
||||
if (this.cloneOnStart) {
|
||||
initClonedRepository();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the working directory ready.
|
||||
* @param label label to refresh
|
||||
* @return head id
|
||||
*/
|
||||
public String refresh(String label) {
|
||||
Git git = null;
|
||||
try {
|
||||
git = createGitClient();
|
||||
if (shouldPull(git)) {
|
||||
FetchResult fetchStatus = fetch(git, label);
|
||||
if (this.deleteUntrackedBranches && fetchStatus != null) {
|
||||
deleteUntrackedLocalBranches(fetchStatus.getTrackingRefUpdates(), git);
|
||||
}
|
||||
}
|
||||
|
||||
// checkout after fetch so we can get any new branches, tags, ect.
|
||||
// if nothing to update so just checkout and merge.
|
||||
// Merge because remote branch could have been updated before
|
||||
checkout(git, label);
|
||||
tryMerge(git, label);
|
||||
|
||||
// always return what is currently HEAD as the version
|
||||
return git.getRepository().findRef("HEAD").getObjectId().getName();
|
||||
}
|
||||
catch (RefNotFoundException e) {
|
||||
throw new NoSuchLabelException("No such label: " + label, e);
|
||||
}
|
||||
catch (NoRemoteRepositoryException e) {
|
||||
throw new NoSuchRepositoryException("No such repository: " + getUri(), e);
|
||||
}
|
||||
catch (GitAPIException e) {
|
||||
throw new NoSuchRepositoryException("Cannot clone or checkout repository: " + getUri(), e);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new IllegalStateException("Cannot load environment", e);
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
if (git != null) {
|
||||
git.close();
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
this.logger.warn("Could not close git repository", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void tryMerge(Git git, String label) {
|
||||
try {
|
||||
if (isBranch(git, label)) {
|
||||
// merge results from fetch
|
||||
merge(git, label);
|
||||
if (!isClean(git, label)) {
|
||||
this.logger.warn("The local repository is dirty or ahead of origin. Resetting" + " it to origin/"
|
||||
+ label + ".");
|
||||
resetHard(git, label, LOCAL_BRANCH_REF_PREFIX + label);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (GitAPIException e) {
|
||||
throw new NoSuchRepositoryException("Cannot clone or checkout repository: " + getUri(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones the remote repository and then opens a connection to it. Checks out to the
|
||||
* defaultLabel if specified.
|
||||
* @throws GitAPIException when cloning fails
|
||||
* @throws IOException when repo opening fails
|
||||
*/
|
||||
private void initClonedRepository() throws GitAPIException, IOException {
|
||||
if (!getUri().startsWith(FILE_URI_PREFIX)) {
|
||||
deleteBaseDirIfExists();
|
||||
Git git = cloneToBasedir();
|
||||
if (git != null) {
|
||||
git.close();
|
||||
}
|
||||
git = openGitRepository();
|
||||
|
||||
// Check if git points to valid repository and default label is not empty or
|
||||
// null.
|
||||
if (null != git && git.getRepository() != null && !ObjectUtils.isEmpty(getDefaultLabel())) {
|
||||
// Checkout the default branch set for repo in git. This may not always be
|
||||
// master. It depends on the
|
||||
// admin and organization settings.
|
||||
String defaultBranchInGit = git.getRepository().getBranch();
|
||||
// If default branch is not empty and NOT equal to defaultLabel, then
|
||||
// checkout the branch/tag/commit-id.
|
||||
if (!ObjectUtils.isEmpty(defaultBranchInGit)
|
||||
&& !getDefaultLabel().equalsIgnoreCase(defaultBranchInGit)) {
|
||||
checkoutDefaultBranchWithRetry(git);
|
||||
}
|
||||
}
|
||||
|
||||
if (git != null) {
|
||||
git.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void checkoutDefaultBranchWithRetry(Git git) throws GitAPIException {
|
||||
try {
|
||||
checkout(git, getDefaultLabel());
|
||||
}
|
||||
catch (Exception e) {
|
||||
if (JGitEnvironmentProperties.MAIN_LABEL.equals(getDefaultLabel()) && tryMasterBranch) {
|
||||
logger.info("Could not checkout default label " + getDefaultLabel(), e);
|
||||
logger.info("Will try to checkout master label instead.");
|
||||
checkout(git, JGitEnvironmentProperties.MASTER_LABEL);
|
||||
}
|
||||
else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes local branches if corresponding remote branch was removed.
|
||||
* @param trackingRefUpdates list of tracking ref updates
|
||||
* @param git git instance
|
||||
* @return list of deleted branches
|
||||
*/
|
||||
private Collection<String> deleteUntrackedLocalBranches(Collection<TrackingRefUpdate> trackingRefUpdates, Git git) {
|
||||
if (CollectionUtils.isEmpty(trackingRefUpdates)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
Collection<String> branchesToDelete = new ArrayList<>();
|
||||
for (TrackingRefUpdate trackingRefUpdate : trackingRefUpdates) {
|
||||
ReceiveCommand receiveCommand = trackingRefUpdate.asReceiveCommand();
|
||||
if (receiveCommand.getType() == DELETE) {
|
||||
String localRefName = trackingRefUpdate.getLocalName();
|
||||
if (StringUtils.startsWithIgnoreCase(localRefName, LOCAL_BRANCH_REF_PREFIX)) {
|
||||
String localBranchName = localRefName.substring(LOCAL_BRANCH_REF_PREFIX.length(),
|
||||
localRefName.length());
|
||||
branchesToDelete.add(localBranchName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (CollectionUtils.isEmpty(branchesToDelete)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
try {
|
||||
// make sure that deleted branch not a current one
|
||||
checkoutDefaultBranchWithRetry(git);
|
||||
return deleteBranches(git, branchesToDelete);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
String message = format("Failed to delete %s branches.", branchesToDelete);
|
||||
warn(message, ex);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> deleteBranches(Git git, Collection<String> branchesToDelete) throws GitAPIException {
|
||||
DeleteBranchCommand deleteBranchCommand = git.branchDelete()
|
||||
.setBranchNames(branchesToDelete.toArray(new String[0]))
|
||||
// local branch can contain data which is not merged to HEAD - force
|
||||
// delete it anyway, since local copy should be R/O
|
||||
.setForce(true);
|
||||
List<String> resultList = deleteBranchCommand.call();
|
||||
this.logger.info(format("Deleted %s branches from %s branches to delete.", resultList, branchesToDelete));
|
||||
return resultList;
|
||||
}
|
||||
|
||||
private Ref checkout(Git git, String label) throws GitAPIException {
|
||||
CheckoutCommand checkout = git.checkout();
|
||||
if (shouldTrack(git, label)) {
|
||||
trackBranch(git, checkout, label);
|
||||
}
|
||||
else {
|
||||
// works for tags and local branches
|
||||
checkout.setName(label);
|
||||
}
|
||||
return checkout.call();
|
||||
}
|
||||
|
||||
protected boolean shouldPull(Git git) throws GitAPIException {
|
||||
boolean shouldPull;
|
||||
|
||||
if (this.refreshRate > 0 && System.currentTimeMillis() - this.lastRefresh < (this.refreshRate * 1000)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Status gitStatus;
|
||||
try {
|
||||
gitStatus = git.status().call();
|
||||
}
|
||||
catch (JGitInternalException e) {
|
||||
onPullInvalidIndex(git, e);
|
||||
gitStatus = git.status().call();
|
||||
}
|
||||
|
||||
boolean isWorkingTreeClean = gitStatus.isClean();
|
||||
String originUrl = git.getRepository().getConfig().getString("remote", "origin", "url");
|
||||
|
||||
if (this.forcePull && !isWorkingTreeClean) {
|
||||
shouldPull = true;
|
||||
logDirty(gitStatus);
|
||||
}
|
||||
else {
|
||||
shouldPull = isWorkingTreeClean && originUrl != null;
|
||||
}
|
||||
if (!isWorkingTreeClean && !this.forcePull) {
|
||||
this.logger.info("Cannot pull from remote " + originUrl + ", the working tree is not clean.");
|
||||
}
|
||||
return shouldPull;
|
||||
}
|
||||
|
||||
protected void onPullInvalidIndex(Git git, JGitInternalException e) {
|
||||
if (!e.getMessage().contains("Short read of block.")) {
|
||||
throw e;
|
||||
}
|
||||
if (!this.forcePull) {
|
||||
throw e;
|
||||
}
|
||||
try {
|
||||
new File(getWorkingDirectory(), ".git/index").delete();
|
||||
git.reset().setMode(ResetType.HARD).setRef("HEAD").call();
|
||||
}
|
||||
catch (GitAPIException ex) {
|
||||
e.addSuppressed(ex);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void logDirty(Status status) {
|
||||
Set<String> dirties = dirties(status.getAdded(), status.getChanged(), status.getRemoved(), status.getMissing(),
|
||||
status.getModified(), status.getConflicting(), status.getUntracked());
|
||||
this.logger.warn(format("Dirty files found: %s", dirties));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Set<String> dirties(Set<String>... changes) {
|
||||
Set<String> dirties = new HashSet<>();
|
||||
for (Set<String> files : changes) {
|
||||
dirties.addAll(files);
|
||||
}
|
||||
return dirties;
|
||||
}
|
||||
|
||||
private boolean shouldTrack(Git git, String label) throws GitAPIException {
|
||||
return isBranch(git, label) && !isLocalBranch(git, label);
|
||||
}
|
||||
|
||||
protected FetchResult fetch(Git git, String label) {
|
||||
FetchCommand fetch = git.fetch();
|
||||
fetch.setRemote("origin");
|
||||
fetch.setTagOpt(TagOpt.FETCH_TAGS);
|
||||
fetch.setRemoveDeletedRefs(this.deleteUntrackedBranches);
|
||||
if (this.refreshRate > 0) {
|
||||
this.setLastRefresh(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
configureCommand(fetch);
|
||||
try {
|
||||
FetchResult result = fetch.call();
|
||||
if (result.getTrackingRefUpdates() != null && result.getTrackingRefUpdates().size() > 0) {
|
||||
this.logger.info("Fetched for remote " + label + " and found " + result.getTrackingRefUpdates().size()
|
||||
+ " updates");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
String message = "Could not fetch remote for " + label + " remote: "
|
||||
+ git.getRepository().getConfig().getString("remote", "origin", "url");
|
||||
warn(message, ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private MergeResult merge(Git git, String label) {
|
||||
try {
|
||||
MergeCommand merge = git.merge();
|
||||
merge.include(git.getRepository().findRef("origin/" + label));
|
||||
MergeResult result = merge.call();
|
||||
if (!result.getMergeStatus().isSuccessful()) {
|
||||
this.logger.warn("Merged from remote " + label + " with result " + result.getMergeStatus());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
String message = "Could not merge remote for " + label + " remote: "
|
||||
+ git.getRepository().getConfig().getString("remote", "origin", "url");
|
||||
warn(message, ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Ref resetHard(Git git, String label, String ref) {
|
||||
ResetCommand reset = git.reset();
|
||||
reset.setRef(ref);
|
||||
reset.setMode(ResetType.HARD);
|
||||
try {
|
||||
Ref resetRef = reset.call();
|
||||
if (resetRef != null) {
|
||||
this.logger.info("Reset label " + label + " to version " + resetRef.getObjectId());
|
||||
}
|
||||
return resetRef;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
String message = "Could not reset to remote for " + label + " (current ref=" + ref + "), remote: "
|
||||
+ git.getRepository().getConfig().getString("remote", "origin", "url");
|
||||
warn(message, ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Git createGitClient() throws IOException, GitAPIException {
|
||||
File lock = new File(getWorkingDirectory(), ".git/index.lock");
|
||||
if (lock.exists()) {
|
||||
// The only way this can happen is if another JVM (e.g. one that
|
||||
// crashed earlier) created the lock. We can attempt to recover by
|
||||
// wiping the slate clean.
|
||||
this.logger.info("Deleting stale JGit lock file at " + lock);
|
||||
lock.delete();
|
||||
}
|
||||
if (new File(getWorkingDirectory(), ".git").exists()) {
|
||||
return openGitRepository();
|
||||
}
|
||||
else {
|
||||
return copyRepository();
|
||||
}
|
||||
}
|
||||
|
||||
// Synchronize here so that multiple requests don't all try and delete the
|
||||
// base dir
|
||||
// together (this is a once only operation, so it only holds things up on
|
||||
// the first
|
||||
// request).
|
||||
private synchronized Git copyRepository() throws IOException, GitAPIException {
|
||||
deleteBaseDirIfExists();
|
||||
getBasedir().mkdirs();
|
||||
Assert.state(getBasedir().exists(), "Could not create basedir: " + getBasedir());
|
||||
if (getUri().startsWith(FILE_URI_PREFIX)) {
|
||||
return copyFromLocalRepository();
|
||||
}
|
||||
else {
|
||||
return cloneToBasedir();
|
||||
}
|
||||
}
|
||||
|
||||
private Git openGitRepository() throws IOException {
|
||||
Git git = this.gitFactory.getGitByOpen(getWorkingDirectory());
|
||||
return git;
|
||||
}
|
||||
|
||||
private Git copyFromLocalRepository() throws IOException {
|
||||
Git git;
|
||||
File remote = new UrlResource(StringUtils.cleanPath(getUri())).getFile();
|
||||
Assert.state(remote.isDirectory(), "No directory at " + getUri());
|
||||
File gitDir = new File(remote, ".git");
|
||||
Assert.state(gitDir.exists(), "No .git at " + getUri());
|
||||
Assert.state(gitDir.isDirectory(), "No .git directory at " + getUri());
|
||||
git = this.gitFactory.getGitByOpen(remote);
|
||||
return git;
|
||||
}
|
||||
|
||||
private Git cloneToBasedir() throws GitAPIException {
|
||||
CloneCommand clone = this.gitFactory.getCloneCommandByCloneRepository().setURI(getUri())
|
||||
.setDirectory(getBasedir());
|
||||
configureCommand(clone);
|
||||
try {
|
||||
return clone.call();
|
||||
}
|
||||
catch (GitAPIException e) {
|
||||
this.logger.warn("Error occured cloning to base directory.", e);
|
||||
deleteBaseDirIfExists();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteBaseDirIfExists() {
|
||||
if (getBasedir().exists()) {
|
||||
for (File file : getBasedir().listFiles()) {
|
||||
try {
|
||||
FileUtils.delete(file, FileUtils.RECURSIVE);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new IllegalStateException("Failed to initialize base directory", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void configureCommand(TransportCommand<?, ?> command) {
|
||||
command.setTimeout(this.timeout);
|
||||
if (this.transportConfigCallback != null) {
|
||||
command.setTransportConfigCallback(this.transportConfigCallback);
|
||||
}
|
||||
CredentialsProvider credentialsProvider = getCredentialsProvider();
|
||||
if (credentialsProvider != null) {
|
||||
command.setCredentialsProvider(credentialsProvider);
|
||||
}
|
||||
}
|
||||
|
||||
private CredentialsProvider getCredentialsProvider() {
|
||||
return this.gitCredentialsProviderFactory.createFor(this.getUri(), getUsername(), getPassword(),
|
||||
getPassphrase(), isSkipSslValidation());
|
||||
}
|
||||
|
||||
private boolean isClean(Git git, String label) {
|
||||
StatusCommand status = git.status();
|
||||
try {
|
||||
BranchTrackingStatus trackingStatus = BranchTrackingStatus.of(git.getRepository(), label);
|
||||
boolean isBranchAhead = trackingStatus != null && trackingStatus.getAheadCount() > 0;
|
||||
return status.call().isClean() && !isBranchAhead;
|
||||
}
|
||||
catch (Exception e) {
|
||||
String message = "Could not execute status command on local repository. Cause: ("
|
||||
+ e.getClass().getSimpleName() + ") " + e.getMessage();
|
||||
warn(message, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void trackBranch(Git git, CheckoutCommand checkout, String label) {
|
||||
checkout.setCreateBranch(true).setName(label).setUpstreamMode(SetupUpstreamMode.TRACK)
|
||||
.setStartPoint("origin/" + label);
|
||||
}
|
||||
|
||||
private boolean isBranch(Git git, String label) throws GitAPIException {
|
||||
return containsBranch(git, label, ListMode.ALL);
|
||||
}
|
||||
|
||||
private boolean isLocalBranch(Git git, String label) throws GitAPIException {
|
||||
return containsBranch(git, label, null);
|
||||
}
|
||||
|
||||
private boolean containsBranch(Git git, String label, ListMode listMode) throws GitAPIException {
|
||||
ListBranchCommand command = git.branchList();
|
||||
if (listMode != null) {
|
||||
command.setListMode(listMode);
|
||||
}
|
||||
List<Ref> branches = command.call();
|
||||
for (Ref ref : branches) {
|
||||
if (ref.getName().equals("refs/heads/" + label) || ref.getName().equals("refs/remotes/origin/" + label)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void warn(String message, Exception ex) {
|
||||
this.logger.warn(message);
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
this.logger.debug("Stacktrace for: " + message, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public long getLastRefresh() {
|
||||
return this.lastRefresh;
|
||||
}
|
||||
|
||||
public void setLastRefresh(long lastRefresh) {
|
||||
this.lastRefresh = lastRefresh;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the static method calls to {@link org.eclipse.jgit.api.Git} and
|
||||
* {@link org.eclipse.jgit.api.CloneCommand} allowing for easier unit testing.
|
||||
*/
|
||||
public static class JGitFactory {
|
||||
|
||||
private final boolean cloneSubmodules;
|
||||
|
||||
public JGitFactory() {
|
||||
this(false);
|
||||
}
|
||||
|
||||
public JGitFactory(boolean cloneSubmodules) {
|
||||
this.cloneSubmodules = cloneSubmodules;
|
||||
}
|
||||
|
||||
public Git getGitByOpen(File file) throws IOException {
|
||||
Git git = Git.open(file);
|
||||
return git;
|
||||
}
|
||||
|
||||
public CloneCommand getCloneCommandByCloneRepository() {
|
||||
CloneCommand command = Git.cloneRepository().setCloneSubmodules(cloneSubmodules);
|
||||
return command;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2019 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;
|
||||
import org.springframework.core.Ordered;
|
||||
|
||||
/**
|
||||
* @author Dylan Roberts
|
||||
* @author Thomas Vitale
|
||||
*/
|
||||
@ConfigurationProperties("spring.cloud.config.server.jdbc")
|
||||
public class JdbcEnvironmentProperties implements EnvironmentRepositoryProperties {
|
||||
|
||||
private static final String DEFAULT_SQL = "SELECT \"KEY\", \"VALUE\" from PROPERTIES"
|
||||
+ " where APPLICATION=? and PROFILE=? and LABEL=?";
|
||||
|
||||
private static final String DEFAULT_SQL_WITHOUT_PROFILE = "SELECT \"KEY\", \"VALUE\" from PROPERTIES"
|
||||
+ " where APPLICATION=? and PROFILE is null and LABEL=?";
|
||||
|
||||
/**
|
||||
* Flag to indicate that JDBC environment repository configuration is enabled.
|
||||
*/
|
||||
private boolean enabled = true;
|
||||
|
||||
private int order = Ordered.LOWEST_PRECEDENCE - 10;
|
||||
|
||||
/** SQL used to query database for keys and values. */
|
||||
private String sql = DEFAULT_SQL;
|
||||
|
||||
/** SQL used to query database for keys and values when profile is null. */
|
||||
private String sqlWithoutProfile = DEFAULT_SQL_WITHOUT_PROFILE;
|
||||
|
||||
/**
|
||||
* Flag to determine how to handle query exceptions.
|
||||
*/
|
||||
private boolean failOnError = true;
|
||||
|
||||
private String defaultLabel = "master";
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return this.order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
public String getSql() {
|
||||
return this.sql;
|
||||
}
|
||||
|
||||
public void setSql(String sql) {
|
||||
this.sql = sql;
|
||||
}
|
||||
|
||||
public String getSqlWithoutProfile() {
|
||||
return this.sqlWithoutProfile;
|
||||
}
|
||||
|
||||
public void setSqlWithoutProfile(String sqlWithoutProfile) {
|
||||
this.sqlWithoutProfile = sqlWithoutProfile;
|
||||
}
|
||||
|
||||
public boolean isFailOnError() {
|
||||
return failOnError;
|
||||
}
|
||||
|
||||
public void setFailOnError(boolean failOnError) {
|
||||
this.failOnError = failOnError;
|
||||
}
|
||||
|
||||
public boolean isConfigIncomplete() {
|
||||
// sql and sqlWithoutProfile should be customized at the same time
|
||||
return !this.sql.equals(DEFAULT_SQL) && this.sqlWithoutProfile.equals(DEFAULT_SQL_WITHOUT_PROFILE);
|
||||
}
|
||||
|
||||
public String getDefaultLabel() {
|
||||
return this.defaultLabel;
|
||||
}
|
||||
|
||||
public void setDefaultLabel(String defaultLabel) {
|
||||
this.defaultLabel = defaultLabel;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,194 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016-2019 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.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.cloud.config.environment.Environment;
|
||||
import org.springframework.cloud.config.environment.PropertySource;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.ResultSetExtractor;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* An {@link EnvironmentRepository} that picks up data from a relational database. The
|
||||
* database should have a table called "PROPERTIES" with columns "APPLICATION", "PROFILE",
|
||||
* "LABEL" (with the usual {@link Environment} meaning), plus "KEY" and "VALUE" for the
|
||||
* key and value pairs in {@link Properties} style. Property values behave in the same way
|
||||
* as they would if they came from Spring Boot properties files named
|
||||
* <code>{application}-{profile}.properties</code>, including all the encryption and
|
||||
* decryption, which will be applied as post-processing steps (i.e. not in this repository
|
||||
* directly).
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class JdbcEnvironmentRepository implements EnvironmentRepository, Ordered {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(JdbcEnvironmentRepository.class);
|
||||
|
||||
private final JdbcTemplate jdbc;
|
||||
|
||||
private final PropertiesResultSetExtractor extractor;
|
||||
|
||||
private int order;
|
||||
|
||||
private String sql;
|
||||
|
||||
private String sqlWithoutProfile;
|
||||
|
||||
private boolean failOnError;
|
||||
|
||||
private boolean configIncomplete;
|
||||
|
||||
private String defaultLabel;
|
||||
|
||||
@Deprecated
|
||||
public JdbcEnvironmentRepository(JdbcTemplate jdbc, JdbcEnvironmentProperties properties) {
|
||||
this(jdbc, properties, new PropertiesResultSetExtractor());
|
||||
}
|
||||
|
||||
public JdbcEnvironmentRepository(JdbcTemplate jdbc, JdbcEnvironmentProperties properties,
|
||||
PropertiesResultSetExtractor extractor) {
|
||||
this.jdbc = jdbc;
|
||||
this.order = properties.getOrder();
|
||||
this.sql = properties.getSql();
|
||||
this.sqlWithoutProfile = properties.getSqlWithoutProfile();
|
||||
this.failOnError = properties.isFailOnError();
|
||||
this.extractor = extractor;
|
||||
this.configIncomplete = properties.isConfigIncomplete();
|
||||
this.defaultLabel = properties.getDefaultLabel();
|
||||
}
|
||||
|
||||
public String getSql() {
|
||||
return this.sql;
|
||||
}
|
||||
|
||||
public void setSql(String sql) {
|
||||
this.sql = sql;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Environment findOne(String application, String profile, String label) {
|
||||
String config = application;
|
||||
if (StringUtils.isEmpty(label)) {
|
||||
label = this.defaultLabel;
|
||||
}
|
||||
if (!StringUtils.hasText(profile)) {
|
||||
profile = "default";
|
||||
}
|
||||
// fallback to previous logic: always include profile "default"
|
||||
if (configIncomplete && !profile.startsWith("default")) {
|
||||
profile = "default," + profile;
|
||||
}
|
||||
String[] profiles = StringUtils.commaDelimitedListToStringArray(profile);
|
||||
Environment environment = new Environment(application, profiles, label, null, null);
|
||||
if (!config.startsWith("application")) {
|
||||
config = "application," + config;
|
||||
}
|
||||
List<String> applications = new ArrayList<>(
|
||||
new LinkedHashSet<>(Arrays.asList(StringUtils.commaDelimitedListToStringArray(config))));
|
||||
List<String> envs = new ArrayList<>(new LinkedHashSet<>(Arrays.asList(profiles)));
|
||||
Collections.reverse(applications);
|
||||
Collections.reverse(envs);
|
||||
for (String env : envs) {
|
||||
for (String app : applications) {
|
||||
addPropertySource(environment, app, env, label);
|
||||
}
|
||||
}
|
||||
// add properties without profile, equivalent to foo.yml, application.yml
|
||||
if (!configIncomplete) {
|
||||
for (String app : applications) {
|
||||
addPropertySource(environment, app, null, label);
|
||||
}
|
||||
}
|
||||
return environment;
|
||||
}
|
||||
|
||||
private void addPropertySource(Environment environment, String application, String profile, String label) {
|
||||
try {
|
||||
Map<String, Object> source;
|
||||
String name;
|
||||
if (profile != null) {
|
||||
source = this.jdbc.query(this.sql, this.extractor, application, profile, label);
|
||||
name = application + "-" + profile;
|
||||
}
|
||||
else {
|
||||
source = this.jdbc.query(this.sqlWithoutProfile, this.extractor, application, label);
|
||||
name = application;
|
||||
}
|
||||
if (source != null && !source.isEmpty()) {
|
||||
environment.add(new PropertySource(name, source));
|
||||
}
|
||||
}
|
||||
catch (DataAccessException e) {
|
||||
if (!failOnError) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Failed to retrieve configuration from JDBC Repository", e);
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return this.order;
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
public boolean isFailOnError() {
|
||||
return failOnError;
|
||||
}
|
||||
|
||||
public void setFailOnError(boolean failOnError) {
|
||||
this.failOnError = failOnError;
|
||||
}
|
||||
|
||||
public static class PropertiesResultSetExtractor implements ResultSetExtractor<Map<String, Object>> {
|
||||
|
||||
@Override
|
||||
public Map<String, Object> extractData(ResultSet rs) throws SQLException, DataAccessException {
|
||||
Map<String, Object> map = new LinkedHashMap<>();
|
||||
while (rs.next()) {
|
||||
map.put(rs.getString(1), rs.getString(2));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2019 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.jdbc.core.JdbcTemplate;
|
||||
|
||||
/**
|
||||
* @author Dylan Roberts
|
||||
*/
|
||||
public class JdbcEnvironmentRepositoryFactory
|
||||
implements EnvironmentRepositoryFactory<JdbcEnvironmentRepository, JdbcEnvironmentProperties> {
|
||||
|
||||
private final JdbcTemplate jdbc;
|
||||
|
||||
private final JdbcEnvironmentRepository.PropertiesResultSetExtractor extractor;
|
||||
|
||||
@Deprecated
|
||||
public JdbcEnvironmentRepositoryFactory(JdbcTemplate jdbc) {
|
||||
this(jdbc, new JdbcEnvironmentRepository.PropertiesResultSetExtractor());
|
||||
}
|
||||
|
||||
public JdbcEnvironmentRepositoryFactory(JdbcTemplate jdbc,
|
||||
JdbcEnvironmentRepository.PropertiesResultSetExtractor extractor) {
|
||||
this.jdbc = jdbc;
|
||||
this.extractor = extractor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JdbcEnvironmentRepository build(JdbcEnvironmentProperties environmentProperties) {
|
||||
return new JdbcEnvironmentRepository(this.jdbc, environmentProperties, extractor);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2019 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.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.cloud.config.server.ssh.HostKeyAlgoSupported;
|
||||
import org.springframework.cloud.config.server.ssh.HostKeyAndAlgoBothExist;
|
||||
import org.springframework.cloud.config.server.ssh.KnownHostsFileIsValid;
|
||||
import org.springframework.cloud.config.server.ssh.PrivateKeyIsValid;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
/**
|
||||
* @author Dylan Roberts
|
||||
*/
|
||||
@ConfigurationProperties("spring.cloud.config.server.git")
|
||||
@Validated
|
||||
@PrivateKeyIsValid
|
||||
@HostKeyAndAlgoBothExist
|
||||
@HostKeyAlgoSupported
|
||||
@KnownHostsFileIsValid
|
||||
public class MultipleJGitEnvironmentProperties extends JGitEnvironmentProperties {
|
||||
|
||||
/**
|
||||
* Map of repository identifier to location and other properties.
|
||||
*/
|
||||
private Map<String, PatternMatchingJGitEnvironmentProperties> repos = new LinkedHashMap<>();
|
||||
|
||||
public Map<String, PatternMatchingJGitEnvironmentProperties> getRepos() {
|
||||
return this.repos;
|
||||
}
|
||||
|
||||
public void setRepos(Map<String, PatternMatchingJGitEnvironmentProperties> repos) {
|
||||
this.repos = repos;
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link JGitEnvironmentProperties} that matches patterns.
|
||||
*/
|
||||
public static class PatternMatchingJGitEnvironmentProperties extends JGitEnvironmentProperties {
|
||||
|
||||
/**
|
||||
* Pattern to match on application name and profiles.
|
||||
*/
|
||||
private String[] pattern = new String[0];
|
||||
|
||||
/**
|
||||
* Name of repository (same as map key by default).
|
||||
*/
|
||||
private String name;
|
||||
|
||||
public PatternMatchingJGitEnvironmentProperties() {
|
||||
}
|
||||
|
||||
public PatternMatchingJGitEnvironmentProperties(String uri) {
|
||||
setUri(uri);
|
||||
}
|
||||
|
||||
public String[] getPattern() {
|
||||
return this.pattern;
|
||||
}
|
||||
|
||||
public void setPattern(String[] pattern) {
|
||||
this.pattern = pattern;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,350 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2019 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.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.cloud.config.environment.Environment;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.util.PatternMatchUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link EnvironmentRepository} that based on one or more git repositories. Can be
|
||||
* configured just like a single {@link JGitEnvironmentRepository}, for the "default"
|
||||
* properties, and then additional repositories can be registered by name. The simplest
|
||||
* form of the registration is just a map from name to uri (plus credentials if needed),
|
||||
* where each app has its own git repository. As well as a name you can provide a pattern
|
||||
* that matches on the application name (or even a list of patterns). Each sub-repository
|
||||
* additionally can have its own search paths (subdirectories inside the top level of the
|
||||
* repository).
|
||||
*
|
||||
* @author Andy Chan (iceycake)
|
||||
* @author Dave Syer
|
||||
* @author Gareth Clay
|
||||
*
|
||||
*/
|
||||
public class MultipleJGitEnvironmentRepository extends JGitEnvironmentRepository {
|
||||
|
||||
/**
|
||||
* Map of repository identifier to location and other properties.
|
||||
*/
|
||||
private Map<String, PatternMatchingJGitEnvironmentRepository> repos = new LinkedHashMap<>();
|
||||
|
||||
private Map<String, JGitEnvironmentRepository> placeholders = new LinkedHashMap<>();
|
||||
|
||||
private final ObservationRegistry observationRegistry;
|
||||
|
||||
public MultipleJGitEnvironmentRepository(ConfigurableEnvironment environment,
|
||||
MultipleJGitEnvironmentProperties properties, ObservationRegistry observationRegistry) {
|
||||
super(environment, properties, observationRegistry);
|
||||
this.observationRegistry = observationRegistry;
|
||||
properties.getRepos().forEach((name, props) -> this.repos.put(name,
|
||||
new PatternMatchingJGitEnvironmentRepository(environment, props, this.observationRegistry)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
super.afterPropertiesSet();
|
||||
for (String name : this.repos.keySet()) {
|
||||
PatternMatchingJGitEnvironmentRepository repo = this.repos.get(name);
|
||||
repo.setEnvironment(getEnvironment());
|
||||
if (!StringUtils.hasText(repo.getName())) {
|
||||
repo.setName(name);
|
||||
}
|
||||
if (repo.getPattern() == null || repo.getPattern().length == 0) {
|
||||
repo.setPattern(new String[] { name });
|
||||
}
|
||||
if (repo.getTransportConfigCallback() == null) {
|
||||
repo.setTransportConfigCallback(getTransportConfigCallback());
|
||||
}
|
||||
if (getTimeout() != 0 && repo.getTimeout() == 0) {
|
||||
repo.setTimeout(getTimeout());
|
||||
}
|
||||
if (getRefreshRate() != 0 && repo.getRefreshRate() == 0) {
|
||||
repo.setRefreshRate(getRefreshRate());
|
||||
}
|
||||
String user = repo.getUsername();
|
||||
String passphrase = repo.getPassphrase();
|
||||
if (user == null) {
|
||||
repo.setUsername(getUsername());
|
||||
repo.setPassword(getPassword());
|
||||
}
|
||||
if (passphrase == null) {
|
||||
repo.setPassphrase(getPassphrase());
|
||||
}
|
||||
if (isSkipSslValidation()) {
|
||||
repo.setSkipSslValidation(true);
|
||||
}
|
||||
repo.afterPropertiesSet();
|
||||
}
|
||||
if (!getBasedir().exists() && !getBasedir().mkdirs()) {
|
||||
throw new IllegalStateException("Basedir does not exist and can not be created: " + getBasedir());
|
||||
}
|
||||
if (!getBasedir().getParentFile().canWrite()) {
|
||||
throw new IllegalStateException(
|
||||
"Cannot write parent of basedir (please configure a writable location): " + getBasedir());
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, PatternMatchingJGitEnvironmentRepository> getRepos() {
|
||||
return this.repos;
|
||||
}
|
||||
|
||||
public void setRepos(Map<String, PatternMatchingJGitEnvironmentRepository> repos) {
|
||||
this.repos.putAll(repos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locations getLocations(String application, String profile, String label) {
|
||||
for (PatternMatchingJGitEnvironmentRepository repository : this.repos.values()) {
|
||||
if (repository.matches(application, profile, label)) {
|
||||
for (JGitEnvironmentRepository candidate : getRepositories(repository, application, profile, label)) {
|
||||
try {
|
||||
Environment source = candidate.findOne(application, profile, label, false);
|
||||
if (source != null) {
|
||||
return candidate.getLocations(application, profile, label);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
this.logger.debug("Cannot retrieve resource locations from " + candidate.getUri()
|
||||
+ ", cause: (" + e.getClass().getSimpleName() + ") " + e.getMessage(), e);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
JGitEnvironmentRepository candidate = getRepository(this, application, profile, label);
|
||||
if (candidate == this) {
|
||||
return super.getLocations(application, profile, label);
|
||||
}
|
||||
return candidate.getLocations(application, profile, label);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Environment findOne(String application, String profile, String label, boolean includeOrigin) {
|
||||
for (PatternMatchingJGitEnvironmentRepository repository : this.repos.values()) {
|
||||
if (repository.matches(application, profile, label)) {
|
||||
for (JGitEnvironmentRepository candidate : getRepositories(repository, application, profile, label)) {
|
||||
try {
|
||||
if (label == null) {
|
||||
label = candidate.getDefaultLabel();
|
||||
}
|
||||
Environment source = candidate.findOne(application, profile, label, includeOrigin);
|
||||
if (source != null) {
|
||||
return source;
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
this.logger.debug("Cannot load configuration from " + candidate.getUri() + ", cause: ("
|
||||
+ e.getClass().getSimpleName() + ") " + e.getMessage(), e);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
JGitEnvironmentRepository candidate = getRepository(this, application, profile, label);
|
||||
if (label == null) {
|
||||
label = candidate.getDefaultLabel();
|
||||
}
|
||||
try {
|
||||
return findOneFromCandidate(candidate, application, profile, label, includeOrigin);
|
||||
}
|
||||
catch (Exception e) {
|
||||
if (MultipleJGitEnvironmentProperties.MAIN_LABEL.equals(label) && isTryMasterBranch()) {
|
||||
logger.info("Cannot find Environment with default label " + getDefaultLabel(), e);
|
||||
logger.info("Will try to find Environment master label instead.");
|
||||
candidate = getRepository(this, application, profile, MultipleJGitEnvironmentProperties.MASTER_LABEL);
|
||||
return findOneFromCandidate(candidate, application, profile,
|
||||
MultipleJGitEnvironmentProperties.MASTER_LABEL, includeOrigin);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private Environment findOneFromCandidate(JGitEnvironmentRepository candidate, String application, String profile,
|
||||
String label, boolean includeOrigin) {
|
||||
if (candidate == this) {
|
||||
return super.findOne(application, profile, label, includeOrigin);
|
||||
}
|
||||
return candidate.findOne(application, profile, label, includeOrigin);
|
||||
}
|
||||
|
||||
private List<JGitEnvironmentRepository> getRepositories(JGitEnvironmentRepository repository, String application,
|
||||
String profile, String label) {
|
||||
List<JGitEnvironmentRepository> list = new ArrayList<>();
|
||||
String[] profiles = profile == null ? new String[] { null }
|
||||
: StringUtils.commaDelimitedListToStringArray(profile);
|
||||
for (int i = profiles.length; i-- > 0;) {
|
||||
list.add(getRepository(repository, application, profiles[i], label));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
JGitEnvironmentRepository getRepository(JGitEnvironmentRepository repository, String application, String profile,
|
||||
String label) {
|
||||
if (!repository.getUri().contains("{")) {
|
||||
return repository;
|
||||
}
|
||||
String key = repository.getUri();
|
||||
|
||||
// cover the case where label is in the uri, but no label was sent with the
|
||||
// request
|
||||
if (key.contains("{label}") && label == null) {
|
||||
label = repository.getDefaultLabel();
|
||||
}
|
||||
if (application != null) {
|
||||
key = key.replace("{application}", application);
|
||||
}
|
||||
if (profile != null) {
|
||||
key = key.replace("{profile}", profile);
|
||||
}
|
||||
if (label != null) {
|
||||
key = key.replace("{label}", label);
|
||||
}
|
||||
if (!this.placeholders.containsKey(key)) {
|
||||
this.placeholders.put(key, getRepository(repository, key));
|
||||
}
|
||||
return this.placeholders.get(key);
|
||||
}
|
||||
|
||||
private JGitEnvironmentRepository getRepository(JGitEnvironmentRepository source, String uri) {
|
||||
JGitEnvironmentRepository repository = new JGitEnvironmentRepository(null, new JGitEnvironmentProperties(),
|
||||
observationRegistry);
|
||||
File basedir = repository.getBasedir();
|
||||
BeanUtils.copyProperties(source, repository);
|
||||
repository.setUri(uri);
|
||||
repository.setBasedir(new File(source.getBasedir(), basedir.getName()));
|
||||
return repository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOrder(int order) {
|
||||
super.setOrder(order);
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link JGitEnvironmentProperties} that matches patterns.
|
||||
*/
|
||||
public static class PatternMatchingJGitEnvironmentRepository extends JGitEnvironmentRepository {
|
||||
|
||||
/**
|
||||
* Pattern to match on application name and profiles.
|
||||
*/
|
||||
private String[] pattern = new String[0];
|
||||
|
||||
/**
|
||||
* Name of repository (same as map key by default).
|
||||
*/
|
||||
private String name;
|
||||
|
||||
private final ObservationRegistry observationRegistry;
|
||||
|
||||
public PatternMatchingJGitEnvironmentRepository(ObservationRegistry observationRegistry) {
|
||||
super(null, new JGitEnvironmentProperties(), observationRegistry);
|
||||
this.observationRegistry = observationRegistry;
|
||||
}
|
||||
|
||||
public PatternMatchingJGitEnvironmentRepository(ConfigurableEnvironment environment,
|
||||
MultipleJGitEnvironmentProperties.PatternMatchingJGitEnvironmentProperties properties,
|
||||
ObservationRegistry observationRegistry) {
|
||||
super(environment, properties, observationRegistry);
|
||||
this.observationRegistry = observationRegistry;
|
||||
this.setPattern(properties.getPattern());
|
||||
this.name = properties.getName();
|
||||
}
|
||||
|
||||
public boolean matches(String application, String profile, String label) {
|
||||
if (this.pattern == null || this.pattern.length == 0) {
|
||||
return false;
|
||||
}
|
||||
String[] profiles = StringUtils.commaDelimitedListToStringArray(profile);
|
||||
for (int i = profiles.length; i-- > 0;) {
|
||||
if (PatternMatchUtils.simpleMatch(this.pattern, application + "/" + profiles[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Environment findOne(String application, String profile, String label, boolean includeOrigin) {
|
||||
|
||||
if (this.pattern == null || this.pattern.length == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (PatternMatchUtils.simpleMatch(this.pattern, application + "/" + profile)) {
|
||||
return super.findOne(application, profile, label, includeOrigin);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String[] getPattern() {
|
||||
return this.pattern;
|
||||
}
|
||||
|
||||
public void setPattern(String[] pattern) {
|
||||
Collection<String> patterns = new ArrayList<>();
|
||||
List<String> otherProfiles = new ArrayList<>();
|
||||
for (String p : pattern) {
|
||||
if (p != null) {
|
||||
if (!p.contains("/")) {
|
||||
// Match any profile
|
||||
patterns.add(p + "/*");
|
||||
}
|
||||
if (!p.endsWith("*")) {
|
||||
// If user supplies only one profile, allow others
|
||||
otherProfiles.add(p + ",*");
|
||||
}
|
||||
}
|
||||
patterns.add(p);
|
||||
}
|
||||
patterns.addAll(otherProfiles);
|
||||
if (!patterns.contains(null)) {
|
||||
// Make sure they are unique
|
||||
patterns = new LinkedHashSet<>(patterns);
|
||||
}
|
||||
this.pattern = patterns.toArray(new String[0]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2019 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.Optional;
|
||||
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import org.eclipse.jgit.transport.HttpTransport;
|
||||
|
||||
import org.springframework.cloud.config.server.config.ConfigServerProperties;
|
||||
import org.springframework.cloud.config.server.support.GitCredentialsProviderFactory;
|
||||
import org.springframework.cloud.config.server.support.TransportConfigCallbackFactory;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
|
||||
/**
|
||||
* @author Dylan Roberts
|
||||
*/
|
||||
public class MultipleJGitEnvironmentRepositoryFactory
|
||||
implements EnvironmentRepositoryFactory<MultipleJGitEnvironmentRepository, MultipleJGitEnvironmentProperties> {
|
||||
|
||||
private ConfigurableEnvironment environment;
|
||||
|
||||
private ConfigServerProperties server;
|
||||
|
||||
private Optional<ConfigurableHttpConnectionFactory> connectionFactory;
|
||||
|
||||
private final TransportConfigCallbackFactory transportConfigCallbackFactory;
|
||||
|
||||
private final GitCredentialsProviderFactory gitCredentialsProviderFactory;
|
||||
|
||||
@Deprecated
|
||||
public MultipleJGitEnvironmentRepositoryFactory(ConfigurableEnvironment environment, ConfigServerProperties server,
|
||||
TransportConfigCallbackFactory transportConfigCallbackFactory) {
|
||||
this(environment, server, Optional.empty(), transportConfigCallbackFactory,
|
||||
new GitCredentialsProviderFactory());
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public MultipleJGitEnvironmentRepositoryFactory(ConfigurableEnvironment environment, ConfigServerProperties server,
|
||||
Optional<ConfigurableHttpConnectionFactory> connectionFactory,
|
||||
TransportConfigCallbackFactory transportConfigCallbackFactory) {
|
||||
this(environment, server, connectionFactory, transportConfigCallbackFactory,
|
||||
new GitCredentialsProviderFactory());
|
||||
}
|
||||
|
||||
public MultipleJGitEnvironmentRepositoryFactory(ConfigurableEnvironment environment, ConfigServerProperties server,
|
||||
Optional<ConfigurableHttpConnectionFactory> connectionFactory,
|
||||
TransportConfigCallbackFactory transportConfigCallbackFactory,
|
||||
GitCredentialsProviderFactory gitCredentialsProviderFactory) {
|
||||
this.environment = environment;
|
||||
this.server = server;
|
||||
this.connectionFactory = connectionFactory;
|
||||
this.transportConfigCallbackFactory = transportConfigCallbackFactory;
|
||||
this.gitCredentialsProviderFactory = gitCredentialsProviderFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultipleJGitEnvironmentRepository build(MultipleJGitEnvironmentProperties environmentProperties)
|
||||
throws Exception {
|
||||
if (this.connectionFactory.isPresent()) {
|
||||
HttpTransport.setConnectionFactory(this.connectionFactory.get());
|
||||
this.connectionFactory.get().addConfiguration(environmentProperties);
|
||||
}
|
||||
|
||||
MultipleJGitEnvironmentRepository repository = new MultipleJGitEnvironmentRepository(this.environment,
|
||||
environmentProperties, ObservationRegistry.NOOP);
|
||||
repository.setTransportConfigCallback(transportConfigCallbackFactory.build(environmentProperties));
|
||||
if (this.server.getDefaultLabel() != null) {
|
||||
repository.setDefaultLabel(this.server.getDefaultLabel());
|
||||
}
|
||||
repository.setGitCredentialsProviderFactory(gitCredentialsProviderFactory);
|
||||
repository.getRepos()
|
||||
.forEach((name, repo) -> repo.setGitCredentialsProviderFactory(gitCredentialsProviderFactory));
|
||||
return repository;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2019 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;
|
||||
import org.springframework.core.Ordered;
|
||||
|
||||
/**
|
||||
* @author Dylan Roberts
|
||||
*/
|
||||
@ConfigurationProperties("spring.cloud.config.server.native")
|
||||
public class NativeEnvironmentProperties implements EnvironmentRepositoryProperties {
|
||||
|
||||
/**
|
||||
* Flag to determine how to handle exceptions during decryption (default false).
|
||||
*/
|
||||
private Boolean failOnError = false;
|
||||
|
||||
/**
|
||||
* Flag to determine whether label locations should be added.
|
||||
*/
|
||||
private Boolean addLabelLocations = true;
|
||||
|
||||
private String defaultLabel = "master";
|
||||
|
||||
/**
|
||||
* Locations to search for configuration files. Defaults to the same as a Spring Boot
|
||||
* app so [classpath:/,classpath:/config/,file:./,file:./config/].
|
||||
*/
|
||||
private String[] searchLocations = new String[0];
|
||||
|
||||
/**
|
||||
* Version string to be reported for native repository.
|
||||
*/
|
||||
private String version;
|
||||
|
||||
private int order = Ordered.LOWEST_PRECEDENCE;
|
||||
|
||||
public Boolean getFailOnError() {
|
||||
return this.failOnError;
|
||||
}
|
||||
|
||||
public void setFailOnError(Boolean failOnError) {
|
||||
this.failOnError = failOnError;
|
||||
}
|
||||
|
||||
public Boolean getAddLabelLocations() {
|
||||
return this.addLabelLocations;
|
||||
}
|
||||
|
||||
public void setAddLabelLocations(Boolean addLabelLocations) {
|
||||
this.addLabelLocations = addLabelLocations;
|
||||
}
|
||||
|
||||
public String getDefaultLabel() {
|
||||
return this.defaultLabel;
|
||||
}
|
||||
|
||||
public void setDefaultLabel(String defaultLabel) {
|
||||
this.defaultLabel = defaultLabel;
|
||||
}
|
||||
|
||||
public String[] getSearchLocations() {
|
||||
return this.searchLocations;
|
||||
}
|
||||
|
||||
public void setSearchLocations(String[] searchLocations) {
|
||||
this.searchLocations = searchLocations;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
public void setVersion(String version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return this.order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,410 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2019 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.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor;
|
||||
import org.springframework.boot.context.config.ConfigDataEnvironmentUpdateListener;
|
||||
import org.springframework.boot.context.config.ConfigDataLocation;
|
||||
import org.springframework.boot.context.config.ConfigDataResource;
|
||||
import org.springframework.boot.context.config.StandardConfigDataResource;
|
||||
import org.springframework.cloud.config.environment.Environment;
|
||||
import org.springframework.cloud.config.environment.PropertySource;
|
||||
import org.springframework.core.NestedExceptionUtils;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Simple implementation of {@link EnvironmentRepository} that uses a SpringApplication
|
||||
* and configuration files located through the normal protocols. The resulting Environment
|
||||
* is composed of property sources located using the application name as the config file
|
||||
* stem (spring.config.name) and the environment name as a Spring profile.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Roy Clarkson
|
||||
* @author Venil Noronha
|
||||
* @author Daniel Lavoie
|
||||
*/
|
||||
public class NativeEnvironmentRepository implements EnvironmentRepository, SearchPathLocator, Ordered {
|
||||
|
||||
private static final String[] DEFAULT_LOCATIONS = new String[] { "optional:classpath:/",
|
||||
"optional:classpath:/config/", "optional:file:./", "optional:file:./config/" };
|
||||
|
||||
static final Pattern RESOURCE_PATTERN = Pattern.compile("Config resource '(.*?)' via location '(.*)'");
|
||||
|
||||
private static Log logger = LogFactory.getLog(NativeEnvironmentRepository.class);
|
||||
|
||||
private String defaultLabel;
|
||||
|
||||
/**
|
||||
* Locations to search for configuration files. Defaults to the same as a Spring Boot
|
||||
* app so [classpath:/,classpath:/config/,file:./,file:./config/].
|
||||
*/
|
||||
private String[] searchLocations;
|
||||
|
||||
/**
|
||||
* Flag to determine how to handle exceptions during decryption (default false).
|
||||
*/
|
||||
private boolean failOnError;
|
||||
|
||||
/**
|
||||
* Flag to determine whether label locations should be added.
|
||||
*/
|
||||
private boolean addLabelLocations;
|
||||
|
||||
/**
|
||||
* Version string to be reported for native repository.
|
||||
*/
|
||||
private String version;
|
||||
|
||||
private ConfigurableEnvironment environment;
|
||||
|
||||
private int order;
|
||||
|
||||
private final ObservationRegistry observationRegistry;
|
||||
|
||||
public NativeEnvironmentRepository(ConfigurableEnvironment environment, NativeEnvironmentProperties properties,
|
||||
ObservationRegistry observationRegistry) {
|
||||
this.environment = environment;
|
||||
this.addLabelLocations = properties.getAddLabelLocations();
|
||||
this.defaultLabel = properties.getDefaultLabel();
|
||||
this.failOnError = properties.getFailOnError();
|
||||
this.order = properties.getOrder();
|
||||
this.observationRegistry = observationRegistry;
|
||||
setSearchLocations(properties.getSearchLocations());
|
||||
this.version = properties.getVersion();
|
||||
}
|
||||
|
||||
public boolean isFailOnError() {
|
||||
return this.failOnError;
|
||||
}
|
||||
|
||||
public void setFailOnError(boolean failOnError) {
|
||||
this.failOnError = failOnError;
|
||||
}
|
||||
|
||||
public boolean isAddLabelLocations() {
|
||||
return this.addLabelLocations;
|
||||
}
|
||||
|
||||
public void setAddLabelLocations(boolean addLabelLocations) {
|
||||
this.addLabelLocations = addLabelLocations;
|
||||
}
|
||||
|
||||
public String getDefaultLabel() {
|
||||
return this.defaultLabel;
|
||||
}
|
||||
|
||||
public void setDefaultLabel(String defaultLabel) {
|
||||
this.defaultLabel = defaultLabel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Environment findOne(String config, String profile, String label) {
|
||||
return findOne(config, profile, label, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Environment findOne(String config, String profile, String label, boolean includeOrigin) {
|
||||
|
||||
try {
|
||||
ConfigurableEnvironment environment = getEnvironment(config, profile, label);
|
||||
DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
|
||||
Map<org.springframework.core.env.PropertySource<?>, PropertySourceConfigData> propertySourceToConfigData = new HashMap<>();
|
||||
ConfigDataEnvironmentPostProcessor.applyTo(environment, resourceLoader, null,
|
||||
StringUtils.commaDelimitedListToSet(profile), new ConfigDataEnvironmentUpdateListener() {
|
||||
@Override
|
||||
public void onPropertySourceAdded(org.springframework.core.env.PropertySource<?> propertySource,
|
||||
ConfigDataLocation location, ConfigDataResource resource) {
|
||||
propertySourceToConfigData.put(propertySource,
|
||||
new PropertySourceConfigData(location, resource));
|
||||
}
|
||||
});
|
||||
|
||||
environment.getPropertySources().remove("config-data-setup");
|
||||
return clean(ObservationEnvironmentRepositoryWrapper
|
||||
.wrap(this.observationRegistry, new PassthruEnvironmentRepository(environment))
|
||||
.findOne(config, profile, label, includeOrigin), propertySourceToConfigData);
|
||||
}
|
||||
catch (Exception e) {
|
||||
String msg = String.format("Could not construct context for config=%s profile=%s label=%s includeOrigin=%b",
|
||||
config, profile, label, includeOrigin);
|
||||
String completeMessage = NestedExceptionUtils.buildMessage(msg,
|
||||
NestedExceptionUtils.getMostSpecificCause(e));
|
||||
throw new FailedToConstructEnvironmentException(completeMessage, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locations getLocations(String application, String profile, String label) {
|
||||
String[] locations = this.searchLocations;
|
||||
if (this.searchLocations == null || this.searchLocations.length == 0) {
|
||||
locations = DEFAULT_LOCATIONS;
|
||||
}
|
||||
Collection<String> output = new LinkedHashSet<String>();
|
||||
|
||||
if (label == null) {
|
||||
label = this.defaultLabel;
|
||||
}
|
||||
for (String location : locations) {
|
||||
String[] profiles = new String[] { profile };
|
||||
if (profile != null) {
|
||||
profiles = StringUtils.commaDelimitedListToStringArray(profile);
|
||||
}
|
||||
String[] apps = new String[] { application };
|
||||
if (application != null) {
|
||||
apps = StringUtils.commaDelimitedListToStringArray(application);
|
||||
}
|
||||
for (String prof : profiles) {
|
||||
for (String app : apps) {
|
||||
String value = location;
|
||||
if (application != null) {
|
||||
value = value.replace("{application}", app);
|
||||
}
|
||||
if (prof != null) {
|
||||
value = value.replace("{profile}", prof);
|
||||
}
|
||||
if (label != null) {
|
||||
value = value.replace("{label}", label);
|
||||
}
|
||||
if (!value.endsWith("/")) {
|
||||
value = value + "/";
|
||||
}
|
||||
if (isDirectory(value)) {
|
||||
output.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.addLabelLocations) {
|
||||
for (String location : locations) {
|
||||
if (StringUtils.hasText(label)) {
|
||||
String labelled = location + label.trim() + "/";
|
||||
if (isDirectory(labelled)) {
|
||||
output.add(labelled);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return new Locations(application, profile, label, this.version, output.toArray(new String[0]));
|
||||
}
|
||||
|
||||
private ConfigurableEnvironment getEnvironment(String application, String profile, String label) {
|
||||
ConfigurableEnvironment environment = new StandardEnvironment();
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("spring.profiles.active", profile);
|
||||
String config = application;
|
||||
if (!config.equals("application")) {
|
||||
config = "application," + config;
|
||||
}
|
||||
map.put("spring.config.name", config);
|
||||
// map.put("encrypt.failOnError=" + this.failOnError);
|
||||
map.put("spring.config.location",
|
||||
StringUtils.arrayToDelimitedString(getLocations(application, profile, label).getLocations(), ";"));
|
||||
// globally ignore config files that are not found
|
||||
map.put("spring.config.on-not-found", "IGNORE");
|
||||
environment.getPropertySources().addFirst(new MapPropertySource("config-data-setup", map));
|
||||
return environment;
|
||||
}
|
||||
|
||||
protected Environment clean(Environment env) {
|
||||
return clean(env, Collections.emptyMap());
|
||||
}
|
||||
|
||||
protected Environment clean(Environment env,
|
||||
Map<org.springframework.core.env.PropertySource<?>, PropertySourceConfigData> propertySourceToConfigData) {
|
||||
Environment result = new Environment(env.getName(), env.getProfiles(), env.getLabel(), this.version,
|
||||
env.getState());
|
||||
for (PropertySource source : env.getPropertySources()) {
|
||||
String originalName = source.getName();
|
||||
String name = originalName;
|
||||
if (this.environment.getPropertySources().contains(name)) {
|
||||
continue;
|
||||
}
|
||||
String[] locations = null;
|
||||
|
||||
PropertySourceConfigData configData = propertySourceToConfigData.get(source.getOriginalPropertySource());
|
||||
// try and get information directly from ConfigData
|
||||
if (configData != null && configData.resource instanceof StandardConfigDataResource) {
|
||||
StandardConfigDataResource configDataResource = (StandardConfigDataResource) configData.resource;
|
||||
// use StandardConfigDataResource as that format is expected still
|
||||
name = configDataResource.toString();
|
||||
locations = configDataLocations(configData.location.split());
|
||||
}
|
||||
else {
|
||||
// if not, try and parse
|
||||
Matcher matcher = RESOURCE_PATTERN.matcher(name);
|
||||
if (matcher.find()) {
|
||||
name = matcher.group(1);
|
||||
locations = new String[] { matcher.group(2) };
|
||||
}
|
||||
}
|
||||
name = name.replace("\\", "/"); // change windows path '\' into '/'
|
||||
name = name.replaceAll("\\[(?=\\w:)", "[/"); // change [D:/path] into
|
||||
// [/D:/path]
|
||||
name = name.replace("applicationConfig: [", "");
|
||||
name = name.replace("file [", "file:");
|
||||
name = name.replace("class path resource [", "classpath:/");
|
||||
if (name.indexOf('[') < 0) {
|
||||
// only remove if there isn't a matching left bracket
|
||||
name = name.replace("]", "");
|
||||
}
|
||||
if (this.searchLocations != null) {
|
||||
boolean matches = matchesLocation(locations, name, result);
|
||||
if (!matches) {
|
||||
// Don't include this one: it wasn't matched by our search locations
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Not adding property source: " + originalName);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
logger.info("Adding property source: " + originalName);
|
||||
if (originalName.contains("document #")) {
|
||||
// this is a multi-document file, use originalName for uniqueness.
|
||||
result.add(new PropertySource(originalName, source.getSource()));
|
||||
}
|
||||
else {
|
||||
// many other file tests rely on the mangled name
|
||||
result.add(new PropertySource(name, source.getSource()));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private String[] configDataLocations(ConfigDataLocation[] locations) {
|
||||
String[] stringLocations = new String[locations.length];
|
||||
for (int i = 0; i < locations.length; i++) {
|
||||
stringLocations[i] = locations[i].toString();
|
||||
}
|
||||
return stringLocations;
|
||||
}
|
||||
|
||||
private boolean matchesLocation(String[] locations, String name, Environment result) {
|
||||
boolean matches = false;
|
||||
String normal = name;
|
||||
if (normal.startsWith("file:")) {
|
||||
normal = StringUtils.cleanPath(new File(normal.substring("file:".length())).getAbsolutePath());
|
||||
}
|
||||
String profile = result.getProfiles() == null ? null
|
||||
: StringUtils.arrayToCommaDelimitedString(result.getProfiles());
|
||||
for (String pattern : getLocations(result.getName(), profile, result.getLabel()).getLocations()) {
|
||||
if (!pattern.contains(":")) {
|
||||
pattern = "file:" + pattern;
|
||||
}
|
||||
if (pattern.startsWith("optional:")) {
|
||||
pattern = pattern.substring("optional:".length());
|
||||
}
|
||||
if (pattern.startsWith("file:")) {
|
||||
pattern = StringUtils.cleanPath(new File(pattern.substring("file:".length())).getAbsolutePath()) + "/";
|
||||
}
|
||||
final String finalPattern = pattern;
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Testing pattern: " + finalPattern + " with property source: " + name);
|
||||
}
|
||||
if (normal.startsWith(finalPattern)) {
|
||||
matches = true;
|
||||
break;
|
||||
}
|
||||
if (locations != null) {
|
||||
matches = Arrays.stream(locations).map(this::cleanFileLocation)
|
||||
.anyMatch(location -> location.startsWith(finalPattern));
|
||||
if (matches) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
|
||||
private String cleanFileLocation(String location) {
|
||||
if (location.startsWith("file:")) {
|
||||
return StringUtils.cleanPath(new File(location.substring("file:".length())).getAbsolutePath()) + "/";
|
||||
}
|
||||
return location;
|
||||
}
|
||||
|
||||
public String[] getSearchLocations() {
|
||||
return this.searchLocations;
|
||||
}
|
||||
|
||||
public void setSearchLocations(String... locations) {
|
||||
this.searchLocations = locations;
|
||||
if (locations != null) {
|
||||
for (int i = 0; i < locations.length; i++) {
|
||||
String location = locations[i];
|
||||
if (isDirectory(location) && !location.endsWith("/")) {
|
||||
location = location + "/";
|
||||
}
|
||||
locations[i] = location;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
public void setVersion(String version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
private boolean isDirectory(String location) {
|
||||
return !location.contains("{") && !location.endsWith(".properties") && !location.endsWith(".yml")
|
||||
&& !location.endsWith(".yaml");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return this.order;
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
private final class PropertySourceConfigData {
|
||||
|
||||
private final ConfigDataLocation location;
|
||||
|
||||
private final ConfigDataResource resource;
|
||||
|
||||
private PropertySourceConfigData(ConfigDataLocation location, ConfigDataResource resource) {
|
||||
this.location = location;
|
||||
this.resource = resource;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2019 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 io.micrometer.observation.ObservationRegistry;
|
||||
|
||||
import org.springframework.cloud.config.server.config.ConfigServerProperties;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
|
||||
/**
|
||||
* @author Dylan Roberts
|
||||
*/
|
||||
public class NativeEnvironmentRepositoryFactory
|
||||
implements EnvironmentRepositoryFactory<NativeEnvironmentRepository, NativeEnvironmentProperties> {
|
||||
|
||||
private ConfigurableEnvironment environment;
|
||||
|
||||
private ConfigServerProperties properties;
|
||||
|
||||
private final ObservationRegistry observationRegistry;
|
||||
|
||||
public NativeEnvironmentRepositoryFactory(ConfigurableEnvironment environment, ConfigServerProperties properties,
|
||||
ObservationRegistry observationRegistry) {
|
||||
this.environment = environment;
|
||||
this.properties = properties;
|
||||
this.observationRegistry = observationRegistry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NativeEnvironmentRepository build(NativeEnvironmentProperties environmentProperties) {
|
||||
NativeEnvironmentRepository repository = new NativeEnvironmentRepository(this.environment,
|
||||
environmentProperties, this.observationRegistry);
|
||||
if (this.properties.getDefaultLabel() != null) {
|
||||
repository.setDefaultLabel(this.properties.getDefaultLabel());
|
||||
}
|
||||
return repository;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015-2019 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;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class NoSuchLabelException extends RepositoryException {
|
||||
|
||||
public NoSuchLabelException(String string) {
|
||||
super(string);
|
||||
}
|
||||
|
||||
public NoSuchLabelException(String string, Exception e) {
|
||||
super(string, e);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015-2019 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;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class NoSuchRepositoryException extends RepositoryException {
|
||||
|
||||
public NoSuchRepositoryException(String string) {
|
||||
super(string);
|
||||
}
|
||||
|
||||
public NoSuchRepositoryException(String string, Exception e) {
|
||||
super(string, e);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2021 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 io.micrometer.observation.Observation;
|
||||
|
||||
/**
|
||||
* {@link Observation.Context} for Spring Cloud Config.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public class ObservationEnvironmentRepositoryContext extends Observation.Context {
|
||||
|
||||
private final Class<? extends EnvironmentRepository> clazz;
|
||||
|
||||
private final String application;
|
||||
|
||||
private final String profile;
|
||||
|
||||
private final String label;
|
||||
|
||||
public ObservationEnvironmentRepositoryContext(Class<? extends EnvironmentRepository> clazz, String application,
|
||||
String profile, String label) {
|
||||
this.clazz = clazz;
|
||||
this.application = application;
|
||||
this.profile = profile;
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public Class<? extends EnvironmentRepository> getEnvironmentRepositoryClass() {
|
||||
return this.clazz;
|
||||
}
|
||||
|
||||
public String getApplication() {
|
||||
return this.application;
|
||||
}
|
||||
|
||||
public String getProfile() {
|
||||
return this.profile;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return this.label;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2021 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 io.micrometer.common.KeyValues;
|
||||
import io.micrometer.common.docs.KeyName;
|
||||
import io.micrometer.observation.Observation;
|
||||
import io.micrometer.observation.ObservationConvention;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Default provider of key values for {@link ObservationEnvironmentRepositoryContext}.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 4.0.0
|
||||
*/
|
||||
class ObservationEnvironmentRepositoryObservationConvention
|
||||
implements ObservationConvention<ObservationEnvironmentRepositoryContext> {
|
||||
|
||||
@Override
|
||||
public KeyValues getLowCardinalityKeyValues(ObservationEnvironmentRepositoryContext context) {
|
||||
KeyValues keyValues = KeyValues.empty();
|
||||
keyValues = appendIfPresent(keyValues, DocumentedConfigObservation.LowCardinalityTags.ENVIRONMENT_CLASS,
|
||||
context.getEnvironmentRepositoryClass().getName());
|
||||
keyValues = appendIfPresent(keyValues, DocumentedConfigObservation.LowCardinalityTags.LABEL,
|
||||
context.getLabel());
|
||||
keyValues = appendIfPresent(keyValues, DocumentedConfigObservation.LowCardinalityTags.PROFILE,
|
||||
context.getProfile());
|
||||
return appendIfPresent(keyValues, DocumentedConfigObservation.LowCardinalityTags.APPLICATION,
|
||||
context.getApplication());
|
||||
}
|
||||
|
||||
private KeyValues appendIfPresent(KeyValues keyValues, KeyName profile, String value) {
|
||||
if (StringUtils.hasText(value)) {
|
||||
keyValues = keyValues.and(profile.withValue(value));
|
||||
}
|
||||
return keyValues;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsContext(Observation.Context context) {
|
||||
return context instanceof ObservationEnvironmentRepositoryContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "spring.cloud.config.environment.find";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContextualName(ObservationEnvironmentRepositoryContext context) {
|
||||
return "env find";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2021 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 io.micrometer.observation.ObservationRegistry;
|
||||
|
||||
import org.springframework.cloud.config.environment.Environment;
|
||||
|
||||
/**
|
||||
* Wraps a {@link EnvironmentRepository} execution with observation.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public final class ObservationEnvironmentRepositoryWrapper implements EnvironmentRepository {
|
||||
|
||||
private static final ObservationEnvironmentRepositoryObservationConvention CONVENTION = new ObservationEnvironmentRepositoryObservationConvention();
|
||||
|
||||
private final ObservationRegistry registry;
|
||||
|
||||
private final EnvironmentRepository delegate;
|
||||
|
||||
/**
|
||||
* Creates an instance of {@link ObservationEnvironmentRepositoryWrapper}.
|
||||
* @param registry observation registry
|
||||
* @param delegate environment repository delegate
|
||||
*/
|
||||
private ObservationEnvironmentRepositoryWrapper(ObservationRegistry registry, EnvironmentRepository delegate) {
|
||||
this.registry = registry;
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the {@link EnvironmentRepository} in an observation representation.
|
||||
* @param observationRegistry observation registry
|
||||
* @param delegate repository to wrap
|
||||
* @return wrapped repository
|
||||
*/
|
||||
public static EnvironmentRepository wrap(ObservationRegistry observationRegistry, EnvironmentRepository delegate) {
|
||||
if (delegate instanceof ObservationEnvironmentRepositoryWrapper) {
|
||||
return delegate;
|
||||
}
|
||||
return new ObservationEnvironmentRepositoryWrapper(observationRegistry, delegate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Environment findOne(String application, String profile, String label) {
|
||||
ObservationEnvironmentRepositoryContext context = new ObservationEnvironmentRepositoryContext(
|
||||
this.delegate.getClass(), application, profile, label);
|
||||
return DocumentedConfigObservation.ENVIRONMENT_REPOSITORY
|
||||
.observation(null, CONVENTION, () -> context, this.registry)
|
||||
.observe(() -> this.delegate.findOne(application, profile, label));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Environment findOne(String application, String profile, String label, boolean includeOrigin) {
|
||||
ObservationEnvironmentRepositoryContext context = new ObservationEnvironmentRepositoryContext(
|
||||
this.delegate.getClass(), application, profile, label);
|
||||
return DocumentedConfigObservation.ENVIRONMENT_REPOSITORY
|
||||
.observation(null, CONVENTION, () -> context, this.registry)
|
||||
.observe(() -> this.delegate.findOne(application, profile, label, includeOrigin));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the actual delegate.
|
||||
* @return delegate
|
||||
*/
|
||||
public EnvironmentRepository getDelegate() {
|
||||
return delegate;
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user