Added checkstyle

This commit is contained in:
Marcin Grzejszczak
2019-02-05 16:49:55 +01:00
parent f9144b1510
commit 2220ed3a32
196 changed files with 4001 additions and 3024 deletions

View File

@@ -1,7 +1,18 @@
# EditorConfig is awesome: http://EditorConfig.org
# top-most EditorConfig file
root = true
[*]
end_of_line = lf
insert_final_newline = true
indent_style = tab
indent_size = 4
end_of_line = lf
insert_final_newline = true
[*.yml]
indent_style = space
indent_size = 2
[*.yaml]
indent_style = space
indent_size = 2

117
.mvn/wrapper/MavenWrapperDownloader.java vendored Executable file
View File

@@ -0,0 +1,117 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you 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
http://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.
*/
import java.util.Properties;
public class MavenWrapperDownloader {
/**
* Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is
* provided.
*/
private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar";
/**
* Path to the maven-wrapper.properties file, which might contain a downloadUrl
* property to use instead of the default one.
*/
private static final String MAVEN_WRAPPER_PROPERTIES_PATH = ".mvn/wrapper/maven-wrapper.properties";
/**
* Path where the maven-wrapper.jar will be saved to.
*/
private static final String MAVEN_WRAPPER_JAR_PATH = ".mvn/wrapper/maven-wrapper.jar";
/**
* Name of the property which should be used to override the default download url for
* the wrapper.
*/
private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
public static void main(String args[]) {
System.out.println("- Downloader started");
File baseDirectory = new File(args[0]);
System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
// If the maven-wrapper.properties exists, read it and check if it contains a
// custom
// wrapperUrl parameter.
File mavenWrapperPropertyFile = new File(baseDirectory,
MAVEN_WRAPPER_PROPERTIES_PATH);
String url = DEFAULT_DOWNLOAD_URL;
if (mavenWrapperPropertyFile.exists()) {
FileInputStream mavenWrapperPropertyFileInputStream = null;
try {
mavenWrapperPropertyFileInputStream = new FileInputStream(
mavenWrapperPropertyFile);
Properties mavenWrapperProperties = new Properties();
mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
}
catch (IOException e) {
System.out.println(
"- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
}
finally {
try {
if (mavenWrapperPropertyFileInputStream != null) {
mavenWrapperPropertyFileInputStream.close();
}
}
catch (IOException e) {
// Ignore ...
}
}
}
System.out.println("- Downloading from: : " + url);
File outputFile = new File(baseDirectory.getAbsolutePath(),
MAVEN_WRAPPER_JAR_PATH);
if (!outputFile.getParentFile().exists()) {
if (!outputFile.getParentFile().mkdirs()) {
System.out.println("- ERROR creating output direcrory '"
+ outputFile.getParentFile().getAbsolutePath() + "'");
}
}
System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
try {
downloadFileFromURL(url, outputFile);
System.out.println("Done");
System.exit(0);
}
catch (Throwable e) {
System.out.println("- Error downloading");
e.printStackTrace();
System.exit(1);
}
}
private static void downloadFileFromURL(String urlString, File destination)
throws Exception {
URL website = new URL(urlString);
ReadableByteChannel rbc;
rbc = Channels.newChannel(website.openStream());
FileOutputStream fos = new FileOutputStream(destination);
fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
fos.close();
rbc.close();
}
}

BIN
.mvn/wrapper/maven-wrapper.jar vendored Normal file → Executable file

Binary file not shown.

2
.mvn/wrapper/maven-wrapper.properties vendored Normal file → Executable file
View File

@@ -1 +1 @@
distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.0/apache-maven-3.5.0-bin.zip
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.5.4/apache-maven-3.5.4-bin.zip

0
.springformat Normal file
View File

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.cloud</groupId>

177
mvnw vendored
View File

@@ -54,38 +54,16 @@ case "`uname`" in
CYGWIN*) cygwin=true ;;
MINGW*) mingw=true;;
Darwin*) darwin=true
#
# Look for the Apple JDKs first to preserve the existing behaviour, and then look
# for the new JDKs provided by Oracle.
#
if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then
#
# Apple JDKs
#
export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home
fi
if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then
#
# Apple JDKs
#
export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home
fi
if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then
#
# Oracle JDKs
#
export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home
fi
if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then
#
# Apple JDKs
#
export JAVA_HOME=`/usr/libexec/java_home`
fi
;;
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
if [ -z "$JAVA_HOME" ]; then
if [ -x "/usr/libexec/java_home" ]; then
export JAVA_HOME="`/usr/libexec/java_home`"
else
export JAVA_HOME="/Library/Java/Home"
fi
fi
;;
esac
if [ -z "$JAVA_HOME" ] ; then
@@ -130,7 +108,7 @@ if $cygwin ; then
CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
fi
# For Migwn, ensure paths are in UNIX format before anything is touched
# For Mingw, ensure paths are in UNIX format before anything is touched
if $mingw ; then
[ -n "$M2_HOME" ] &&
M2_HOME="`(cd "$M2_HOME"; pwd)`"
@@ -184,27 +162,28 @@ fi
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
# For Cygwin, switch paths to Windows format before running java
if $cygwin; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --path --windows "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
fi
# traverses directory structure from process work directory to filesystem root
# first directory with .mvn subdirectory is considered project base directory
find_maven_basedir() {
local basedir=$(pwd)
local wdir=$(pwd)
if [ -z "$1" ]
then
echo "Path not specified to find_maven_basedir"
return 1
fi
basedir="$1"
wdir="$1"
while [ "$wdir" != '/' ] ; do
if [ -d "$wdir"/.mvn ] ; then
basedir=$wdir
break
fi
wdir=$(cd "$wdir/.."; pwd)
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
if [ -d "${wdir}" ]; then
wdir=`cd "$wdir/.."; pwd`
fi
# end of workaround
done
echo "${basedir}"
}
@@ -216,38 +195,92 @@ concat_lines() {
fi
}
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)}
BASE_DIR=`find_maven_basedir "$(pwd)"`
if [ -z "$BASE_DIR" ]; then
exit 1;
fi
##########################################################################################
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
# This allows using the maven wrapper in projects that prohibit checking in binary data.
##########################################################################################
if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found .mvn/wrapper/maven-wrapper.jar"
fi
else
if [ "$MVNW_VERBOSE" = true ]; then
echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
fi
jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar"
while IFS="=" read key value; do
case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
esac
done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
if [ "$MVNW_VERBOSE" = true ]; then
echo "Downloading from: $jarUrl"
fi
wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
if command -v wget > /dev/null; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found wget ... using wget"
fi
wget "$jarUrl" -O "$wrapperJarPath"
elif command -v curl > /dev/null; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found curl ... using curl"
fi
curl -o "$wrapperJarPath" "$jarUrl"
else
if [ "$MVNW_VERBOSE" = true ]; then
echo "Falling back to using Java to download"
fi
javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
if [ -e "$javaClass" ]; then
if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
if [ "$MVNW_VERBOSE" = true ]; then
echo " - Compiling MavenWrapperDownloader.java ..."
fi
# Compiling the Java class
("$JAVA_HOME/bin/javac" "$javaClass")
fi
if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
# Running the downloader
if [ "$MVNW_VERBOSE" = true ]; then
echo " - Running MavenWrapperDownloader.java ..."
fi
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
fi
fi
fi
fi
##########################################################################################
# End of extension
##########################################################################################
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
if [ "$MVNW_VERBOSE" = true ]; then
echo $MAVEN_PROJECTBASEDIR
fi
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
# Provide a "standardized" way to retrieve the CLI args that will
# work with both Windows and non-Windows executions.
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
export MAVEN_CMD_LINE_ARGS
# For Cygwin, switch paths to Windows format before running java
if $cygwin; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --path --windows "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
fi
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
echo "Running version check"
VERSION=$( sed '\!<parent!,\!</parent!d' `dirname $0`/pom.xml | grep '<version' | head -1 | sed -e 's/.*<version>//' -e 's!</version>.*$!!' )
echo "The found version is [${VERSION}]"
if echo $VERSION | egrep -q 'M|RC'; then
echo Activating \"milestone\" profile for version=\"$VERSION\"
echo $MAVEN_ARGS | grep -q milestone || MAVEN_ARGS="$MAVEN_ARGS -Pmilestone"
else
echo Deactivating \"milestone\" profile for version=\"$VERSION\"
echo $MAVEN_ARGS | grep -q milestone && MAVEN_ARGS=$(echo $MAVEN_ARGS | sed -e 's/-Pmilestone//')
fi
if echo $VERSION | egrep -q 'RELEASE'; then
echo Activating \"central\" profile for version=\"$VERSION\"
echo $MAVEN_ARGS | grep -q milestone || MAVEN_ARGS="$MAVEN_ARGS -Pcentral"
else
echo Deactivating \"central\" profile for version=\"$VERSION\"
echo $MAVEN_ARGS | grep -q central && MAVEN_ARGS=$(echo $MAVEN_ARGS | sed -e 's/-Pcentral//')
fi
exec "$JAVACMD" \
$MAVEN_OPTS \
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
"-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
${WRAPPER_LAUNCHER} ${MAVEN_ARGS} "$@"
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"

306
mvnw.cmd vendored Normal file → Executable file
View File

@@ -1,145 +1,161 @@
@REM ----------------------------------------------------------------------------
@REM Licensed to the Apache Software Foundation (ASF) under one
@REM or more contributor license agreements. See the NOTICE file
@REM distributed with this work for additional information
@REM regarding copyright ownership. The ASF licenses this file
@REM to you under the Apache License, Version 2.0 (the
@REM "License"); you may not use this file except in compliance
@REM with the License. You may obtain a copy of the License at
@REM
@REM http://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing,
@REM software distributed under the License is distributed on an
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@REM KIND, either express or implied. See the License for the
@REM specific language governing permissions and limitations
@REM under the License.
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
@REM Maven2 Start Up Batch script
@REM
@REM Required ENV vars:
@REM JAVA_HOME - location of a JDK home dir
@REM
@REM Optional ENV vars
@REM M2_HOME - location of maven2's installed home dir
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
@REM e.g. to debug Maven itself, use
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
@REM ----------------------------------------------------------------------------
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
@echo off
@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
@REM set %HOME% to equivalent of $HOME
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
@REM Execute a user defined script before this one
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
:skipRcPre
@setlocal
set ERROR_CODE=0
@REM To isolate internal variables from possible post scripts, we use another setlocal
@setlocal
@REM ==== START VALIDATION ====
if not "%JAVA_HOME%" == "" goto OkJHome
echo.
echo Error: JAVA_HOME not found in your environment. >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
:OkJHome
if exist "%JAVA_HOME%\bin\java.exe" goto init
echo.
echo Error: JAVA_HOME is set to an invalid directory. >&2
echo JAVA_HOME = "%JAVA_HOME%" >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
@REM ==== END VALIDATION ====
:init
set MAVEN_CMD_LINE_ARGS=%*
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
@REM Fallback to current working directory if not found.
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
set EXEC_DIR=%CD%
set WDIR=%EXEC_DIR%
:findBaseDir
IF EXIST "%WDIR%"\.mvn goto baseDirFound
cd ..
IF "%WDIR%"=="%CD%" goto baseDirNotFound
set WDIR=%CD%
goto findBaseDir
:baseDirFound
set MAVEN_PROJECTBASEDIR=%WDIR%
cd "%EXEC_DIR%"
goto endDetectBaseDir
:baseDirNotFound
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
cd "%EXEC_DIR%"
:endDetectBaseDir
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
@setlocal EnableExtensions EnableDelayedExpansion
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
:endReadAdditionalConfig
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
set WRAPPER_JAR="".\.mvn\wrapper\maven-wrapper.jar""
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CMD_LINE_ARGS%
if ERRORLEVEL 1 goto error
goto end
:error
set ERROR_CODE=1
:end
@endlocal & set ERROR_CODE=%ERROR_CODE%
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
@REM check for post script, once with legacy .bat ending and once with .cmd ending
if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
:skipRcPost
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
if "%MAVEN_BATCH_PAUSE%" == "on" pause
if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
exit /B %ERROR_CODE%
@REM ----------------------------------------------------------------------------
@REM Licensed to the Apache Software Foundation (ASF) under one
@REM or more contributor license agreements. See the NOTICE file
@REM distributed with this work for additional information
@REM regarding copyright ownership. The ASF licenses this file
@REM to you under the Apache License, Version 2.0 (the
@REM "License"); you may not use this file except in compliance
@REM with the License. You may obtain a copy of the License at
@REM
@REM http://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing,
@REM software distributed under the License is distributed on an
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@REM KIND, either express or implied. See the License for the
@REM specific language governing permissions and limitations
@REM under the License.
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
@REM Maven2 Start Up Batch script
@REM
@REM Required ENV vars:
@REM JAVA_HOME - location of a JDK home dir
@REM
@REM Optional ENV vars
@REM M2_HOME - location of maven2's installed home dir
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
@REM e.g. to debug Maven itself, use
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
@REM ----------------------------------------------------------------------------
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
@echo off
@REM set title of command window
title %0
@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
@REM set %HOME% to equivalent of $HOME
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
@REM Execute a user defined script before this one
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
:skipRcPre
@setlocal
set ERROR_CODE=0
@REM To isolate internal variables from possible post scripts, we use another setlocal
@setlocal
@REM ==== START VALIDATION ====
if not "%JAVA_HOME%" == "" goto OkJHome
echo.
echo Error: JAVA_HOME not found in your environment. >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
:OkJHome
if exist "%JAVA_HOME%\bin\java.exe" goto init
echo.
echo Error: JAVA_HOME is set to an invalid directory. >&2
echo JAVA_HOME = "%JAVA_HOME%" >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
@REM ==== END VALIDATION ====
:init
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
@REM Fallback to current working directory if not found.
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
set EXEC_DIR=%CD%
set WDIR=%EXEC_DIR%
:findBaseDir
IF EXIST "%WDIR%"\.mvn goto baseDirFound
cd ..
IF "%WDIR%"=="%CD%" goto baseDirNotFound
set WDIR=%CD%
goto findBaseDir
:baseDirFound
set MAVEN_PROJECTBASEDIR=%WDIR%
cd "%EXEC_DIR%"
goto endDetectBaseDir
:baseDirNotFound
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
cd "%EXEC_DIR%"
:endDetectBaseDir
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
@setlocal EnableExtensions EnableDelayedExpansion
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
:endReadAdditionalConfig
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar"
FOR /F "tokens=1,2 delims==" %%A IN (%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties) DO (
IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
)
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
if exist %WRAPPER_JAR% (
echo Found %WRAPPER_JAR%
) else (
echo Couldn't find %WRAPPER_JAR%, downloading it ...
echo Downloading from: %DOWNLOAD_URL%
powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"
echo Finished downloading %WRAPPER_JAR%
)
@REM End of extension
%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
if ERRORLEVEL 1 goto error
goto end
:error
set ERROR_CODE=1
:end
@endlocal & set ERROR_CODE=%ERROR_CODE%
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
@REM check for post script, once with legacy .bat ending and once with .cmd ending
if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
:skipRcPost
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
if "%MAVEN_BATCH_PAUSE%" == "on" pause
if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
exit /B %ERROR_CODE%

34
pom.xml
View File

@@ -16,8 +16,8 @@
~
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
@@ -52,8 +52,11 @@
<scm>
<url>https://github.com/spring-cloud-incubator/spring-cloud-kubernetes</url>
<connection>scm:git:git://github.com/spring-cloud-incubator/spring-cloud-kubernetes.git</connection>
<developerConnection>scm:git:ssh://git@github.com/spring-cloud-incubator/spring-cloud-kubernetes.git
<connection>
scm:git:git://github.com/spring-cloud-incubator/spring-cloud-kubernetes.git
</connection>
<developerConnection>
scm:git:ssh://git@github.com/spring-cloud-incubator/spring-cloud-kubernetes.git
</developerConnection>
<tag>HEAD</tag>
</scm>
@@ -74,6 +77,12 @@
<groovy.version>2.4.12</groovy.version>
<restassured.version>3.0.2</restassured.version>
<spock-spring.version>1.1-groovy-2.4</spock-spring.version>
<maven-checkstyle-plugin.failsOnError>true</maven-checkstyle-plugin.failsOnError>
<maven-checkstyle-plugin.failsOnViolation>true
</maven-checkstyle-plugin.failsOnViolation>
<maven-checkstyle-plugin.includeTestSourceDirectory>true
</maven-checkstyle-plugin.includeTestSourceDirectory>
</properties>
<modules>
@@ -176,9 +185,26 @@
<useSystemClassLoader>false</useSystemClassLoader>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
</plugin>
<plugin>
<groupId>io.spring.javaformat</groupId>
<artifactId>spring-javaformat-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<reporting>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
</plugin>
</plugins>
</reporting>
<profiles>
<profile>
<id>spring</id>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-cloud-kubernetes</artifactId>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,21 +12,27 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.config;
/**
* Abstraction over configuration properties.
*
* @author Ioannis Canellos
*/
public abstract class AbstractConfigProperties {
protected boolean enabled = true;
protected String name;
protected String namespace;
public abstract String getConfigurationTarget();
public boolean isEnabled() {
return enabled;
return this.enabled;
}
public void setEnabled(boolean enabled) {
@@ -34,7 +40,7 @@ public abstract class AbstractConfigProperties {
}
public String getName() {
return name;
return this.name;
}
public void setName(String name) {
@@ -42,10 +48,11 @@ public abstract class AbstractConfigProperties {
}
public String getNamespace() {
return namespace;
return this.namespace;
}
public void setNamespace(String namespace) {
this.namespace = namespace;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,7 +12,6 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.config;
@@ -30,6 +29,11 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* Auto configuration that reuses Kubernetes config maps as property sources.
*
* @author Ioannis Canellos
*/
@Configuration
@ConditionalOnProperty(value = "spring.cloud.kubernetes.enabled", matchIfMissing = true)
@ConditionalOnClass({ ConfigMap.class, Secret.class })
@@ -40,6 +44,7 @@ public class BootstrapConfiguration {
@EnableConfigurationProperties({ ConfigMapConfigProperties.class,
SecretsConfigProperties.class })
protected static class KubernetesPropertySourceConfiguration {
@Autowired
private KubernetesClient client;
@@ -47,14 +52,16 @@ public class BootstrapConfiguration {
@ConditionalOnProperty(name = "spring.cloud.kubernetes.config.enabled", matchIfMissing = true)
public ConfigMapPropertySourceLocator configMapPropertySourceLocator(
ConfigMapConfigProperties properties) {
return new ConfigMapPropertySourceLocator(client, properties);
return new ConfigMapPropertySourceLocator(this.client, properties);
}
@Bean
@ConditionalOnProperty(name = "spring.cloud.kubernetes.secrets.enabled", matchIfMissing = true)
public SecretsPropertySourceLocator secretsPropertySourceLocator(
SecretsConfigProperties properties) {
return new SecretsPropertySourceLocator(client, properties);
return new SecretsPropertySourceLocator(this.client, properties);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,7 +12,6 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.config;
@@ -25,33 +24,40 @@ import java.util.stream.Collectors;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.util.StringUtils;
/**
* Config map configuration properties.
*
* @author Ioannis Canellos
*/
@ConfigurationProperties("spring.cloud.kubernetes.config")
public class ConfigMapConfigProperties extends AbstractConfigProperties {
private static final String TARGET = "Config Map";
private boolean enableApi = true;
private List<String> paths = new LinkedList<>();
private List<Source> sources = new LinkedList<>();
public boolean isEnableApi() {
return enableApi;
return this.enableApi;
}
public void setEnableApi(boolean enableApi) {
this.enableApi = enableApi;
}
public List<String> getPaths() {
return this.paths;
}
public void setPaths(List<String> paths) {
this.paths = paths;
}
public List<String> getPaths() {
return paths;
}
public List<Source> getSources() {
return sources;
return this.sources;
}
public void setSources(List<Source> sources) {
@@ -67,15 +73,16 @@ public class ConfigMapConfigProperties extends AbstractConfigProperties {
* ConfigMapPropertySource
*/
public List<NormalizedSource> determineSources() {
if (sources.isEmpty()) {
if (this.sources.isEmpty()) {
return new ArrayList<NormalizedSource>() {
{
add(new NormalizedSource(name, namespace));
add(new NormalizedSource(ConfigMapConfigProperties.this.name,
ConfigMapConfigProperties.this.namespace));
}
};
}
return sources.stream().map(s -> s.normalize(name, namespace))
return this.sources.stream().map(s -> s.normalize(this.name, this.namespace))
.collect(Collectors.toList());
}
@@ -84,15 +91,18 @@ public class ConfigMapConfigProperties extends AbstractConfigProperties {
return TARGET;
}
/**
* Config map source.
*/
public static class Source {
/**
* The name of the ConfigMap
* The name of the ConfigMap.
*/
private String name;
/**
* The namespace where the ConfigMap is found
* The namespace where the ConfigMap is found.
*/
private String namespace;
@@ -105,7 +115,7 @@ public class ConfigMapConfigProperties extends AbstractConfigProperties {
}
public String getName() {
return name;
return this.name;
}
public void setName(String name) {
@@ -113,7 +123,7 @@ public class ConfigMapConfigProperties extends AbstractConfigProperties {
}
public String getNamespace() {
return namespace;
return this.namespace;
}
public void setNamespace(String namespace) {
@@ -121,7 +131,7 @@ public class ConfigMapConfigProperties extends AbstractConfigProperties {
}
public boolean isEmpty() {
return StringUtils.isEmpty(name) && StringUtils.isEmpty(namespace);
return StringUtils.isEmpty(this.name) && StringUtils.isEmpty(this.namespace);
}
public NormalizedSource normalize(String defaultName, String defaultNamespace) {
@@ -132,23 +142,28 @@ public class ConfigMapConfigProperties extends AbstractConfigProperties {
return new NormalizedSource(normalizedName, normalizedNamespace);
}
}
static class NormalizedSource {
private final String name;
private final String namespace;
public NormalizedSource(String name, String namespace) {
NormalizedSource(String name, String namespace) {
this.name = name;
this.namespace = namespace;
}
public String getName() {
return name;
return this.name;
}
public String getNamespace() {
return namespace;
return this.namespace;
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,32 +12,41 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.config;
import static org.springframework.cloud.kubernetes.config.PropertySourceUtils.KEY_VALUE_TO_PROPERTIES;
import static org.springframework.cloud.kubernetes.config.PropertySourceUtils.PROPERTIES_TO_MAP;
import static org.springframework.cloud.kubernetes.config.PropertySourceUtils.yamlParserGenerator;
import io.fabric8.kubernetes.api.model.ConfigMap;
import io.fabric8.kubernetes.client.KubernetesClient;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.stream.Collectors;
import io.fabric8.kubernetes.api.model.ConfigMap;
import io.fabric8.kubernetes.client.KubernetesClient;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.env.MapPropertySource;
import org.springframework.util.StringUtils;
import static org.springframework.cloud.kubernetes.config.PropertySourceUtils.KEY_VALUE_TO_PROPERTIES;
import static org.springframework.cloud.kubernetes.config.PropertySourceUtils.PROPERTIES_TO_MAP;
import static org.springframework.cloud.kubernetes.config.PropertySourceUtils.yamlParserGenerator;
/**
* A {@link MapPropertySource} that uses Kubernetes config maps.
*
* @author Ioannis Canellos
*/
public class ConfigMapPropertySource extends MapPropertySource {
private static final Log LOG = LogFactory.getLog(ConfigMapPropertySource.class);
private static final String APPLICATION_YML = "application.yml";
private static final String APPLICATION_YAML = "application.yaml";
private static final String APPLICATION_PROPERTIES = "application.properties";
private static final String PREFIX = "configmap";
@@ -47,7 +56,7 @@ public class ConfigMapPropertySource extends MapPropertySource {
}
public ConfigMapPropertySource(KubernetesClient client, String name, String namespace,
String[] profiles) {
String[] profiles) {
super(getName(client, name, namespace),
asObjectMap(getData(client, name, namespace, profiles)));
}
@@ -63,11 +72,11 @@ public class ConfigMapPropertySource extends MapPropertySource {
}
private static Map<String, String> getData(KubernetesClient client, String name,
String namespace, String[] profiles) {
String namespace, String[] profiles) {
try {
ConfigMap map = StringUtils.isEmpty(namespace)
? client.configMaps().withName(name).get()
: client.configMaps().inNamespace(namespace).withName(name).get();
? client.configMaps().withName(name).get()
: client.configMaps().inNamespace(namespace).withName(name).get();
if (map != null) {
return processAllEntries(map.getData(), profiles);
@@ -75,7 +84,7 @@ public class ConfigMapPropertySource extends MapPropertySource {
}
catch (Exception e) {
LOG.warn("Can't read configMap with name: [" + name + "] in namespace:["
+ namespace + "]. Ignoring");
+ namespace + "]. Ignoring");
}
return new HashMap<>();
@@ -97,8 +106,7 @@ public class ConfigMapPropertySource extends MapPropertySource {
+ "] will be treated as a yaml file");
}
return yamlParserGenerator(profiles).andThen(
PROPERTIES_TO_MAP)
return yamlParserGenerator(profiles).andThen(PROPERTIES_TO_MAP)
.apply(propertyValue);
}
else if (propertyName.endsWith(".properties")) {
@@ -107,8 +115,7 @@ public class ConfigMapPropertySource extends MapPropertySource {
+ "] will be treated as a properties file");
}
return KEY_VALUE_TO_PROPERTIES.andThen(
PROPERTIES_TO_MAP)
return KEY_VALUE_TO_PROPERTIES.andThen(PROPERTIES_TO_MAP)
.apply(propertyValue);
}
else {
@@ -124,24 +131,27 @@ public class ConfigMapPropertySource extends MapPropertySource {
return input.entrySet().stream()
.map(e -> extractProperties(e.getKey(), e.getValue(), profiles))
.filter(m -> !m.isEmpty())
.flatMap(m -> m.entrySet().stream())
.filter(m -> !m.isEmpty()).flatMap(m -> m.entrySet().stream())
.collect(Collectors.toMap(Entry::getKey, Entry::getValue));
}
private static Map<String, String> extractProperties(String resourceName,
String content, String[] profiles) {
if (resourceName.equals(APPLICATION_YAML) || resourceName.equals(APPLICATION_YML)) {
return yamlParserGenerator(profiles).andThen(PROPERTIES_TO_MAP).apply(content);
if (resourceName.equals(APPLICATION_YAML)
|| resourceName.equals(APPLICATION_YML)) {
return yamlParserGenerator(profiles).andThen(PROPERTIES_TO_MAP)
.apply(content);
}
else if (resourceName.equals(APPLICATION_PROPERTIES)) {
return KEY_VALUE_TO_PROPERTIES.andThen(PROPERTIES_TO_MAP).apply(content);
}
return new HashMap<String, String>() {{
put(resourceName, content);
}};
return new HashMap<String, String>() {
{
put(resourceName, content);
}
};
}
private static Map<String, Object> asObjectMap(Map<String, String> source) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,27 +12,22 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.config;
import static org.springframework.cloud.kubernetes.config.ConfigUtils.getApplicationName;
import static org.springframework.cloud.kubernetes.config.ConfigUtils.getApplicationNamespace;
import static org.springframework.cloud.kubernetes.config.PropertySourceUtils.KEY_VALUE_TO_PROPERTIES;
import static org.springframework.cloud.kubernetes.config.PropertySourceUtils.PROPERTIES_TO_MAP;
import static org.springframework.cloud.kubernetes.config.PropertySourceUtils.yamlParserGenerator;
import io.fabric8.kubernetes.api.builder.Function;
import io.fabric8.kubernetes.client.KubernetesClient;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import io.fabric8.kubernetes.api.builder.Function;
import io.fabric8.kubernetes.client.KubernetesClient;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.bootstrap.config.PropertySourceLocator;
import org.springframework.cloud.kubernetes.config.ConfigMapConfigProperties.NormalizedSource;
import org.springframework.core.annotation.Order;
@@ -42,12 +37,25 @@ import org.springframework.core.env.Environment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertySource;
import static org.springframework.cloud.kubernetes.config.ConfigUtils.getApplicationName;
import static org.springframework.cloud.kubernetes.config.ConfigUtils.getApplicationNamespace;
import static org.springframework.cloud.kubernetes.config.PropertySourceUtils.KEY_VALUE_TO_PROPERTIES;
import static org.springframework.cloud.kubernetes.config.PropertySourceUtils.PROPERTIES_TO_MAP;
import static org.springframework.cloud.kubernetes.config.PropertySourceUtils.yamlParserGenerator;
/**
* A {@link PropertySourceLocator} that uses config maps.
*
* @author Ioannis Canellos
*/
@Order(0)
public class ConfigMapPropertySourceLocator implements PropertySourceLocator {
private static final Log LOG = LogFactory.getLog(ConfigMapPropertySourceLocator.class);
private static final Log LOG = LogFactory
.getLog(ConfigMapPropertySourceLocator.class);
private final KubernetesClient client;
private final ConfigMapConfigProperties properties;
public ConfigMapPropertySourceLocator(KubernetesClient client,
@@ -61,11 +69,11 @@ public class ConfigMapPropertySourceLocator implements PropertySourceLocator {
if (environment instanceof ConfigurableEnvironment) {
ConfigurableEnvironment env = (ConfigurableEnvironment) environment;
List<ConfigMapConfigProperties.NormalizedSource> sources = properties
List<ConfigMapConfigProperties.NormalizedSource> sources = this.properties
.determineSources();
CompositePropertySource composite = new CompositePropertySource(
"composite-configmap");
if (properties.isEnableApi()) {
if (this.properties.isEnableApi()) {
sources.forEach(s -> composite.addFirstPropertySource(
getMapPropertySourceForSingleConfigMap(env, s)));
}
@@ -80,71 +88,63 @@ public class ConfigMapPropertySourceLocator implements PropertySourceLocator {
private MapPropertySource getMapPropertySourceForSingleConfigMap(
ConfigurableEnvironment environment, NormalizedSource normalizedSource) {
String configurationTarget = properties.getConfigurationTarget();
return new ConfigMapPropertySource(client,
String configurationTarget = this.properties.getConfigurationTarget();
return new ConfigMapPropertySource(this.client,
getApplicationName(environment, normalizedSource.getName(),
configurationTarget),
getApplicationNamespace(client, normalizedSource.getNamespace(),
getApplicationNamespace(this.client, normalizedSource.getNamespace(),
configurationTarget),
environment.getActiveProfiles());
}
private void addPropertySourcesFromPaths(Environment environment,
CompositePropertySource composite) {
properties
.getPaths()
.stream()
.map(Paths::get)
.peek(p -> {
if(!Files.exists(p)) {
LOG.warn("Configured input path: " + p + " will be ignored because it does not exist on the file system");
CompositePropertySource composite) {
this.properties.getPaths().stream().map(Paths::get).peek(p -> {
if (!Files.exists(p)) {
LOG.warn("Configured input path: " + p
+ " will be ignored because it does not exist on the file system");
}
}).filter(Files::exists).peek(p -> {
if (!Files.isRegularFile(p)) {
LOG.warn("Configured input path: " + p
+ " will be ignored because it is not a regular file");
}
}).filter(Files::isRegularFile).forEach(p -> {
try {
String content = new String(Files.readAllBytes(p)).trim();
String filename = p.getFileName().toString().toLowerCase();
if (filename.endsWith(".properties")) {
addPropertySourceIfNeeded(
c -> PROPERTIES_TO_MAP
.apply(KEY_VALUE_TO_PROPERTIES.apply(c)),
content, filename, composite);
}
})
.filter(Files::exists)
.peek(p -> {
if(!Files.isRegularFile(p)) {
LOG.warn("Configured input path: " + p + " will be ignored because it is not a regular file");
else if (filename.endsWith(".yml") || filename.endsWith(".yaml")) {
addPropertySourceIfNeeded(c -> PROPERTIES_TO_MAP
.apply(yamlParserGenerator(environment.getActiveProfiles())
.apply(c)),
content, filename, composite);
}
})
.filter(Files::isRegularFile)
.forEach(p -> {
try {
String content = new String(Files.readAllBytes(p)).trim();
String filename = p.getFileName().toString().toLowerCase();
if(filename.endsWith(".properties")) {
addPropertySourceIfNeeded(
c -> PROPERTIES_TO_MAP.apply(KEY_VALUE_TO_PROPERTIES.apply(c)),
content,
filename,
composite
);
}
else if(filename.endsWith(".yml") || filename.endsWith(".yaml")) {
addPropertySourceIfNeeded(
c -> PROPERTIES_TO_MAP.apply(
yamlParserGenerator(environment.getActiveProfiles()).apply(c)
),
content,
filename,
composite
);
}
} catch (IOException e) {
LOG.warn("Error reading input file", e);
}
});
}
catch (IOException e) {
LOG.warn("Error reading input file", e);
}
});
}
private void addPropertySourceIfNeeded(Function<String, Map<String, String>> contentToMapFunction,
String content, String name, CompositePropertySource composite) {
private void addPropertySourceIfNeeded(
Function<String, Map<String, String>> contentToMapFunction, String content,
String name, CompositePropertySource composite) {
Map<String, Object> map = new HashMap<>();
map.putAll(contentToMapFunction.apply(content));
if(map.isEmpty()) {
LOG.warn("Property source: " + name + "will be ignored because no properties could be found");
if (map.isEmpty()) {
LOG.warn("Property source: " + name
+ "will be ignored because no properties could be found");
}
else {
composite.addFirstPropertySource(new MapPropertySource(name, map));
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,7 +12,6 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.config;
@@ -27,10 +26,19 @@ import org.springframework.util.StringUtils;
import static org.springframework.cloud.kubernetes.config.Constants.FALLBACK_APPLICATION_NAME;
import static org.springframework.cloud.kubernetes.config.Constants.SPRING_APPLICATION_NAME;
public class ConfigUtils {
/**
* Utility class that works with configuration properties.
*
* @author Ioannis Canellos
*/
public final class ConfigUtils {
private static final Log LOG = LogFactory.getLog(ConfigUtils.class);
private ConfigUtils() {
throw new IllegalStateException("Can't instantiate a utility class");
}
public static <C extends AbstractConfigProperties> String getApplicationName(
Environment env, String configName, String configurationTarget) {
String name = configName;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,15 +12,17 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.config;
final class Constants {
static final String SPRING_APPLICATION_NAME = "spring.application.name";
static final String FALLBACK_APPLICATION_NAME = "application";
static final String PROPERTY_SOURCE_NAME_SEPARATOR = ".";
private Constants() {
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,15 +12,10 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.config;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.env.MapPropertySource;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -28,10 +23,18 @@ import java.nio.file.Paths;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.env.MapPropertySource;
/**
* Secrets and ConfigMaps shared features.
*
* @author Stefan Larsson
*/
public class KubernetesPropertySource extends MapPropertySource {
private static final Log LOG = LogFactory.getLog(KubernetesPropertySource.class);
@SuppressWarnings("unchecked")
@@ -39,31 +42,30 @@ public class KubernetesPropertySource extends MapPropertySource {
super(name, source);
}
protected static void putPathConfig(Map<String, ? super String> result, List<String> paths) {
paths
.stream()
.map(Paths::get)
.filter(Files::exists)
.forEach(p -> putAll(p, result));
protected static void putPathConfig(Map<String, ? super String> result,
List<String> paths) {
paths.stream().map(Paths::get).filter(Files::exists)
.forEach(p -> putAll(p, result));
}
private static void putAll(Path path, Map<String, ? super String> result) {
try {
Files.walk(path)
.filter(Files::isRegularFile)
.forEach(p -> readFile(p, result));
} catch (IOException e) {
Files.walk(path).filter(Files::isRegularFile)
.forEach(p -> readFile(p, result));
}
catch (IOException e) {
LOG.warn("Error walking properties files", e);
}
}
private static void readFile(Path path, Map<String, ? super String> result) {
try {
result.put(
path.getFileName().toString(),
new String(Files.readAllBytes(path)).trim());
} catch (IOException e) {
result.put(path.getFileName().toString(),
new String(Files.readAllBytes(path)).trim());
}
catch (IOException e) {
LOG.warn("Error reading properties file", e);
}
}
}

View File

@@ -1,9 +1,20 @@
package org.springframework.cloud.kubernetes.config;
/*
* 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
*
* http://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.
*/
import static java.util.Arrays.asList;
import static org.springframework.beans.factory.config.YamlProcessor.MatchStatus.ABSTAIN;
import static org.springframework.beans.factory.config.YamlProcessor.MatchStatus.FOUND;
import static org.springframework.beans.factory.config.YamlProcessor.MatchStatus.NOT_FOUND;
package org.springframework.cloud.kubernetes.config;
import java.io.ByteArrayInputStream;
import java.io.IOException;
@@ -11,35 +22,41 @@ import java.util.Map;
import java.util.Properties;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.core.io.ByteArrayResource;
public class PropertySourceUtils {
import static java.util.Arrays.asList;
import static org.springframework.beans.factory.config.YamlProcessor.MatchStatus.ABSTAIN;
import static org.springframework.beans.factory.config.YamlProcessor.MatchStatus.FOUND;
import static org.springframework.beans.factory.config.YamlProcessor.MatchStatus.NOT_FOUND;
static final Function<String, Properties> KEY_VALUE_TO_PROPERTIES =
s -> {
Properties properties = new Properties();
try {
properties.load(new ByteArrayInputStream(s.getBytes()));
return properties;
}
catch (IOException e) {
throw new IllegalArgumentException();
/**
* Utility class to work with property sources.
*
* @author Georgios Andrianakis
*/
public final class PropertySourceUtils {
static final Function<String, Properties> KEY_VALUE_TO_PROPERTIES = s -> {
Properties properties = new Properties();
try {
properties.load(new ByteArrayInputStream(s.getBytes()));
return properties;
}
catch (IOException e) {
throw new IllegalArgumentException();
}
};
static final Function<Properties, Map<String, String>> PROPERTIES_TO_MAP = p -> p
.entrySet().stream().collect(Collectors.toMap(e -> String.valueOf(e.getKey()),
e -> String.valueOf(e.getValue())));
static final Function<Properties, Map<String, String>> PROPERTIES_TO_MAP =
p -> p.entrySet()
.stream()
.collect(
Collectors.toMap(
e -> String.valueOf(e.getKey()),
e -> String.valueOf(e.getValue())
)
);
private PropertySourceUtils() {
throw new IllegalStateException("Can't instantiate a utility class");
}
static Function<String, Properties> yamlParserGenerator(
final String[] profiles) {
static Function<String, Properties> yamlParserGenerator(final String[] profiles) {
return s -> {
YamlPropertiesFactoryBean yamlFactory = new YamlPropertiesFactoryBean();
yamlFactory.setDocumentMatchers(properties -> {
@@ -55,4 +72,5 @@ public class PropertySourceUtils {
return yamlFactory.getObject();
};
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,8 +12,8 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.config;
import java.util.HashMap;
@@ -23,41 +23,49 @@ import java.util.Map;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Properties for configuring Kubernetes secrets.
*
* @author l burgazzoli
*/
@ConfigurationProperties("spring.cloud.kubernetes.secrets")
public class SecretsConfigProperties extends AbstractConfigProperties {
private static final String TARGET = "Secret";
private boolean enableApi = false;
private Map<String, String> labels = new HashMap<>();
private List<String> paths = new LinkedList<>();
public boolean isEnableApi() {
return enableApi;
return this.enableApi;
}
public void setEnableApi(boolean enableApi) {
this.enableApi = enableApi;
}
public Map<String, String> getLabels() {
return this.labels;
}
public void setLabels(Map<String, String> labels) {
this.labels = labels;
}
public Map<String, String> getLabels() {
return labels;
public List<String> getPaths() {
return this.paths;
}
public void setPaths(List<String> paths) {
this.paths = paths;
}
public List<String> getPaths() {
return paths;
}
@Override
public String getConfigurationTarget() {
return TARGET;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,8 +12,8 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.config;
import java.util.Base64;
@@ -31,33 +31,40 @@ import org.springframework.util.StringUtils;
import static org.springframework.cloud.kubernetes.config.ConfigUtils.getApplicationName;
import static org.springframework.cloud.kubernetes.config.ConfigUtils.getApplicationNamespace;
/**
* Kubernetes property source for secrets.
*
* @author l burgazzoli
*/
public class SecretsPropertySource extends KubernetesPropertySource {
private static final Log LOG = LogFactory.getLog(SecretsPropertySource.class);
private static final String PREFIX = "secrets";
public SecretsPropertySource(KubernetesClient client, Environment env,
SecretsConfigProperties config) {
SecretsConfigProperties config) {
super(getSourceName(client, env, config), getSourceData(client, env, config));
}
private static String getSourceName(KubernetesClient client, Environment env,
SecretsConfigProperties config) {
SecretsConfigProperties config) {
return new StringBuilder().append(PREFIX)
.append(Constants.PROPERTY_SOURCE_NAME_SEPARATOR).append(
getApplicationName(env, config.getName(),
config.getConfigurationTarget()))
.append(Constants.PROPERTY_SOURCE_NAME_SEPARATOR).append(
getApplicationNamespace(client, config.getNamespace(),
config.getConfigurationTarget())).toString();
.append(Constants.PROPERTY_SOURCE_NAME_SEPARATOR)
.append(getApplicationName(env, config.getName(),
config.getConfigurationTarget()))
.append(Constants.PROPERTY_SOURCE_NAME_SEPARATOR)
.append(getApplicationNamespace(client, config.getNamespace(),
config.getConfigurationTarget()))
.toString();
}
private static Map<String, Object> getSourceData(KubernetesClient client,
Environment env, SecretsConfigProperties config) {
Environment env, SecretsConfigProperties config) {
String name = getApplicationName(env, config.getName(),
config.getConfigurationTarget());
config.getConfigurationTarget());
String namespace = getApplicationNamespace(client, config.getNamespace(),
config.getConfigurationTarget());
config.getConfigurationTarget());
Map<String, Object> result = new HashMap<>();
if (config.isEnableApi()) {
@@ -76,20 +83,19 @@ public class SecretsPropertySource extends KubernetesPropertySource {
if (!config.getLabels().isEmpty()) {
if (StringUtils.isEmpty(namespace)) {
client.secrets().withLabels(config.getLabels()).list().getItems()
.forEach(s -> putAll(s, result));
.forEach(s -> putAll(s, result));
}
else {
client.secrets().inNamespace(namespace)
.withLabels(config.getLabels()).list().getItems()
.forEach(s -> putAll(s, result));
.withLabels(config.getLabels()).list().getItems()
.forEach(s -> putAll(s, result));
}
}
}
catch (Exception e) {
LOG.warn(
"Can't read secret with name: [" + name + "] or labels [" + config
.getLabels() + "] in namespace:[" + namespace + "] (cause: " + e
.getMessage() + "). Ignoring");
LOG.warn("Can't read secret with name: [" + name + "] or labels ["
+ config.getLabels() + "] in namespace:[" + namespace
+ "] (cause: " + e.getMessage() + "). Ignoring");
}
}
@@ -99,17 +105,19 @@ public class SecretsPropertySource extends KubernetesPropertySource {
return result;
}
@Override public String toString() {
return getClass().getSimpleName() + " {name='" + this.name + "'}";
}
// *****************************
// Helpers
// *****************************
private static void putAll(Secret secret, Map<String, Object> result) {
if (secret != null && secret.getData() != null) {
secret.getData().forEach((k, v) -> result
.put(k, new String(Base64.getDecoder().decode(v)).trim()));
secret.getData().forEach((k, v) -> result.put(k,
new String(Base64.getDecoder().decode(v)).trim()));
}
}
@Override
public String toString() {
return getClass().getSimpleName() + " {name='" + this.name + "'}";
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,7 +12,6 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.config;
@@ -25,9 +24,16 @@ import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MapPropertySource;
/**
* Kubernetes {@link PropertySourceLocator} for secrets.
*
* @author l burgazzoli
*/
@Order(1)
public class SecretsPropertySourceLocator implements PropertySourceLocator {
private final KubernetesClient client;
private final SecretsConfigProperties properties;
public SecretsPropertySourceLocator(KubernetesClient client,
@@ -39,6 +45,8 @@ public class SecretsPropertySourceLocator implements PropertySourceLocator {
@Override
public MapPropertySource locate(Environment environment) {
return environment instanceof ConfigurableEnvironment
? new SecretsPropertySource(client, environment, properties) : null;
? new SecretsPropertySource(this.client, environment, this.properties)
: null;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,8 +12,8 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.config.reload;
import io.fabric8.kubernetes.client.KubernetesClient;
@@ -41,6 +41,8 @@ import org.springframework.scheduling.annotation.EnableScheduling;
/**
* Definition of beans needed for the automatic reload of configuration.
*
* @author Nicolla Ferraro
*/
@Configuration
@ConditionalOnProperty(value = "spring.cloud.kubernetes.enabled", matchIfMissing = true)
@@ -54,7 +56,7 @@ public class ConfigReloadAutoConfiguration {
/**
* Configuration reload must be enabled explicitly.
*/
@ConditionalOnProperty(value = "spring.cloud.kubernetes.reload.enabled")
@ConditionalOnProperty("spring.cloud.kubernetes.reload.enabled")
@ConditionalOnClass({ RestartEndpoint.class, ContextRefresher.class })
@EnableScheduling
@EnableAsync
@@ -73,7 +75,9 @@ public class ConfigReloadAutoConfiguration {
private SecretsPropertySourceLocator secretsPropertySourceLocator;
/**
* Provides a bean that listen to configuration changes and fire a reload.
* @param properties config reload properties
* @param strategy configuration update strategy
* @return a bean that listen to configuration changes and fire a reload.
*/
@Bean
@ConditionalOnMissingBean
@@ -81,20 +85,26 @@ public class ConfigReloadAutoConfiguration {
ConfigReloadProperties properties, ConfigurationUpdateStrategy strategy) {
switch (properties.getMode()) {
case POLLING:
return new PollingConfigurationChangeDetector(environment, properties,
kubernetesClient, strategy, configMapPropertySourceLocator,
secretsPropertySourceLocator);
return new PollingConfigurationChangeDetector(this.environment,
properties, this.kubernetesClient, strategy,
this.configMapPropertySourceLocator,
this.secretsPropertySourceLocator);
case EVENT:
return new EventBasedConfigurationChangeDetector(environment, properties,
kubernetesClient, strategy, configMapPropertySourceLocator,
secretsPropertySourceLocator);
return new EventBasedConfigurationChangeDetector(this.environment,
properties, this.kubernetesClient, strategy,
this.configMapPropertySourceLocator,
this.secretsPropertySourceLocator);
}
throw new IllegalStateException(
"Unsupported configuration reload mode: " + properties.getMode());
}
/**
* Provides the action to execute when the configuration changes.
* @param properties config reload properties
* @param ctx application context
* @param restarter restart endpoint
* @param refresher context refresher
* @return provides the action to execute when the configuration changes.
*/
@Bean
@ConditionalOnMissingBean

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,8 +12,8 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.config.reload;
import java.time.Duration;
@@ -22,6 +22,8 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* General configuration for the configuration reload.
*
* @author Nicola Ferraro
*/
@ConfigurationProperties(prefix = "spring.cloud.kubernetes.reload")
public class ConfigReloadProperties {
@@ -60,7 +62,7 @@ public class ConfigReloadProperties {
}
public boolean isEnabled() {
return enabled;
return this.enabled;
}
public void setEnabled(boolean enabled) {
@@ -68,7 +70,7 @@ public class ConfigReloadProperties {
}
public boolean isMonitoringConfigMaps() {
return monitoringConfigMaps;
return this.monitoringConfigMaps;
}
public void setMonitoringConfigMaps(boolean monitoringConfigMaps) {
@@ -76,7 +78,7 @@ public class ConfigReloadProperties {
}
public boolean isMonitoringSecrets() {
return monitoringSecrets;
return this.monitoringSecrets;
}
public void setMonitoringSecrets(boolean monitoringSecrets) {
@@ -84,7 +86,7 @@ public class ConfigReloadProperties {
}
public ReloadStrategy getStrategy() {
return strategy;
return this.strategy;
}
public void setStrategy(ReloadStrategy strategy) {
@@ -92,22 +94,26 @@ public class ConfigReloadProperties {
}
public ReloadDetectionMode getMode() {
return mode;
}
public void setPeriod(Duration period) {
this.period = period;
}
public Duration getPeriod() {
return period;
return this.mode;
}
public void setMode(ReloadDetectionMode mode) {
this.mode = mode;
}
public Duration getPeriod() {
return this.period;
}
public void setPeriod(Duration period) {
this.period = period;
}
/**
* Reload strategies.
*/
public enum ReloadStrategy {
/**
* Fire a refresh of beans annotated with @ConfigurationProperties
* or @RefreshScope.
@@ -126,9 +132,14 @@ public class ConfigReloadProperties {
* configured to restart the pod.
*/
SHUTDOWN
}
/**
* Reload detection modes.
*/
public enum ReloadDetectionMode {
/**
* Enables a polling task that retrieves periodically all external properties and
* fire a reload when they change.
@@ -140,6 +151,7 @@ public class ConfigReloadProperties {
* or secrets change.
*/
EVENT
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,8 +12,8 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.config.reload;
import java.util.ArrayList;
@@ -38,6 +38,8 @@ import org.springframework.core.env.PropertySource;
/**
* This is the superclass of all beans that can listen to changes in the configuration and
* fire a reload.
*
* @author Nicola Ferraro
*/
public abstract class ConfigurationChangeDetector {
@@ -64,22 +66,27 @@ public abstract class ConfigurationChangeDetector {
public void shutdown() {
// Ensure the kubernetes client is cleaned up from spare threads when shutting
// down
kubernetesClient.close();
this.kubernetesClient.close();
}
public void reloadProperties() {
log.info("Reloading using strategy: " + strategy.getName());
strategy.reload();
this.log.info("Reloading using strategy: " + this.strategy.getName());
this.strategy.reload();
}
/**
* Determines if two property sources are different.
* @param mp1 map property sources 1
* @param mp2 map property sources 2
* @return {@code true} if source has changed
*/
protected boolean changed(MapPropertySource mp1, MapPropertySource mp2) {
if (mp1 == mp2)
if (mp1 == mp2) {
return false;
if (mp1 == null && mp2 != null || mp1 != null && mp2 == null)
}
if (mp1 == null && mp2 != null || mp1 != null && mp2 == null) {
return true;
}
Map<String, Object> s1 = mp1.getSource();
Map<String, Object> s2 = mp2.getSource();
@@ -91,8 +98,9 @@ public abstract class ConfigurationChangeDetector {
List<? extends MapPropertySource> l2) {
if (l1.size() != l2.size()) {
log.debug("The current number of Confimap PropertySources does not match "
+ "the ones loaded from the Kubernetes - No reload will take place");
this.log.debug(
"The current number of Confimap PropertySources does not match "
+ "the ones loaded from the Kubernetes - No reload will take place");
return false;
}
@@ -107,6 +115,9 @@ public abstract class ConfigurationChangeDetector {
/**
* Finds one registered property source of the given type, logging a warning if
* multiple property sources of that type are available.
* @param <S> property source type
* @param sourceClass class for which property sources will be searched for
* @return matched property source
*/
protected <S extends PropertySource<?>> S findPropertySource(Class<S> sourceClass) {
List<S> sources = findPropertySources(sourceClass);
@@ -114,20 +125,22 @@ public abstract class ConfigurationChangeDetector {
return null;
}
if (sources.size() > 1) {
log.warn("Found more than one property source of type " + sourceClass);
this.log.warn("Found more than one property source of type " + sourceClass);
}
return sources.get(0);
}
/**
* Finds all registered property sources of the given type.
* @param <S> property source type
* @param sourceClass class for which property sources will be found
* @return finds all registered property sources of the given type
*/
protected <S extends PropertySource<?>> List<S> findPropertySources(
Class<S> sourceClass) {
List<S> managedSources = new LinkedList<>();
LinkedList<PropertySource<?>> sources = toLinkedList(
environment.getPropertySources());
this.environment.getPropertySources());
while (!sources.isEmpty()) {
PropertySource<?> source = sources.pop();
if (source instanceof CompositePropertySource) {
@@ -152,7 +165,11 @@ public abstract class ConfigurationChangeDetector {
/**
* Returns a list of MapPropertySource that correspond to the current state of the
* system This only handles the PropertySource objects that are returned
* system. This only handles the PropertySource objects that are returned.
* @param propertySourceLocator Spring's property source locator
* @param environment Spring environment
* @return a list of MapPropertySource that correspond to the current state of the
* system
*/
protected List<MapPropertySource> locateMapPropertySources(
PropertySourceLocator propertySourceLocator, Environment environment) {
@@ -168,7 +185,7 @@ public abstract class ConfigurationChangeDetector {
.map(p -> (MapPropertySource) p).collect(Collectors.toList()));
}
else {
log.debug("Found property source that cannot be handled: "
this.log.debug("Found property source that cannot be handled: "
+ propertySource.getClass());
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,8 +12,8 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.config.reload;
import java.util.Objects;
@@ -21,6 +21,8 @@ import java.util.Objects;
/**
* This is the superclass of all named strategies that can be fired when the configuration
* changes.
*
* @author Nicola Ferraro
*/
public class ConfigurationUpdateStrategy {
@@ -36,7 +38,7 @@ public class ConfigurationUpdateStrategy {
}
public String getName() {
return name;
return this.name;
}
public void reload() {
@@ -46,8 +48,9 @@ public class ConfigurationUpdateStrategy {
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("ConfigurationUpdateStrategy{");
sb.append("name='").append(name).append('\'');
sb.append("name='").append(this.name).append('\'');
sb.append('}');
return sb.toString();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,8 +12,8 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.config.reload;
import java.util.HashMap;
@@ -39,6 +39,8 @@ import org.springframework.core.env.MapPropertySource;
/**
* A change detector that subscribes to changes in secrets and configmaps and fire a
* reload when something changes.
*
* @author Nicola Ferraro
*/
public class EventBasedConfigurationChangeDetector extends ConfigurationChangeDetector {
@@ -64,11 +66,11 @@ public class EventBasedConfigurationChangeDetector extends ConfigurationChangeDe
public void watch() {
boolean activated = false;
if (properties.isMonitoringConfigMaps()) {
if (this.properties.isMonitoringConfigMaps()) {
try {
String name = "config-maps-watch";
watches.put(name,
kubernetesClient.configMaps().watch(new Watcher<ConfigMap>() {
this.watches.put(name, this.kubernetesClient.configMaps()
.watch(new Watcher<ConfigMap>() {
@Override
public void eventReceived(Action action,
ConfigMap configMap) {
@@ -80,41 +82,43 @@ public class EventBasedConfigurationChangeDetector extends ConfigurationChangeDe
}
}));
activated = true;
log.info("Added new Kubernetes watch: " + name);
this.log.info("Added new Kubernetes watch: " + name);
}
catch (Exception e) {
log.error(
this.log.error(
"Error while establishing a connection to watch config maps: configuration may remain stale",
e);
}
}
if (properties.isMonitoringSecrets()) {
if (this.properties.isMonitoringSecrets()) {
try {
activated = false;
String name = "secrets-watch";
watches.put(name, kubernetesClient.secrets().watch(new Watcher<Secret>() {
@Override
public void eventReceived(Action action, Secret secret) {
onEvent(secret);
}
this.watches.put(name,
this.kubernetesClient.secrets().watch(new Watcher<Secret>() {
@Override
public void eventReceived(Action action, Secret secret) {
onEvent(secret);
}
@Override
public void onClose(KubernetesClientException e) {
}
}));
@Override
public void onClose(KubernetesClientException e) {
}
}));
activated = true;
log.info("Added new Kubernetes watch: " + name);
this.log.info("Added new Kubernetes watch: " + name);
}
catch (Exception e) {
log.error(
this.log.error(
"Error while establishing a connection to watch secrets: configuration may remain stale",
e);
}
}
if (activated) {
log.info("Kubernetes event-based configuration change detector activated");
this.log.info(
"Kubernetes event-based configuration change detector activated");
}
}
@@ -123,12 +127,12 @@ public class EventBasedConfigurationChangeDetector extends ConfigurationChangeDe
if (this.watches != null) {
for (Map.Entry<String, Watch> entry : this.watches.entrySet()) {
try {
log.debug("Closing the watch " + entry.getKey());
this.log.debug("Closing the watch " + entry.getKey());
entry.getValue().close();
}
catch (Exception e) {
log.error("Error while closing the watch connection", e);
this.log.error("Error while closing the watch connection", e);
}
}
}
@@ -136,10 +140,11 @@ public class EventBasedConfigurationChangeDetector extends ConfigurationChangeDe
private void onEvent(ConfigMap configMap) {
boolean changed = changed(
locateMapPropertySources(configMapPropertySourceLocator, environment),
locateMapPropertySources(this.configMapPropertySourceLocator,
this.environment),
findPropertySources(ConfigMapPropertySource.class));
if (changed) {
log.info("Detected change in config maps");
this.log.info("Detected change in config maps");
reloadProperties();
}
}
@@ -148,10 +153,10 @@ public class EventBasedConfigurationChangeDetector extends ConfigurationChangeDe
MapPropertySource currentSecretSource = findPropertySource(
SecretsPropertySource.class);
if (currentSecretSource != null) {
MapPropertySource newSecretSource = secretsPropertySourceLocator
.locate(environment);
MapPropertySource newSecretSource = this.secretsPropertySourceLocator
.locate(this.environment);
if (changed(currentSecretSource, newSecretSource)) {
log.info("Detected change in secrets");
this.log.info("Detected change in secrets");
reloadProperties();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,8 +12,8 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.config.reload;
import java.util.List;
@@ -35,6 +35,8 @@ import org.springframework.scheduling.annotation.Scheduled;
/**
* A change detector that periodically retrieves secrets and configmaps and fire a reload
* when something changes.
*
* @author Nicola Ferraro
*/
public class PollingConfigurationChangeDetector extends ConfigurationChangeDetector {
@@ -57,32 +59,32 @@ public class PollingConfigurationChangeDetector extends ConfigurationChangeDetec
@PostConstruct
public void init() {
log.info("Kubernetes polling configuration change detector activated");
this.log.info("Kubernetes polling configuration change detector activated");
}
@Scheduled(initialDelayString = "${spring.cloud.kubernetes.reload.period:15000}", fixedDelayString = "${spring.cloud.kubernetes.reload.period:15000}")
public void executeCycle() {
boolean changedConfigMap = false;
if (properties.isMonitoringConfigMaps()) {
if (this.properties.isMonitoringConfigMaps()) {
List<? extends MapPropertySource> currentConfigMapSources = findPropertySources(
ConfigMapPropertySource.class);
if (!currentConfigMapSources.isEmpty()) {
changedConfigMap = changed(
locateMapPropertySources(configMapPropertySourceLocator,
environment),
locateMapPropertySources(this.configMapPropertySourceLocator,
this.environment),
currentConfigMapSources);
}
}
boolean changedSecrets = false;
if (properties.isMonitoringSecrets()) {
if (this.properties.isMonitoringSecrets()) {
MapPropertySource currentSecretSource = findPropertySource(
SecretsPropertySource.class);
if (currentSecretSource != null) {
MapPropertySource newSecretSource = secretsPropertySourceLocator
.locate(environment);
MapPropertySource newSecretSource = this.secretsPropertySourceLocator
.locate(this.environment);
changedSecrets = changed(currentSecretSource, newSecretSource);
}
}

View File

@@ -1,15 +1,17 @@
{"properties": [
{
"name": "spring.cloud.kubernetes.config.enabled",
"type": "java.lang.Boolean",
"description": "Enable the ConfigMap property source locator.",
"defaultValue": true
},
{
"name": "spring.cloud.kubernetes.secrets.enabled",
"type": "java.lang.Boolean",
"description": "Enable the Secrets property source locator.",
"defaultValue": true
}
]}
{
"properties": [
{
"name": "spring.cloud.kubernetes.config.enabled",
"type": "java.lang.Boolean",
"description": "Enable the ConfigMap property source locator.",
"defaultValue": true
},
{
"name": "spring.cloud.kubernetes.secrets.enabled",
"type": "java.lang.Boolean",
"description": "Enable the Secrets property source locator.",
"defaultValue": true
}
]
}

View File

@@ -1,5 +1,4 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.kubernetes.config.reload.ConfigReloadAutoConfiguration
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.kubernetes.config.BootstrapConfiguration

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,27 +12,27 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.config
import io.fabric8.kubernetes.api.model.SecretBuilder
import groovy.util.logging.Slf4j
import io.fabric8.kubernetes.api.model.ConfigMapBuilder
import io.fabric8.kubernetes.api.model.SecretBuilder
import io.fabric8.kubernetes.client.Config
import io.fabric8.kubernetes.client.KubernetesClient
import io.fabric8.kubernetes.client.server.mock.KubernetesMockServer
import spock.lang.Specification
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.core.env.Environment
import org.springframework.test.context.ContextConfiguration
import spock.lang.Specification
import groovy.util.logging.Slf4j
@Slf4j
@ContextConfiguration(classes=[TestApplication.class])
@SpringBootTest(properties=[
@ContextConfiguration(classes = [TestApplication.class])
@SpringBootTest(properties = [
"spring.application.name=testapp",
"spring.cloud.kubernetes.client.namespace=testns",
"spring.cloud.kubernetes.client.trustCerts=true",
@@ -42,84 +42,87 @@ import groovy.util.logging.Slf4j
@EnableConfigurationProperties
class CoreTest extends Specification {
private static KubernetesMockServer mockServer = new KubernetesMockServer()
private static KubernetesClient mockClient
private static KubernetesMockServer mockServer = new KubernetesMockServer()
private static KubernetesClient mockClient
@Autowired
Environment environment
@Autowired
Environment environment
@Autowired(required = false)
Config config
@Autowired(required = false)
Config config
@Autowired(required = false)
KubernetesClient client
@Autowired(required = false)
KubernetesClient client
def setupSpec() {
mockServer.init()
mockClient = mockServer.createClient()
def setupSpec() {
mockServer.init()
mockClient = mockServer.createClient()
mockServer.expect().get()
.withPath("/api/v1/namespaces/testns/configmaps/testapp")
.andReturn(
200,
new ConfigMapBuilder()
.withData([
'spring.kubernetes.test.value': 'value1'])
.build())
.always()
mockServer.expect().get()
.withPath("/api/v1/namespaces/testns/secrets/testapp")
.andReturn(
200,
new SecretBuilder()
.withData([
'amq.pwd': 'MWYyZDFlMmU2N2Rm',
'amq.usr': 'YWRtaW4K'
])
.build())
.always()
mockServer.expect().get()
.withPath("/api/v1/namespaces/testns/configmaps/testapp")
.andReturn(
200,
new ConfigMapBuilder()
.withData([
'spring.kubernetes.test.value': 'value1'])
.build())
.always()
mockServer.expect().get()
.withPath("/api/v1/namespaces/testns/secrets/testapp")
.andReturn(
200,
new SecretBuilder()
.withData([
'amq.pwd': 'MWYyZDFlMmU2N2Rm',
'amq.usr': 'YWRtaW4K'
])
.build())
.always()
//Configure the kubernetes master url to point to the mock server
System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY, mockClient.getConfiguration().getMasterUrl())
System.setProperty(Config.KUBERNETES_TRUST_CERT_SYSTEM_PROPERTY, "true")
System.setProperty(Config.KUBERNETES_AUTH_TRYKUBECONFIG_SYSTEM_PROPERTY, "false")
System.setProperty(Config.KUBERNETES_AUTH_TRYSERVICEACCOUNT_SYSTEM_PROPERTY, "false")
}
//Configure the kubernetes master url to point to the mock server
System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY, mockClient.
getConfiguration().getMasterUrl())
System.setProperty(Config.KUBERNETES_TRUST_CERT_SYSTEM_PROPERTY, "true")
System.setProperty(Config.KUBERNETES_AUTH_TRYKUBECONFIG_SYSTEM_PROPERTY, "false")
System.
setProperty(Config.KUBERNETES_AUTH_TRYSERVICEACCOUNT_SYSTEM_PROPERTY, "false")
}
def cleanupSpec() {
mockServer.destroy();
}
def cleanupSpec() {
mockServer.destroy();
}
def "Kubernetes client config bean should be present"() {
expect:
config != null
}
def "Kubernetes client config bean should be present"() {
expect:
config != null
}
def "Kubernetes client config bean should be configurable via system properties"() {
expect:
config.getMasterUrl().equals(mockClient.getConfiguration().getMasterUrl())
config.getNamespace().equals("testns")
config.trustCerts
}
def "Kubernetes client config bean should be configurable via system properties"() {
expect:
config.getMasterUrl().equals(mockClient.getConfiguration().getMasterUrl())
config.getNamespace().equals("testns")
config.trustCerts
}
def "Kubernetes client bean should be present"() {
expect:
client != null
}
def "Kubernetes client bean should be present"() {
expect:
client != null
}
def "Kubernetes client should be configured from system properties"() {
expect:
client.getConfiguration().getMasterUrl().equals(mockClient.getConfiguration().getMasterUrl())
}
def "Kubernetes client should be configured from system properties"() {
expect:
client.getConfiguration().getMasterUrl().
equals(mockClient.getConfiguration().getMasterUrl())
}
def "properties should be read from config map"() {
expect:
environment.getProperty("spring.kubernetes.test.value").equals("value1")
}
def "properties should be read from config map"() {
expect:
environment.getProperty("spring.kubernetes.test.value").equals("value1")
}
def "properties should be read from secrets"() {
expect:
environment.getProperty("amq.pwd").equals("1f2d1e2e67df")
environment.getProperty("amq.usr").equals('admin');
}
def "properties should be read from secrets"() {
expect:
environment.getProperty("amq.pwd").equals("1f2d1e2e67df")
environment.getProperty("amq.usr").equals('admin');
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,7 +12,6 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.config
@@ -23,7 +22,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication
@SpringBootApplication
class TestApplication {
def main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
def main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,28 +12,27 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.config;
import io.fabric8.kubernetes.client.utils.IOHelpers;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import io.fabric8.kubernetes.client.utils.IOHelpers;
final class ConfigMapTestUtil {
private ConfigMapTestUtil() {
}
static String readResourceFile(String file) {
String resource;
try {
resource = IOHelpers.readFully(
ConfigMapTestUtil.class.getClassLoader().getResourceAsStream(file));
ConfigMapTestUtil.class.getClassLoader().getResourceAsStream(file));
}
catch (IOException e) {
resource = "";
@@ -44,4 +43,5 @@ final class ConfigMapTestUtil {
static void createFileWithContent(String file, String content) throws IOException {
Files.write(Paths.get(file), content.getBytes(), StandardOpenOption.CREATE);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,56 +12,65 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.config;
import static org.assertj.core.util.Lists.newArrayList;
import static org.springframework.cloud.kubernetes.config.ConfigMapTestUtil.createFileWithContent;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.server.mock.KubernetesServer;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.config.example.App;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.assertj.core.util.Lists.newArrayList;
import static org.springframework.cloud.kubernetes.config.ConfigMapTestUtil.createFileWithContent;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = App.class , properties = {
"spring.application.name=configmap-path-example",
"spring.cloud.kubernetes.config.enableApi=false",
"spring.cloud.kubernetes.config.paths="
+ ConfigMapsFromFilePathsSpringBootTest.FIRST_FILE_NAME_FULL_PATH + ","
+ ConfigMapsFromFilePathsSpringBootTest.SECOND_FILE_NAME_FULL_PATH
})
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = App.class, properties = {
"spring.application.name=configmap-path-example",
"spring.cloud.kubernetes.config.enableApi=false",
"spring.cloud.kubernetes.config.paths="
+ ConfigMapsFromFilePathsSpringBootTest.FIRST_FILE_NAME_FULL_PATH + ","
+ ConfigMapsFromFilePathsSpringBootTest.SECOND_FILE_NAME_FULL_PATH })
public class ConfigMapsFromFilePathsSpringBootTest {
protected static final String FILES_ROOT_PATH = "/tmp/scktests";
protected static final String FIRST_FILE_NAME = "application.properties";
protected static final String SECOND_FILE_NAME = "extra.properties";
protected static final String UNUSED_FILE_NAME = "unused.properties";
protected static final String FIRST_FILE_NAME_FULL_PATH = FILES_ROOT_PATH + "/" + FIRST_FILE_NAME;
protected static final String SECOND_FILE_NAME_FULL_PATH = FILES_ROOT_PATH + "/" + SECOND_FILE_NAME;
protected static final String UNUSED_FILE_NAME_FULL_PATH = FILES_ROOT_PATH + "/" + UNUSED_FILE_NAME;
protected static final String FIRST_FILE_NAME_FULL_PATH = FILES_ROOT_PATH + "/"
+ FIRST_FILE_NAME;
protected static final String SECOND_FILE_NAME_FULL_PATH = FILES_ROOT_PATH + "/"
+ SECOND_FILE_NAME;
protected static final String UNUSED_FILE_NAME_FULL_PATH = FILES_ROOT_PATH + "/"
+ UNUSED_FILE_NAME;
@ClassRule
public static KubernetesServer server = new KubernetesServer();
private static KubernetesClient mockClient;
@Autowired
private WebTestClient webClient;
@Autowired
private WebTestClient webClient;
@BeforeClass
public static void setUpBeforeClass() throws IOException {
@@ -77,41 +86,41 @@ public class ConfigMapsFromFilePathsSpringBootTest {
System.setProperty(Config.KUBERNETES_NAMESPACE_SYSTEM_PROPERTY, "test");
Files.createDirectories(Paths.get(FILES_ROOT_PATH));
createFileWithContent(FIRST_FILE_NAME_FULL_PATH, "bean.greeting=Hello from path!");
createFileWithContent(FIRST_FILE_NAME_FULL_PATH,
"bean.greeting=Hello from path!");
createFileWithContent(SECOND_FILE_NAME_FULL_PATH, "bean.farewell=Bye from path!");
createFileWithContent(UNUSED_FILE_NAME_FULL_PATH, "bean.morning=Morning from path!");
createFileWithContent(UNUSED_FILE_NAME_FULL_PATH,
"bean.morning=Morning from path!");
}
@AfterClass
public static void teardownAfterClass() {
newArrayList(
FIRST_FILE_NAME_FULL_PATH,
SECOND_FILE_NAME_FULL_PATH,
SECOND_FILE_NAME_FULL_PATH,
FILES_ROOT_PATH
).forEach(fn -> {
try {
Files.delete(Paths.get(fn));
} catch (IOException ignored) {}
});
newArrayList(FIRST_FILE_NAME_FULL_PATH, SECOND_FILE_NAME_FULL_PATH,
SECOND_FILE_NAME_FULL_PATH, FILES_ROOT_PATH).forEach(fn -> {
try {
Files.delete(Paths.get(fn));
}
catch (IOException ignored) {
}
});
}
@Test
public void greetingInputShouldReturnPropertyFromFirstFile() {
this.webClient.get().uri("/api/greeting").exchange().expectStatus().isOk()
.expectBody().jsonPath("content").isEqualTo("Hello from path!");
this.webClient.get().uri("/api/greeting").exchange().expectStatus().isOk()
.expectBody().jsonPath("content").isEqualTo("Hello from path!");
}
@Test
public void farewellInputShouldReturnPropertyFromSecondFile() {
this.webClient.get().uri("/api/farewell").exchange().expectStatus().isOk()
.expectBody().jsonPath("content").isEqualTo("Bye from path!");
this.webClient.get().uri("/api/farewell").exchange().expectStatus().isOk()
.expectBody().jsonPath("content").isEqualTo("Bye from path!");
}
@Test
public void morningInputShouldReturnDefaultValue() {
this.webClient.get().uri("/api/morning").exchange().expectStatus().isOk()
.expectBody().jsonPath("content").isEqualTo("Good morning, World!");
this.webClient.get().uri("/api/morning").exchange().expectStatus().isOk()
.expectBody().jsonPath("content").isEqualTo("Good morning, World!");
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,44 +12,46 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.config;
import static org.assertj.core.util.Lists.newArrayList;
import static org.springframework.cloud.kubernetes.config.ConfigMapTestUtil.readResourceFile;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import io.fabric8.kubernetes.api.model.ConfigMapBuilder;
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.server.mock.KubernetesServer;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.config.example.App;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.assertj.core.util.Lists.newArrayList;
import static org.springframework.cloud.kubernetes.config.ConfigMapTestUtil.readResourceFile;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = App.class , properties = {
"spring.application.name=" + ConfigMapsMixedSpringBootTest.APPLICATION_NAME,
"spring.cloud.kubernetes.config.enableApi=true",
"spring.cloud.kubernetes.config.paths="
+ ConfigMapsMixedSpringBootTest.FILE_NAME_FULL_PATH
})
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = App.class, properties = {
"spring.application.name=" + ConfigMapsMixedSpringBootTest.APPLICATION_NAME,
"spring.cloud.kubernetes.config.enableApi=true",
"spring.cloud.kubernetes.config.paths="
+ ConfigMapsMixedSpringBootTest.FILE_NAME_FULL_PATH })
public class ConfigMapsMixedSpringBootTest {
protected static final String FILES_ROOT_PATH = "/tmp/scktests";
protected static final String FILE_NAME = "application-path.yaml";
protected static final String FILE_NAME_FULL_PATH = FILES_ROOT_PATH + "/" + FILE_NAME;
protected static final String APPLICATION_NAME = "configmap-mixed-example";
@@ -59,8 +61,8 @@ public class ConfigMapsMixedSpringBootTest {
private static KubernetesClient mockClient;
@Autowired
private WebTestClient webClient;
@Autowired
private WebTestClient webClient;
@BeforeClass
public static void setUpBeforeClass() throws IOException {
@@ -76,44 +78,47 @@ public class ConfigMapsMixedSpringBootTest {
System.setProperty(Config.KUBERNETES_NAMESPACE_SYSTEM_PROPERTY, "test");
Files.createDirectories(Paths.get(FILES_ROOT_PATH));
ConfigMapTestUtil.createFileWithContent(FILE_NAME_FULL_PATH, readResourceFile("application-path.yaml"));
ConfigMapTestUtil.createFileWithContent(FILE_NAME_FULL_PATH,
readResourceFile("application-path.yaml"));
HashMap<String, String> data = new HashMap<>();
data.put("bean.morning", "Buenos Dias ConfigMap, %s");
server.expect().withPath("/api/v1/namespaces/test/configmaps/" + APPLICATION_NAME)
.andReturn(200, new ConfigMapBuilder().withNewMetadata()
.withName(APPLICATION_NAME).endMetadata().addToData(data).build())
.always();
.andReturn(200, new ConfigMapBuilder().withNewMetadata()
.withName(APPLICATION_NAME).endMetadata().addToData(data).build())
.always();
}
@AfterClass
public static void teardownAfterClass() {
newArrayList(
FILE_NAME_FULL_PATH,
FILES_ROOT_PATH
).forEach(fn -> {
newArrayList(FILE_NAME_FULL_PATH, FILES_ROOT_PATH).forEach(fn -> {
try {
Files.delete(Paths.get(fn));
} catch (IOException ignored) {}
}
catch (IOException ignored) {
}
});
}
@Test
public void greetingInputShouldReturnPropertyFromFile() {
this.webClient.get().uri("/api/greeting").exchange().expectStatus().isOk()
.expectBody().jsonPath("content").isEqualTo("Hello ConfigMap, World from path");
this.webClient.get().uri("/api/greeting").exchange().expectStatus().isOk()
.expectBody().jsonPath("content")
.isEqualTo("Hello ConfigMap, World from path");
}
@Test
public void farewellInputShouldReturnPropertyFromFile() {
this.webClient.get().uri("/api/farewell").exchange().expectStatus().isOk()
.expectBody().jsonPath("content").isEqualTo("Bye ConfigMap, World from path");
this.webClient.get().uri("/api/farewell").exchange().expectStatus().isOk()
.expectBody().jsonPath("content")
.isEqualTo("Bye ConfigMap, World from path");
}
@Test
public void morningInputShouldReturnPropertyFromApi() {
this.webClient.get().uri("/api/morning").exchange().expectStatus().isOk()
.expectBody().jsonPath("content").isEqualTo("Buenos Dias ConfigMap, World");
this.webClient.get().uri("/api/morning").exchange().expectStatus().isOk()
.expectBody().jsonPath("content")
.isEqualTo("Buenos Dias ConfigMap, World");
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,21 +12,22 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.config;
import java.util.HashMap;
import io.fabric8.kubernetes.api.model.ConfigMap;
import io.fabric8.kubernetes.api.model.ConfigMapBuilder;
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.server.mock.KubernetesServer;
import java.util.HashMap;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
import org.springframework.boot.test.context.SpringBootTest;
@@ -34,7 +35,7 @@ import org.springframework.cloud.kubernetes.config.example.App;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.junit.Assert.assertEquals;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Charles Moulliard
@@ -46,6 +47,8 @@ import static org.junit.Assert.assertEquals;
@AutoConfigureWebTestClient
public class ConfigMapsSpringBootTest {
private static final String APPLICATION_NAME = "configmap-example";
@ClassRule
public static KubernetesServer server = new KubernetesServer();
@@ -54,8 +57,6 @@ public class ConfigMapsSpringBootTest {
@Autowired(required = false)
private Config config;
private static final String APPLICATION_NAME = "configmap-example";
@Autowired
private WebTestClient webClient;
@@ -82,8 +83,9 @@ public class ConfigMapsSpringBootTest {
@Test
public void testConfig() {
assertEquals(config.getMasterUrl(), mockClient.getConfiguration().getMasterUrl());
assertEquals(config.getNamespace(), mockClient.getNamespace());
assertThat(mockClient.getConfiguration().getMasterUrl())
.isEqualTo(this.config.getMasterUrl());
assertThat(mockClient.getNamespace()).isEqualTo(this.config.getNamespace());
}
@Test
@@ -91,13 +93,13 @@ public class ConfigMapsSpringBootTest {
ConfigMap configmap = mockClient.configMaps().inNamespace("test")
.withName(APPLICATION_NAME).get();
HashMap<String, String> keys = (HashMap<String, String>) configmap.getData();
assertEquals(keys.get("bean.greeting"), "Hello ConfigMap, %s!");
assertThat("Hello ConfigMap, %s!").isEqualTo(keys.get("bean.greeting"));
}
@Test
public void testGreetingEndpoint() {
this.webClient.get().uri("/api/greeting").exchange().expectStatus().isOk()
.expectBody().jsonPath("content").isEqualTo("Hello ConfigMap, World!");
.expectBody().jsonPath("content").isEqualTo("Hello ConfigMap, World!");
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,25 +12,23 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.config;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.springframework.cloud.kubernetes.config.ConfigMapTestUtil.readResourceFile;
import java.util.Map;
import io.fabric8.kubernetes.api.model.ConfigMapBuilder;
import io.fabric8.kubernetes.api.model.ConfigMapList;
import io.fabric8.kubernetes.api.model.ConfigMapListBuilder;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.server.mock.KubernetesServer;
import java.util.Map;
import org.junit.Rule;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.cloud.kubernetes.config.ConfigMapTestUtil.readResourceFile;
/**
* @author Charles Moulliard
*/
@@ -41,40 +39,40 @@ public class ConfigMapsTest {
@Test
public void testConfigMapList() {
server.expect().withPath("/api/v1/namespaces/ns1/configmaps")
this.server.expect().withPath("/api/v1/namespaces/ns1/configmaps")
.andReturn(200, new ConfigMapListBuilder().build()).once();
KubernetesClient client = server.getClient();
KubernetesClient client = this.server.getClient();
ConfigMapList configMapList = client.configMaps().inNamespace("ns1").list();
assertNotNull(configMapList);
assertEquals(0, configMapList.getItems().size());
assertThat(configMapList).isNotNull();
assertThat(configMapList.getItems().size()).isEqualTo(0);
}
@Test
public void testConfigMapGet() {
server.expect().withPath("/api/v1/namespaces/ns2/configmaps")
this.server.expect().withPath("/api/v1/namespaces/ns2/configmaps")
.andReturn(200,
new ConfigMapBuilder().withNewMetadata()
.withName("reload-example").endMetadata()
.addToData("KEY", "123").build())
.once();
KubernetesClient client = server.getClient();
KubernetesClient client = this.server.getClient();
ConfigMapList configMapList = client.configMaps().inNamespace("ns2").list();
assertNotNull(configMapList);
assertEquals(1, configMapList.getAdditionalProperties().size());
assertThat(configMapList).isNotNull();
assertThat(configMapList.getAdditionalProperties().size()).isEqualTo(1);
@SuppressWarnings("unchecked")
Map<String, String> data = (Map<String, String>) configMapList
.getAdditionalProperties().get("data");
assertEquals("123", data.get("KEY"));
assertThat(data.get("KEY")).isEqualTo("123");
}
@Test
public void testConfigMapFromSingleApplicationProperties() {
String configMapName = "app-properties-test";
String namespace = "app-props";
server.expect()
this.server.expect()
.withPath(String.format("/api/v1/namespaces/%s/configmaps/%s", namespace,
configMapName))
.andReturn(200, new ConfigMapBuilder().withNewMetadata()
@@ -85,18 +83,18 @@ public class ConfigMapsTest {
.once();
ConfigMapPropertySource cmps = new ConfigMapPropertySource(
server.getClient().inNamespace(namespace), configMapName);
this.server.getClient().inNamespace(namespace), configMapName);
assertEquals("a", cmps.getProperty("dummy.property.string1"));
assertEquals("1", cmps.getProperty("dummy.property.int1"));
assertEquals("true", cmps.getProperty("dummy.property.bool1"));
assertThat(cmps.getProperty("dummy.property.string1")).isEqualTo("a");
assertThat(cmps.getProperty("dummy.property.int1")).isEqualTo("1");
assertThat(cmps.getProperty("dummy.property.bool1")).isEqualTo("true");
}
@Test
public void testConfigMapFromSingleApplicationYaml() {
String configMapName = "app-yaml-test";
String namespace = "app-props";
server.expect()
this.server.expect()
.withPath(String.format("/api/v1/namespaces/%s/configmaps/%s", namespace,
configMapName))
.andReturn(200,
@@ -108,18 +106,18 @@ public class ConfigMapsTest {
.once();
ConfigMapPropertySource cmps = new ConfigMapPropertySource(
server.getClient().inNamespace(namespace), configMapName);
this.server.getClient().inNamespace(namespace), configMapName);
assertEquals("a", cmps.getProperty("dummy.property.string2"));
assertEquals("1", cmps.getProperty("dummy.property.int2"));
assertEquals("true", cmps.getProperty("dummy.property.bool2"));
assertThat(cmps.getProperty("dummy.property.string2")).isEqualTo("a");
assertThat(cmps.getProperty("dummy.property.int2")).isEqualTo("1");
assertThat(cmps.getProperty("dummy.property.bool2")).isEqualTo("true");
}
@Test
public void testConfigMapFromSingleNonStandardFileName() {
String configMapName = "single-non-standard-test";
String namespace = "app-props";
server.expect()
this.server.expect()
.withPath(String.format("/api/v1/namespaces/%s/configmaps/%s", namespace,
configMapName))
.andReturn(200, new ConfigMapBuilder().withNewMetadata()
@@ -128,18 +126,18 @@ public class ConfigMapsTest {
.once();
ConfigMapPropertySource cmps = new ConfigMapPropertySource(
server.getClient().inNamespace(namespace), configMapName);
this.server.getClient().inNamespace(namespace), configMapName);
assertEquals("a", cmps.getProperty("dummy.property.string3"));
assertEquals("1", cmps.getProperty("dummy.property.int3"));
assertEquals("true", cmps.getProperty("dummy.property.bool3"));
assertThat(cmps.getProperty("dummy.property.string3")).isEqualTo("a");
assertThat(cmps.getProperty("dummy.property.int3")).isEqualTo("1");
assertThat(cmps.getProperty("dummy.property.bool3")).isEqualTo("true");
}
@Test
public void testConfigMapFromSingleInvalidPropertiesContent() {
String configMapName = "single-unparseable-properties-test";
String namespace = "app-props";
server.expect()
this.server.expect()
.withPath(String.format("/api/v1/namespaces/%s/configmaps/%s", namespace,
configMapName))
.andReturn(200,
@@ -148,7 +146,7 @@ public class ConfigMapsTest {
.addToData("application.properties", "somevalue").build())
.once();
new ConfigMapPropertySource(server.getClient().inNamespace(namespace),
new ConfigMapPropertySource(this.server.getClient().inNamespace(namespace),
configMapName);
// no exception is thrown for unparseable content
@@ -158,7 +156,7 @@ public class ConfigMapsTest {
public void testConfigMapFromSingleInvalidYamlContent() {
String configMapName = "single-unparseable-yaml-test";
String namespace = "app-props";
server.expect()
this.server.expect()
.withPath(String.format("/api/v1/namespaces/%s/configmaps/%s", namespace,
configMapName))
.andReturn(200,
@@ -167,7 +165,7 @@ public class ConfigMapsTest {
.build())
.once();
new ConfigMapPropertySource(server.getClient().inNamespace(namespace),
new ConfigMapPropertySource(this.server.getClient().inNamespace(namespace),
configMapName);
// no exception is thrown for unparseable content
@@ -177,7 +175,7 @@ public class ConfigMapsTest {
public void testConfigMapFromMultipleApplicationProperties() {
String configMapName = "app-multiple-properties-test";
String namespace = "app-props";
server.expect()
this.server.expect()
.withPath(String.format("/api/v1/namespaces/%s/configmaps/%s", namespace,
configMapName))
.andReturn(200,
@@ -191,17 +189,17 @@ public class ConfigMapsTest {
.once();
ConfigMapPropertySource cmps = new ConfigMapPropertySource(
server.getClient().inNamespace(namespace), configMapName);
this.server.getClient().inNamespace(namespace), configMapName);
// application.properties should be read correctly
assertEquals("a", cmps.getProperty("dummy.property.string1"));
assertEquals("1", cmps.getProperty("dummy.property.int1"));
assertEquals("true", cmps.getProperty("dummy.property.bool1"));
assertThat(cmps.getProperty("dummy.property.string1")).isEqualTo("a");
assertThat(cmps.getProperty("dummy.property.int1")).isEqualTo("1");
assertThat(cmps.getProperty("dummy.property.bool1")).isEqualTo("true");
// the adhoc.properties file should not be parsed
assertNull(cmps.getProperty("dummy.property.bool2"));
assertNull(cmps.getProperty("dummy.property.bool2"));
assertNull(cmps.getProperty("dummy.property.bool2"));
assertThat(cmps.getProperty("dummy.property.bool2")).isNull();
assertThat(cmps.getProperty("dummy.property.bool2")).isNull();
assertThat(cmps.getProperty("dummy.property.bool2")).isNull();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,20 +12,21 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.config;
import java.util.HashMap;
import io.fabric8.kubernetes.api.model.ConfigMapBuilder;
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.server.mock.KubernetesServer;
import java.util.HashMap;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
import org.springframework.boot.test.context.SpringBootTest;
@@ -45,13 +46,13 @@ import static org.springframework.cloud.kubernetes.config.ConfigMapTestUtil.read
@AutoConfigureWebTestClient
public class ConfigMapsWithProfilesNoActiveProfileSpringBootTest {
private static final String APPLICATION_NAME = "configmap-with-profile-no-active-profiles-example";
@ClassRule
public static KubernetesServer server = new KubernetesServer();
private static KubernetesClient mockClient;
private static final String APPLICATION_NAME = "configmap-with-profile-no-active-profiles-example";
@Autowired
private WebTestClient webClient;
@@ -79,14 +80,15 @@ public class ConfigMapsWithProfilesNoActiveProfileSpringBootTest {
@Test
public void testGreetingEndpoint() {
this.webClient.get().uri("/api/greeting").exchange().expectStatus().isOk()
.expectBody().jsonPath("content").isEqualTo("Hello ConfigMap default, World!");
.expectBody().jsonPath("content")
.isEqualTo("Hello ConfigMap default, World!");
}
@Test
public void testFarewellEndpoint() {
this.webClient.get().uri("/api/farewell").exchange().expectStatus().isOk()
.expectBody().jsonPath("content")
.isEqualTo("Goodbye ConfigMap default, World!");
.expectBody().jsonPath("content")
.isEqualTo("Goodbye ConfigMap default, World!");
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,20 +12,21 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.config;
import java.util.HashMap;
import io.fabric8.kubernetes.api.model.ConfigMapBuilder;
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.server.mock.KubernetesServer;
import java.util.HashMap;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
import org.springframework.boot.test.context.SpringBootTest;
@@ -47,6 +48,8 @@ import static org.springframework.cloud.kubernetes.config.ConfigMapTestUtil.read
@AutoConfigureWebTestClient
public class ConfigMapsWithProfilesSpringBootTest {
private static final String APPLICATION_NAME = "configmap-with-profile-example";
@ClassRule
public static KubernetesServer server = new KubernetesServer();
@@ -55,8 +58,6 @@ public class ConfigMapsWithProfilesSpringBootTest {
@Autowired(required = false)
Config config;
private static final String APPLICATION_NAME = "configmap-with-profile-example";
@Autowired
private WebTestClient webClient;
@@ -84,14 +85,15 @@ public class ConfigMapsWithProfilesSpringBootTest {
@Test
public void testGreetingEndpoint() {
this.webClient.get().uri("/api/greeting").exchange().expectStatus().isOk()
.expectBody().jsonPath("content")
.isEqualTo("Hello ConfigMap dev, World!");
.expectBody().jsonPath("content")
.isEqualTo("Hello ConfigMap dev, World!");
}
@Test
public void testFarewellEndpoint() {
this.webClient.get().uri("/api/farewell").exchange().expectStatus().isOk()
.expectBody().jsonPath("content")
.isEqualTo("Goodbye ConfigMap default, World!");
.expectBody().jsonPath("content")
.isEqualTo("Goodbye ConfigMap default, World!");
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,20 +12,21 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.config;
import java.util.HashMap;
import io.fabric8.kubernetes.api.model.ConfigMapBuilder;
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.server.mock.KubernetesServer;
import java.util.HashMap;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
import org.springframework.boot.test.context.SpringBootTest;
@@ -37,22 +38,20 @@ import org.springframework.test.web.reactive.server.WebTestClient;
import static org.springframework.cloud.kubernetes.config.ConfigMapTestUtil.readResourceFile;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
classes = App.class,
properties = { "spring.application.name=configmap-without-profile-example",
"spring.cloud.kubernetes.reload.enabled=false"}
)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = App.class, properties = {
"spring.application.name=configmap-without-profile-example",
"spring.cloud.kubernetes.reload.enabled=false" })
@ActiveProfiles("development")
@AutoConfigureWebTestClient
public class ConfigMapsWithoutProfilesSpringBootTest {
private static final String APPLICATION_NAME = "configmap-without-profile-example";
@ClassRule
public static KubernetesServer server = new KubernetesServer();
private static KubernetesClient mockClient;
private static final String APPLICATION_NAME = "configmap-without-profile-example";
@Autowired
private WebTestClient webClient;
@@ -60,32 +59,34 @@ public class ConfigMapsWithoutProfilesSpringBootTest {
public static void setUpBeforeClass() {
mockClient = server.getClient();
//Configure the kubernetes master url to point to the mock server
System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY, mockClient.getConfiguration().getMasterUrl());
// Configure the kubernetes master url to point to the mock server
System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY,
mockClient.getConfiguration().getMasterUrl());
System.setProperty(Config.KUBERNETES_TRUST_CERT_SYSTEM_PROPERTY, "true");
System.setProperty(Config.KUBERNETES_AUTH_TRYKUBECONFIG_SYSTEM_PROPERTY, "false");
System.setProperty(Config.KUBERNETES_AUTH_TRYSERVICEACCOUNT_SYSTEM_PROPERTY, "false");
System.setProperty(Config.KUBERNETES_AUTH_TRYSERVICEACCOUNT_SYSTEM_PROPERTY,
"false");
System.setProperty(Config.KUBERNETES_NAMESPACE_SYSTEM_PROPERTY, "test");
HashMap<String,String> data = new HashMap<>();
data.put("application.yml", readResourceFile("application-without-profiles.yaml"));
server.expect().withPath("/api/v1/namespaces/test/configmaps/" + APPLICATION_NAME).andReturn(200, new ConfigMapBuilder()
.withNewMetadata().withName(APPLICATION_NAME).endMetadata()
.addToData(data)
.build())
.always();
HashMap<String, String> data = new HashMap<>();
data.put("application.yml",
readResourceFile("application-without-profiles.yaml"));
server.expect().withPath("/api/v1/namespaces/test/configmaps/" + APPLICATION_NAME)
.andReturn(200, new ConfigMapBuilder().withNewMetadata()
.withName(APPLICATION_NAME).endMetadata().addToData(data).build())
.always();
}
@Test
public void testGreetingEndpoint() {
this.webClient.get().uri("/api/greeting").exchange().expectStatus().isOk()
.expectBody().jsonPath("content").isEqualTo("Hello ConfigMap, World!");
.expectBody().jsonPath("content").isEqualTo("Hello ConfigMap, World!");
}
@Test
public void testFarewellEndpoint() {
this.webClient.get().uri("/api/farewell").exchange().expectStatus().isOk()
.expectBody().jsonPath("content")
.isEqualTo("Goodbye ConfigMap, World!");
.expectBody().jsonPath("content").isEqualTo("Goodbye ConfigMap, World!");
}
}

View File

@@ -1,3 +1,19 @@
/*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config;
import io.fabric8.kubernetes.client.Config;
@@ -19,10 +35,8 @@ import org.springframework.test.web.reactive.server.WebTestClient;
import static org.hamcrest.Matchers.containsString;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = App.class,
properties = {
"management.endpoint.health.show-details=always"
})
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = App.class, properties = {
"management.endpoint.health.show-details=always" })
public class HealthIndicatorTest {
@ClassRule
@@ -40,23 +54,21 @@ public class HealthIndicatorTest {
public static void setUpBeforeClass() {
mockClient = server.getClient();
//Configure the kubernetes master url to point to the mock server
System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY, mockClient.getConfiguration().getMasterUrl());
// Configure the kubernetes master url to point to the mock server
System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY,
mockClient.getConfiguration().getMasterUrl());
System.setProperty(Config.KUBERNETES_TRUST_CERT_SYSTEM_PROPERTY, "true");
System.setProperty(Config.KUBERNETES_AUTH_TRYKUBECONFIG_SYSTEM_PROPERTY, "false");
System.setProperty(Config.KUBERNETES_AUTH_TRYSERVICEACCOUNT_SYSTEM_PROPERTY, "false");
System.setProperty(Config.KUBERNETES_AUTH_TRYSERVICEACCOUNT_SYSTEM_PROPERTY,
"false");
System.setProperty(Config.KUBERNETES_NAMESPACE_SYSTEM_PROPERTY, "test");
}
@Test
public void healthEndpointShouldContainKubernetes() {
webClient.get()
.uri("http://localhost:{port}/actuator/health", port)
.accept(MediaType.APPLICATION_JSON)
.exchange()
.expectStatus().isOk()
.expectBody(String.class).value(containsString("kubernetes"));
this.webClient.get().uri("http://localhost:{port}/actuator/health", this.port)
.accept(MediaType.APPLICATION_JSON).exchange().expectStatus().isOk()
.expectBody(String.class).value(containsString("kubernetes"));
}
}

View File

@@ -1,16 +1,32 @@
/*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config;
import io.fabric8.kubernetes.client.KubernetesClient;
import org.junit.After;
import org.junit.Test;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
/**
@@ -27,38 +43,36 @@ public class KubernetesConfigConfigurationTest {
}
}
@Test
public void kubernetesWhenKubernetesDisabled() throws Exception {
setup("spring.cloud.kubernetes.enabled=false");
assertFalse(context.containsBean("configMapPropertySourceLocator"));
assertFalse(context.containsBean("secretsPropertySourceLocator"));
assertThat(this.context.containsBean("configMapPropertySourceLocator")).isFalse();
assertThat(this.context.containsBean("secretsPropertySourceLocator")).isFalse();
}
@Test
public void kubernetesWhenKubernetesConfigDisabled() throws Exception {
setup("spring.cloud.kubernetes.config.enabled=false",
"spring.cloud.kubernetes.secrets.enabled=false");
assertFalse(context.containsBean("configMapPropertySourceLocator"));
assertFalse(context.containsBean("secretsPropertySourceLocator"));
"spring.cloud.kubernetes.secrets.enabled=false");
assertThat(this.context.containsBean("configMapPropertySourceLocator")).isFalse();
assertThat(this.context.containsBean("secretsPropertySourceLocator")).isFalse();
}
@Test
public void kubernetesDefaultEnabled() throws Exception {
setup("spring.cloud.kubernetes.enabled=true");
assertTrue(context.containsBean("configMapPropertySourceLocator"));
assertTrue(context.containsBean("secretsPropertySourceLocator"));
assertThat(this.context.containsBean("configMapPropertySourceLocator")).isTrue();
assertThat(this.context.containsBean("secretsPropertySourceLocator")).isTrue();
}
private void setup(String... env) {
this.context = new SpringApplicationBuilder(
PropertyPlaceholderAutoConfiguration.class,
KubernetesClientTestConfiguration.class,
BootstrapConfiguration.class).web(org.springframework.boot.WebApplicationType.NONE)
.properties(env).run();
PropertyPlaceholderAutoConfiguration.class,
KubernetesClientTestConfiguration.class, BootstrapConfiguration.class)
.web(org.springframework.boot.WebApplicationType.NONE)
.properties(env).run();
}
@Configuration
static class KubernetesClientTestConfiguration {
@@ -67,6 +81,6 @@ public class KubernetesConfigConfigurationTest {
return mock(KubernetesClient.class);
}
}
}

View File

@@ -1,10 +1,25 @@
package org.springframework.cloud.kubernetes.config;
/*
* 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
*
* http://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.
*/
import static org.assertj.core.api.Assertions.assertThat;
package org.springframework.cloud.kubernetes.config;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
@@ -14,28 +29,33 @@ import org.springframework.cloud.test.ModifiedClassPathRunner;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;
import static org.assertj.core.api.Assertions.assertThat;
// inspired by spring-cloud-commons: RefreshAutoConfigurationMoreClassPathTests
@RunWith(ModifiedClassPathRunner.class)
@ClassPathExclusions({"spring-boot-actuator-autoconfigure-*.jar", "spring-boot-starter-actuator-*.jar"})
@ClassPathExclusions({ "spring-boot-actuator-autoconfigure-*.jar",
"spring-boot-starter-actuator-*.jar" })
public class MissingActuatorTest {
@Rule
public OutputCapture outputCapture = new OutputCapture();
@Test
public void unknownClassProtected() {
try (ConfigurableApplicationContext context = getApplicationContext(
Config.class, "debug=true")) {
String output = this.outputCapture.toString();
assertThat(output).doesNotContain("Failed to introspect annotations on [class org.springframework.cloud.autoconfigure.RefreshEndpointAutoConfiguration")
.doesNotContain("TypeNotPresentExceptionProxy");
}
private static ConfigurableApplicationContext getApplicationContext(
Class<?> configuration, String... properties) {
return new SpringApplicationBuilder(configuration).web(WebApplicationType.NONE)
.properties(properties).run();
}
private static ConfigurableApplicationContext getApplicationContext(
Class<?> configuration, String... properties) {
return new SpringApplicationBuilder(configuration).web(WebApplicationType.NONE).properties(properties).run();
@Test
public void unknownClassProtected() {
try (ConfigurableApplicationContext context = getApplicationContext(Config.class,
"debug=true")) {
String output = this.outputCapture.toString();
assertThat(output).doesNotContain("Failed to introspect annotations on"
+ " [class org.springframework.cloud.autoconfigure.RefreshEndpointAutoConfiguration")
.doesNotContain("TypeNotPresentExceptionProxy");
}
}
@Configuration

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,21 +12,22 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.config;
import java.util.HashMap;
import java.util.Map;
import io.fabric8.kubernetes.api.model.ConfigMapBuilder;
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.server.mock.KubernetesServer;
import java.util.HashMap;
import java.util.Map;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
import org.springframework.boot.test.context.SpringBootTest;
@@ -38,8 +39,8 @@ import org.springframework.test.web.reactive.server.WebTestClient;
* @author Charles Moulliard
*/
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = ExampleApp.class,
properties = {"spring.cloud.bootstrap.name=multiplecms"})
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = ExampleApp.class, properties = {
"spring.cloud.bootstrap.name=multiplecms" })
@AutoConfigureWebTestClient
public class MultipleConfigMapsSpringBootTest {
@@ -55,59 +56,51 @@ public class MultipleConfigMapsSpringBootTest {
public static void setUpBeforeClass() {
mockClient = server.getClient();
//Configure the kubernetes master url to point to the mock server
System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY, mockClient.getConfiguration().getMasterUrl());
// Configure the kubernetes master url to point to the mock server
System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY,
mockClient.getConfiguration().getMasterUrl());
System.setProperty(Config.KUBERNETES_TRUST_CERT_SYSTEM_PROPERTY, "true");
System.setProperty(Config.KUBERNETES_AUTH_TRYKUBECONFIG_SYSTEM_PROPERTY, "false");
System.setProperty(Config.KUBERNETES_AUTH_TRYSERVICEACCOUNT_SYSTEM_PROPERTY, "false");
System.setProperty(Config.KUBERNETES_AUTH_TRYSERVICEACCOUNT_SYSTEM_PROPERTY,
"false");
System.setProperty(Config.KUBERNETES_NAMESPACE_SYSTEM_PROPERTY, "test");
createConfigmap(
server,
"s1",
"defnamespace",
new HashMap<String, String>() {{
put("bean.common-message","c1");
createConfigmap(server, "s1", "defnamespace", new HashMap<String, String>() {
{
put("bean.common-message", "c1");
put("bean.message1", "m1");
}});
}
});
createConfigmap(
server,
"defname",
"s2",
new HashMap<String, String>() {{
put("bean.common-message","c2");
createConfigmap(server, "defname", "s2", new HashMap<String, String>() {
{
put("bean.common-message", "c2");
put("bean.message2", "m2");
}});
}
});
createConfigmap(
server,
"othername",
"othernamespace",
new HashMap<String, String>() {{
put("bean.common-message","c3");
put("bean.message3", "m3");
}});
createConfigmap(server, "othername", "othernamespace",
new HashMap<String, String>() {
{
put("bean.common-message", "c3");
put("bean.message3", "m3");
}
});
}
private static void createConfigmap(KubernetesServer server, String configMapName,
String namespace, Map<String, String> data) {
String namespace, Map<String, String> data) {
server
.expect()
.withPath(String.format("/api/v1/namespaces/%s/configmaps/%s", namespace, configMapName))
.andReturn(
200,
new ConfigMapBuilder()
.withNewMetadata().withName(configMapName).endMetadata()
.addToData(data)
.build()
)
.always();
server.expect()
.withPath(String.format("/api/v1/namespaces/%s/configmaps/%s", namespace,
configMapName))
.andReturn(200, new ConfigMapBuilder().withNewMetadata()
.withName(configMapName).endMetadata().addToData(data).build())
.always();
}
//the last confimap defined in 'multiplecms.yml' has the highest priority, so
//the common property defined in all configmaps is taken from the last one defined
// the last confimap defined in 'multiplecms.yml' has the highest priority, so
// the common property defined in all configmaps is taken from the last one defined
@Test
public void testCommonMessage() {
assertResponse("/common", "c3");
@@ -129,9 +122,8 @@ public class MultipleConfigMapsSpringBootTest {
}
private void assertResponse(String path, String expectedMessage) {
this.webClient.get().uri(path).exchange().expectStatus().isOk()
.expectBody().jsonPath("message")
.isEqualTo(expectedMessage);
this.webClient.get().uri(path).exchange().expectStatus().isOk().expectBody()
.jsonPath("message").isEqualTo(expectedMessage);
}
}

View File

@@ -1,5 +1,23 @@
/*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config;
import java.util.Base64;
import io.fabric8.kubernetes.api.model.Secret;
import io.fabric8.kubernetes.api.model.SecretBuilder;
import io.fabric8.kubernetes.client.Config;
@@ -9,6 +27,7 @@ import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.config.example.App;
@@ -16,43 +35,53 @@ import org.springframework.core.env.Environment;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.Base64;
import static java.util.Collections.singletonMap;
import static org.assertj.core.api.Assertions.assertThat;
@RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = App.class) @TestPropertySource("classpath:/application-secrets.properties") public class SecretsPropertySourceTest {
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = App.class)
@TestPropertySource("classpath:/application-secrets.properties")
public class SecretsPropertySourceTest {
private static final String NAMESPACE = "test";
private static final String SECRET_VALUE = "secretValue";
@ClassRule public static KubernetesServer server = new KubernetesServer(false, true);
@ClassRule
public static KubernetesServer server = new KubernetesServer(false, true);
@Autowired private SecretsPropertySourceLocator propertySourceLocator;
@Autowired private Environment environment;
@Autowired
private SecretsPropertySourceLocator propertySourceLocator;
@BeforeClass public static void setUpBeforeClass() {
@Autowired
private Environment environment;
@BeforeClass
public static void setUpBeforeClass() {
KubernetesClient mockClient = server.getClient();
// Configure the kubernetes master url to point to the mock server
System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY,
mockClient.getConfiguration().getMasterUrl());
mockClient.getConfiguration().getMasterUrl());
System.setProperty(Config.KUBERNETES_TRUST_CERT_SYSTEM_PROPERTY, "true");
System.setProperty(Config.KUBERNETES_AUTH_TRYKUBECONFIG_SYSTEM_PROPERTY, "false");
System.setProperty(Config.KUBERNETES_AUTH_TRYSERVICEACCOUNT_SYSTEM_PROPERTY,
"false");
"false");
System.setProperty(Config.KUBERNETES_NAMESPACE_SYSTEM_PROPERTY, NAMESPACE);
Secret secret = new SecretBuilder().withNewMetadata()
.withLabels(singletonMap("foo", "bar")).endMetadata()
.addToData("secretName", Base64.getEncoder().encodeToString(SECRET_VALUE.getBytes()))
.build();
.withLabels(singletonMap("foo", "bar")).endMetadata()
.addToData("secretName",
Base64.getEncoder().encodeToString(SECRET_VALUE.getBytes()))
.build();
mockClient.secrets().inNamespace(NAMESPACE).create(secret);
}
@Test public void toStringShouldNotExposeSecretValues() {
String actual = propertySourceLocator.locate(environment).toString();
@Test
public void toStringShouldNotExposeSecretValues() {
String actual = this.propertySourceLocator.locate(this.environment).toString();
assertThat(actual).doesNotContain(SECRET_VALUE);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,7 +12,6 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.config.example;
@@ -25,7 +24,9 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
*/
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,7 +12,6 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.config.example;
@@ -36,17 +35,21 @@ public class GreetingController {
}
@RequestMapping("/api/greeting")
public ResponseMessage greeting(@RequestParam(value="name", defaultValue="World") String name) {
return new ResponseMessage(String.format(properties.getGreeting(), name));
public ResponseMessage greeting(
@RequestParam(value = "name", defaultValue = "World") String name) {
return new ResponseMessage(String.format(this.properties.getGreeting(), name));
}
@RequestMapping("/api/farewell")
public ResponseMessage farewell(@RequestParam(value="name", defaultValue="World") String name) {
return new ResponseMessage(String.format(properties.getFarewell(), name));
public ResponseMessage farewell(
@RequestParam(value = "name", defaultValue = "World") String name) {
return new ResponseMessage(String.format(this.properties.getFarewell(), name));
}
@RequestMapping("/api/morning")
public ResponseMessage morning(@RequestParam(value="name", defaultValue="World") String name) {
return new ResponseMessage(String.format(properties.getMorning(), name));
public ResponseMessage morning(
@RequestParam(value = "name", defaultValue = "World") String name) {
return new ResponseMessage(String.format(this.properties.getMorning(), name));
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,7 +12,6 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.config.example;
@@ -25,11 +24,13 @@ import org.springframework.context.annotation.Configuration;
public class GreetingProperties {
private String greeting = "Hello, %s!";
private String farewell = "Goodbye, %s!";
private String morning = "Good morning, %s!";
public String getGreeting() {
return greeting;
return this.greeting;
}
public void setGreeting(String greeting) {
@@ -37,7 +38,7 @@ public class GreetingProperties {
}
public String getFarewell() {
return farewell;
return this.farewell;
}
public void setFarewell(String farewell) {
@@ -45,10 +46,11 @@ public class GreetingProperties {
}
public String getMorning() {
return morning;
return this.morning;
}
public void setMorning(String morning) {
this.morning = morning;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,7 +12,6 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.config.example;
@@ -33,6 +32,7 @@ public class ResponseMessage {
}
public String getContent() {
return content;
return this.content;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,7 +12,6 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.config.example2;
@@ -28,7 +27,8 @@ import org.springframework.web.bind.annotation.RestController;
public class ExampleApp {
public static void main(String[] args) {
SpringApplication.run(org.springframework.cloud.kubernetes.config.example.App.class, args);
SpringApplication
.run(org.springframework.cloud.kubernetes.config.example.App.class, args);
}
@RestController
@@ -42,27 +42,28 @@ public class ExampleApp {
@GetMapping("/common")
public Response commonMessage() {
return new Response(exampleAppProps.getCommonMessage());
return new Response(this.exampleAppProps.getCommonMessage());
}
@GetMapping("/m1")
public Response message1() {
return new Response(exampleAppProps.getMessage1());
return new Response(this.exampleAppProps.getMessage1());
}
@GetMapping("/m2")
public Response message2() {
return new Response(exampleAppProps.getMessage2());
return new Response(this.exampleAppProps.getMessage2());
}
@GetMapping("/m3")
public Response message3() {
return new Response(exampleAppProps.getMessage3());
return new Response(this.exampleAppProps.getMessage3());
}
}
public static class Response {
private final String message;
public Response(String message) {
@@ -70,8 +71,9 @@ public class ExampleApp {
}
public String getMessage() {
return message;
return this.message;
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,7 +12,6 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.config.example2;
@@ -23,12 +22,15 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
public class ExampleAppProps {
private String commonMessage;
private String message1;
private String message2;
private String message3;
public String getCommonMessage() {
return commonMessage;
return this.commonMessage;
}
public void setCommonMessage(String commonMessage) {
@@ -36,7 +38,7 @@ public class ExampleAppProps {
}
public String getMessage1() {
return message1;
return this.message1;
}
public void setMessage1(String message1) {
@@ -44,7 +46,7 @@ public class ExampleAppProps {
}
public String getMessage2() {
return message2;
return this.message2;
}
public void setMessage2(String message2) {
@@ -52,10 +54,11 @@ public class ExampleAppProps {
}
public String getMessage3() {
return message3;
return this.message3;
}
public void setMessage3(String message3) {
this.message3 = message3;
}
}

View File

@@ -1,3 +1,3 @@
dummy.property.string1=a
dummy.property.int1=1
dummy.property.bool1=true
dummy.property.bool1=true

View File

@@ -2,4 +2,4 @@ dummy:
property:
string2: "a"
int2: 1
bool2: true
bool2: true

View File

@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/base.xml" />
<logger name="org.hibernate.validator" level="info" /> <!-- Validator prints a lot of debug messages during integration tests -->
<logger name="okhttp3.mockwebserver" level="debug" />
<include resource="org/springframework/boot/logging/logback/base.xml"/>
<logger name="org.hibernate.validator"
level="info"/> <!-- Validator prints a lot of debug messages during integration tests -->
<logger name="okhttp3.mockwebserver" level="debug"/>
</configuration>

View File

@@ -10,7 +10,7 @@ spring:
name: defname
namespace: defnamespace
sources:
- name: s1
- namespace: s2
- name: othername
namespace: othernamespace
- name: s1
- namespace: s2
- name: othername
namespace: othernamespace

View File

@@ -16,8 +16,8 @@
~
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springframework.cloud</groupId>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,20 +12,19 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes;
import java.time.Duration;
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.ConfigBuilder;
import io.fabric8.kubernetes.client.DefaultKubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClient;
import java.time.Duration;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@@ -34,6 +33,11 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Auto configuration for Kubernetes.
*
* @author Ioannis Canellos
*/
@Configuration
@ConditionalOnProperty(value = "spring.cloud.kubernetes.enabled", matchIfMissing = true)
@EnableConfigurationProperties(KubernetesClientProperties.class)
@@ -41,6 +45,33 @@ public class KubernetesAutoConfiguration {
private static final Log LOG = LogFactory.getLog(KubernetesAutoConfiguration.class);
private static <D> D or(D dis, D dat) {
if (dis != null) {
return dis;
}
else {
return dat;
}
}
private static Integer orDurationInt(Duration dis, Integer dat) {
if (dis != null) {
return (int) dis.toMillis();
}
else {
return dat;
}
}
private static Long orDurationLong(Duration dis, Long dat) {
if (dis != null) {
return dis.toMillis();
}
else {
return dat;
}
}
@Bean
@ConditionalOnMissingBean(Config.class)
public Config kubernetesClientConfig(
@@ -83,27 +114,29 @@ public class KubernetesAutoConfiguration {
.withConnectionTimeout(
orDurationInt(kubernetesClientProperties.getConnectionTimeout(),
base.getConnectionTimeout()))
.withRequestTimeout(orDurationInt(kubernetesClientProperties.getRequestTimeout(),
base.getRequestTimeout()))
.withRollingTimeout(orDurationLong(kubernetesClientProperties.getRollingTimeout(),
base.getRollingTimeout()))
.withRequestTimeout(
orDurationInt(kubernetesClientProperties.getRequestTimeout(),
base.getRequestTimeout()))
.withRollingTimeout(
orDurationLong(kubernetesClientProperties.getRollingTimeout(),
base.getRollingTimeout()))
.withTrustCerts(or(kubernetesClientProperties.isTrustCerts(),
base.isTrustCerts()))
.withHttpProxy(or(kubernetesClientProperties.getHttpProxy(),
base.getHttpProxy()))
base.getHttpProxy()))
.withHttpsProxy(or(kubernetesClientProperties.getHttpsProxy(),
base.getHttpsProxy()))
base.getHttpsProxy()))
.withProxyUsername(or(kubernetesClientProperties.getProxyUsername(),
base.getProxyUsername()))
base.getProxyUsername()))
.withPassword(or(kubernetesClientProperties.getProxyPassword(),
base.getProxyPassword()))
.withNoProxy(or(kubernetesClientProperties.getNoProxy(),
base.getNoProxy()))
base.getProxyPassword()))
.withNoProxy(
or(kubernetesClientProperties.getNoProxy(), base.getNoProxy()))
.build();
if (properties.getNamespace() == null || properties.getNamespace().isEmpty()) {
LOG.warn(
"No namespace has been detected. Please specify KUBERNETES_NAMESPACE env var, or use a later kubernetes version (1.3 or later)");
LOG.warn("No namespace has been detected. Please specify "
+ "KUBERNETES_NAMESPACE env var, or use a later kubernetes version (1.3 or later)");
}
return properties;
}
@@ -123,41 +156,17 @@ public class KubernetesAutoConfiguration {
@Configuration
@ConditionalOnClass(HealthIndicator.class)
protected static class KubernetesActuatorConfiguration {
@Bean
public KubernetesHealthIndicator kubernetesHealthIndicator(PodUtils podUtils) {
return new KubernetesHealthIndicator(podUtils);
}
@Bean
public KubernetesInfoContributor kubernetesInfoContributor(PodUtils podUtils) {
return new KubernetesInfoContributor(podUtils);
}
}
private static <D> D or(D dis, D dat) {
if (dis != null) {
return dis;
}
else {
return dat;
}
}
private static Integer orDurationInt(Duration dis, Integer dat) {
if (dis != null) {
return (int)dis.toMillis();
}
else {
return dat;
}
}
private static Long orDurationLong(Duration dis, Long dat) {
if (dis != null) {
return dis.toMillis();
}
else {
return dat;
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,7 +12,6 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes;
@@ -21,37 +20,66 @@ import java.time.Duration;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Kubernetes client properties.
*
* @author Ioannis Canellos
*/
@ConfigurationProperties("spring.cloud.kubernetes.client")
public class KubernetesClientProperties {
private Boolean trustCerts;
private String masterUrl;
private String apiVersion;
private String namespace;
private String caCertFile;
private String caCertData;
private String clientCertFile;
private String clientCertData;
private String clientKeyFile;
private String clientKeyData;
private String clientKeyAlgo;
private String clientKeyPassphrase;
private String username;
private String password;
private Duration watchReconnectInterval;
private Duration watchReconnectLimit;
private Duration connectionTimeout;
private Duration requestTimeout;
private Duration rollingTimeout;
private Duration loggingInterval;
private String httpProxy;
private String httpsProxy;
private String proxyUsername;
private String proxyPassword;
private String[] noProxy;
public String getClientCertData() {
return clientCertData;
return this.clientCertData;
}
public void setClientCertData(String clientCertData) {
@@ -59,15 +87,11 @@ public class KubernetesClientProperties {
}
public Boolean isTrustCerts() {
return trustCerts;
}
public void setTrustCerts(Boolean trustCerts) {
this.trustCerts = trustCerts;
return this.trustCerts;
}
public String getMasterUrl() {
return masterUrl;
return this.masterUrl;
}
public void setMasterUrl(String masterUrl) {
@@ -75,7 +99,7 @@ public class KubernetesClientProperties {
}
public String getApiVersion() {
return apiVersion;
return this.apiVersion;
}
public void setApiVersion(String apiVersion) {
@@ -83,7 +107,7 @@ public class KubernetesClientProperties {
}
public String getNamespace() {
return namespace;
return this.namespace;
}
public void setNamespace(String namespace) {
@@ -91,7 +115,7 @@ public class KubernetesClientProperties {
}
public String getCaCertFile() {
return caCertFile;
return this.caCertFile;
}
public void setCaCertFile(String caCertFile) {
@@ -99,7 +123,7 @@ public class KubernetesClientProperties {
}
public String getCaCertData() {
return caCertData;
return this.caCertData;
}
public void setCaCertData(String caCertData) {
@@ -107,7 +131,7 @@ public class KubernetesClientProperties {
}
public String getClientCertFile() {
return clientCertFile;
return this.clientCertFile;
}
public void setClientCertFile(String clientCertFile) {
@@ -115,7 +139,7 @@ public class KubernetesClientProperties {
}
public String getClientKeyFile() {
return clientKeyFile;
return this.clientKeyFile;
}
public void setClientKeyFile(String clientKeyFile) {
@@ -123,7 +147,7 @@ public class KubernetesClientProperties {
}
public String getClientKeyData() {
return clientKeyData;
return this.clientKeyData;
}
public void setClientKeyData(String clientKeyData) {
@@ -131,7 +155,7 @@ public class KubernetesClientProperties {
}
public String getClientKeyAlgo() {
return clientKeyAlgo;
return this.clientKeyAlgo;
}
public void setClientKeyAlgo(String clientKeyAlgo) {
@@ -139,7 +163,7 @@ public class KubernetesClientProperties {
}
public String getClientKeyPassphrase() {
return clientKeyPassphrase;
return this.clientKeyPassphrase;
}
public void setClientKeyPassphrase(String clientKeyPassphrase) {
@@ -147,7 +171,7 @@ public class KubernetesClientProperties {
}
public String getUsername() {
return username;
return this.username;
}
public void setUsername(String username) {
@@ -155,7 +179,7 @@ public class KubernetesClientProperties {
}
public String getPassword() {
return password;
return this.password;
}
public void setPassword(String password) {
@@ -163,7 +187,7 @@ public class KubernetesClientProperties {
}
public Duration getWatchReconnectInterval() {
return watchReconnectInterval;
return this.watchReconnectInterval;
}
public void setWatchReconnectInterval(Duration watchReconnectInterval) {
@@ -171,7 +195,7 @@ public class KubernetesClientProperties {
}
public Duration getWatchReconnectLimit() {
return watchReconnectLimit;
return this.watchReconnectLimit;
}
public void setWatchReconnectLimit(Duration watchReconnectLimit) {
@@ -179,7 +203,7 @@ public class KubernetesClientProperties {
}
public Duration getConnectionTimeout() {
return connectionTimeout;
return this.connectionTimeout;
}
public void setConnectionTimeout(Duration connectionTimeout) {
@@ -187,7 +211,7 @@ public class KubernetesClientProperties {
}
public Duration getRequestTimeout() {
return requestTimeout;
return this.requestTimeout;
}
public void setRequestTimeout(Duration requestTimeout) {
@@ -195,7 +219,7 @@ public class KubernetesClientProperties {
}
public Duration getRollingTimeout() {
return rollingTimeout;
return this.rollingTimeout;
}
public void setRollingTimeout(Duration rollingTimeout) {
@@ -203,7 +227,7 @@ public class KubernetesClientProperties {
}
public Duration getLoggingInterval() {
return loggingInterval;
return this.loggingInterval;
}
public void setLoggingInterval(Duration loggingInterval) {
@@ -211,11 +235,15 @@ public class KubernetesClientProperties {
}
public Boolean getTrustCerts() {
return trustCerts;
return this.trustCerts;
}
public void setTrustCerts(Boolean trustCerts) {
this.trustCerts = trustCerts;
}
public String getHttpProxy() {
return httpProxy;
return this.httpProxy;
}
public void setHttpProxy(String httpProxy) {
@@ -223,7 +251,7 @@ public class KubernetesClientProperties {
}
public String getHttpsProxy() {
return httpsProxy;
return this.httpsProxy;
}
public void setHttpsProxy(String httpsProxy) {
@@ -231,7 +259,7 @@ public class KubernetesClientProperties {
}
public String getProxyUsername() {
return proxyUsername;
return this.proxyUsername;
}
public void setProxyUsername(String proxyUsername) {
@@ -239,7 +267,7 @@ public class KubernetesClientProperties {
}
public String getProxyPassword() {
return proxyPassword;
return this.proxyPassword;
}
public void setProxyPassword(String proxyPassword) {
@@ -247,10 +275,11 @@ public class KubernetesClientProperties {
}
public String[] getNoProxy() {
return noProxy;
return this.noProxy;
}
public void setNoProxy(String[] noProxy) {
this.noProxy = noProxy;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,7 +12,6 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes;
@@ -22,6 +21,11 @@ import io.fabric8.kubernetes.api.model.Pod;
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
import org.springframework.boot.actuate.health.Health;
/**
* Kubernetes implementation of {@link AbstractHealthIndicator}.
*
* @author Ioannis Canellos
*/
public class KubernetesHealthIndicator extends AbstractHealthIndicator {
private PodUtils utils;
@@ -33,7 +37,7 @@ public class KubernetesHealthIndicator extends AbstractHealthIndicator {
@Override
protected void doHealthCheck(Health.Builder builder) throws Exception {
try {
Pod current = utils.currentPod().get();
Pod current = this.utils.currentPod().get();
if (current != null) {
builder.up().withDetail("inside", true)
.withDetail("namespace", current.getMetadata().getNamespace())
@@ -52,4 +56,5 @@ public class KubernetesHealthIndicator extends AbstractHealthIndicator {
builder.down(e);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,25 +12,29 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes;
import io.fabric8.kubernetes.api.model.Pod;
import java.util.HashMap;
import java.util.Map;
import io.fabric8.kubernetes.api.model.Pod;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.actuate.info.Info.Builder;
import org.springframework.boot.actuate.info.InfoContributor;
/**
* Kubernetes implementation of {@link InfoContributor}.
*
* @author Mark Anderson
*/
public class KubernetesInfoContributor implements InfoContributor {
private static final Log LOG = LogFactory.getLog(KubernetesInfoContributor.class);
private PodUtils utils;
public KubernetesInfoContributor(PodUtils utils) {
@@ -40,7 +44,7 @@ public class KubernetesInfoContributor implements InfoContributor {
@Override
public void contribute(Builder builder) {
try {
Pod current = utils.currentPod().get();
Pod current = this.utils.currentPod().get();
Map<String, Object> details = new HashMap<>();
if (current != null) {
details.put("inside", true);
@@ -60,4 +64,5 @@ public class KubernetesInfoContributor implements InfoContributor {
LOG.warn("Failed to get pod details", e);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,34 +12,41 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes;
import java.util.function.Supplier;
public class LazilyInstantiate<T> implements Supplier<T> {
/**
* Lazy instantiation utility class.
*
* @param <T> return type
* @author Ioannis Canellos
*/
public final class LazilyInstantiate<T> implements Supplier<T> {
public static <T> LazilyInstantiate<T> using(Supplier<T> supplier) {
return new LazilyInstantiate<T>(supplier);
}
private final Supplier<T> supplier;
public synchronized T get() {
return current.get();
}
private Supplier<T> current;
private LazilyInstantiate(Supplier<T> supplier) {
this.supplier = supplier;
this.current = () -> swapper();
}
private final Supplier<T> supplier;
private Supplier<T> current;
public static <T> LazilyInstantiate<T> using(Supplier<T> supplier) {
return new LazilyInstantiate<T>(supplier);
}
public synchronized T get() {
return this.current.get();
}
private T swapper() {
T obj = supplier.get();
current = () -> obj;
T obj = this.supplier.get();
this.current = () -> obj;
return obj;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,7 +12,6 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes;
@@ -21,6 +20,11 @@ import java.util.function.Supplier;
import io.fabric8.kubernetes.api.model.Pod;
/**
* Utility interface to retrieve {@link Pod} related information.
*
* @author Ioannis Canellos
*/
public interface PodUtils {
/**
@@ -33,4 +37,5 @@ public interface PodUtils {
* @return true if called from within Kubernetes, false otherwise.
*/
Boolean isInsideKubernetes();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,7 +12,6 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes;
@@ -26,13 +25,24 @@ import io.fabric8.kubernetes.client.KubernetesClient;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Utility class to work with pods.
*
* @author Ioannis Canellos
*/
public class StandardPodUtils implements PodUtils {
private static final Log LOG = LogFactory.getLog(StandardPodUtils.class);
/**
* Hostname environment variable name.
*/
public static final String HOSTNAME = "HOSTNAME";
private static final Log LOG = LogFactory.getLog(StandardPodUtils.class);
private final KubernetesClient client;
private final String hostName;
private Supplier<Pod> current;
public StandardPodUtils(KubernetesClient client) {
@@ -48,7 +58,7 @@ public class StandardPodUtils implements PodUtils {
@Override
public Supplier<Pod> currentPod() {
return current;
return this.current;
}
@Override
@@ -59,22 +69,23 @@ public class StandardPodUtils implements PodUtils {
private synchronized Pod internalGetPod() {
try {
if (isServiceAccountFound() && isHostNameEnvVarPresent()) {
return client.pods().withName(hostName).get();
return this.client.pods().withName(this.hostName).get();
}
else {
return null;
}
}
catch (Throwable t) {
LOG.warn("Failed to get pod with name:[" + hostName
+ "]. You should look into this if things aren't working as you expect. Are you missing serviceaccount permissions?",
LOG.warn("Failed to get pod with name:[" + this.hostName
+ "]. You should look into this if things aren't"
+ " working as you expect. Are you missing serviceaccount permissions?",
t);
return null;
}
}
private boolean isHostNameEnvVarPresent() {
return hostName != null && !hostName.isEmpty();
return this.hostName != null && !this.hostName.isEmpty();
}
private boolean isServiceAccountFound() {
@@ -82,4 +93,5 @@ public class StandardPodUtils implements PodUtils {
&& Paths.get(Config.KUBERNETES_SERVICE_ACCOUNT_CA_CRT_PATH).toFile()
.exists();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,7 +12,6 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.profile;
@@ -27,6 +26,11 @@ import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.Ordered;
/**
* Kubernetes specific application context initializer.
*
* @author Ioannis Canellos
*/
public class KubernetesApplicationContextInitializer implements
ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
@@ -54,7 +58,8 @@ public class KubernetesApplicationContextInitializer implements
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
listenerSupplier.get().addKubernetesProfile(applicationContext.getEnvironment());
this.listenerSupplier.get()
.addKubernetesProfile(applicationContext.getEnvironment());
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,7 +12,6 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.profile;
@@ -27,6 +26,11 @@ import org.springframework.core.Ordered;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
/**
* Adds Kubernetes profiles.
*
* @author Ioannis Canellos
*/
public class KubernetesProfileApplicationListener
implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered {
@@ -34,8 +38,11 @@ public class KubernetesProfileApplicationListener
.getLog(KubernetesProfileApplicationListener.class);
private static final String KUBERNETES_PROFILE = "kubernetes";
private static final int OFFSET = 1;
private static final int ORDER = Ordered.HIGHEST_PRECEDENCE + OFFSET;
private final PodUtils utils;
public KubernetesProfileApplicationListener(PodUtils utils) {
@@ -49,7 +56,7 @@ public class KubernetesProfileApplicationListener
}
void addKubernetesProfile(ConfigurableEnvironment environment) {
if (utils.isInsideKubernetes()) {
if (this.utils.isInsideKubernetes()) {
if (hasKubernetesProfile(environment)) {
if (LOG.isDebugEnabled()) {
LOG.debug("'kubernetes' already in list of active profiles");
@@ -83,4 +90,5 @@ public class KubernetesProfileApplicationListener
public int getOrder() {
return ORDER;
}
}

View File

@@ -1,7 +1,20 @@
package org.springframework.cloud.kubernetes;
/*
* 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
*
* http://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.
*/
import static io.restassured.RestAssured.given;
import static org.hamcrest.core.StringContains.containsString;
package org.springframework.cloud.kubernetes;
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.KubernetesClient;
@@ -11,16 +24,18 @@ import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.example.App;
import org.springframework.test.context.junit4.SpringRunner;
import static io.restassured.RestAssured.given;
import static org.hamcrest.core.StringContains.containsString;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = App.class,
properties = {
"management.endpoint.health.show-details=always"
})
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = App.class, properties = {
"management.endpoint.health.show-details=always" })
public class HealthIndicatorTest {
@ClassRule
@@ -31,29 +46,26 @@ public class HealthIndicatorTest {
@Value("${local.server.port}")
private int port;
@BeforeClass
public static void setUpBeforeClass() {
mockClient = server.getClient();
//Configure the kubernetes master url to point to the mock server
System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY, mockClient.getConfiguration().getMasterUrl());
// Configure the kubernetes master url to point to the mock server
System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY,
mockClient.getConfiguration().getMasterUrl());
System.setProperty(Config.KUBERNETES_TRUST_CERT_SYSTEM_PROPERTY, "true");
System.setProperty(Config.KUBERNETES_AUTH_TRYKUBECONFIG_SYSTEM_PROPERTY, "false");
System.setProperty(Config.KUBERNETES_AUTH_TRYSERVICEACCOUNT_SYSTEM_PROPERTY, "false");
System.setProperty(Config.KUBERNETES_AUTH_TRYSERVICEACCOUNT_SYSTEM_PROPERTY,
"false");
System.setProperty(Config.KUBERNETES_NAMESPACE_SYSTEM_PROPERTY, "test");
}
@Test
public void healthEndpointShouldContainKubernetes() {
RestAssured.baseURI = String.format("http://localhost:%d/actuator/health", port);
given()
.contentType("application/json")
.get()
.then()
.statusCode(200)
.body(containsString("kubernetes"));
RestAssured.baseURI = String.format("http://localhost:%d/actuator/health",
this.port);
given().contentType("application/json").get().then().statusCode(200)
.body(containsString("kubernetes"));
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,14 +12,10 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes;
import static io.restassured.RestAssured.given;
import static org.hamcrest.core.StringContains.containsString;
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.server.mock.KubernetesServer;
@@ -28,11 +24,15 @@ import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.example.App;
import org.springframework.test.context.junit4.SpringRunner;
import static io.restassured.RestAssured.given;
import static org.hamcrest.core.StringContains.containsString;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = App.class)
public class InfoContributorTest {
@@ -45,29 +45,26 @@ public class InfoContributorTest {
@Value("${local.server.port}")
private int port;
@BeforeClass
public static void setUpBeforeClass() {
mockClient = server.getClient();
//Configure the kubernetes master url to point to the mock server
System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY, mockClient.getConfiguration().getMasterUrl());
// Configure the kubernetes master url to point to the mock server
System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY,
mockClient.getConfiguration().getMasterUrl());
System.setProperty(Config.KUBERNETES_TRUST_CERT_SYSTEM_PROPERTY, "true");
System.setProperty(Config.KUBERNETES_AUTH_TRYKUBECONFIG_SYSTEM_PROPERTY, "false");
System.setProperty(Config.KUBERNETES_AUTH_TRYSERVICEACCOUNT_SYSTEM_PROPERTY, "false");
System.setProperty(Config.KUBERNETES_AUTH_TRYSERVICEACCOUNT_SYSTEM_PROPERTY,
"false");
System.setProperty(Config.KUBERNETES_NAMESPACE_SYSTEM_PROPERTY, "test");
}
@Test
public void infoEndpointShouldContainKubernetes() {
RestAssured.baseURI = String.format("http://localhost:%d/actuator/info", port);
given()
.contentType("application/json")
.get()
.then()
.statusCode(200)
.body(containsString("kubernetes"));
RestAssured.baseURI = String.format("http://localhost:%d/actuator/info",
this.port);
given().contentType("application/json").get().then().statusCode(200)
.body(containsString("kubernetes"));
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,15 +12,10 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import java.util.function.Supplier;
import org.junit.Before;
@@ -29,6 +24,10 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class LazilyInstantiateTest {
@@ -40,30 +39,32 @@ public class LazilyInstantiateTest {
@Before
public void setUp() throws Exception {
// common setup
when(mockSupplier.get()).thenReturn(SINGLETON)
when(this.mockSupplier.get()).thenReturn(SINGLETON)
.thenThrow(new RuntimeException("Supplier was called more than once!"));
}
@Test
public void supplierNotCalledInLazyInstantiateFactoryMethod() {
LazilyInstantiate.using(mockSupplier);
LazilyInstantiate.using(this.mockSupplier);
// verify
verifyZeroInteractions(mockSupplier);
verifyZeroInteractions(this.mockSupplier);
}
@Test
public void factoryReturnsSingletonFromSupplier() {
LazilyInstantiate<String> lazyStringFactory = LazilyInstantiate.using(mockSupplier);
LazilyInstantiate<String> lazyStringFactory = LazilyInstantiate
.using(this.mockSupplier);
String singletonString = lazyStringFactory.get();
// verify
assertEquals(SINGLETON, singletonString);
assertThat(singletonString).isEqualTo(SINGLETON);
}
@Test
public void factoryOnlyCallsSupplierOnce() {
LazilyInstantiate<String> lazyStringFactory = LazilyInstantiate.using(mockSupplier);
LazilyInstantiate<String> lazyStringFactory = LazilyInstantiate
.using(this.mockSupplier);
lazyStringFactory.get();
// mock will throw exception if it is called more than once

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,8 +12,8 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes;
import org.junit.Test;

View File

@@ -1,3 +1,19 @@
/*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.example;
import org.springframework.boot.autoconfigure.SpringBootApplication;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,7 +12,6 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.profile;
@@ -22,6 +21,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.cloud.kubernetes.PodUtils;
import org.springframework.core.env.ConfigurableEnvironment;
@@ -48,23 +48,23 @@ public class KubernetesProfileApplicationListenerTest {
@Before
public void before() {
when(mockEnvironment.getActiveProfiles()).thenReturn(ACTIVE_PROFILES);
when(mockEvent.getEnvironment()).thenReturn(mockEnvironment);
listener = new KubernetesProfileApplicationListener(mockPodUtils);
when(this.mockEnvironment.getActiveProfiles()).thenReturn(ACTIVE_PROFILES);
when(this.mockEvent.getEnvironment()).thenReturn(this.mockEnvironment);
this.listener = new KubernetesProfileApplicationListener(this.mockPodUtils);
}
@Test
public void shouldEnableKubernetesProfile() {
when(mockPodUtils.isInsideKubernetes()).thenReturn(true);
listener.onApplicationEvent(mockEvent);
verify(mockEnvironment).addActiveProfile("kubernetes");
when(this.mockPodUtils.isInsideKubernetes()).thenReturn(true);
this.listener.onApplicationEvent(this.mockEvent);
verify(this.mockEnvironment).addActiveProfile("kubernetes");
}
@Test
public void shouldNotEnableKubernetesProfile() {
when(mockPodUtils.isInsideKubernetes()).thenReturn(false);
listener.onApplicationEvent(mockEvent);
verify(mockEnvironment, times(0)).addActiveProfile("kubernetes");
when(this.mockPodUtils.isInsideKubernetes()).thenReturn(false);
this.listener.onApplicationEvent(this.mockEvent);
verify(this.mockEnvironment, times(0)).addActiveProfile("kubernetes");
}
}

View File

@@ -16,7 +16,8 @@
~
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>

View File

@@ -16,8 +16,8 @@
~
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springframework.cloud</groupId>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,69 +12,76 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.discovery;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* TODO break up into delegates if the implementation get's more complicated
* <p>
* Returns true if one of the following conditions apply:
* Returns true if one of the following conditions apply.
* <p>
* spring.cloud.kubernetes.discovery.secured has been set to true
* the service contains a label or an annotation named 'secured' that is truthy
* the port is one of the known ports used for secure communication
* spring.cloud.kubernetes.discovery.secured has been set to true the service contains a
* label or an annotation named 'secured' that is truthy the port is one of the known
* ports used for secure communication
*/
class DefaultIsServicePortSecureResolver {
private static final Log log = LogFactory.getLog(DefaultIsServicePortSecureResolver.class);
private static final Log log = LogFactory
.getLog(DefaultIsServicePortSecureResolver.class);
private static final Set<String> TRUTHY_STRINGS = new HashSet<String>() {{
add("true");
add("on");
add("yes");
add("1");
}};
private static final Set<String> TRUTHY_STRINGS = new HashSet<String>() {
{
add("true");
add("on");
add("yes");
add("1");
}
};
private final KubernetesDiscoveryProperties properties;
public DefaultIsServicePortSecureResolver(KubernetesDiscoveryProperties properties) {
DefaultIsServicePortSecureResolver(KubernetesDiscoveryProperties properties) {
this.properties = properties;
}
boolean resolve(Input input) {
final String securedLabelValue = input.getServiceLabels().getOrDefault("secured", "false");
final String securedLabelValue = input.getServiceLabels().getOrDefault("secured",
"false");
if (TRUTHY_STRINGS.contains(securedLabelValue)) {
if (log.isDebugEnabled()) {
log.debug("Considering service with name: " + input.getServiceName() + " and port " + input.getPort()
+ " is secure since the service contains a true value for the 'secured' label");
log.debug("Considering service with name: " + input.getServiceName()
+ " and port " + input.getPort()
+ " is secure since the service contains a true value for the 'secured' label");
}
return true;
}
final String securedAnnotationValue = input.getServiceAnnotations().getOrDefault("secured", "false");
final String securedAnnotationValue = input.getServiceAnnotations()
.getOrDefault("secured", "false");
if (TRUTHY_STRINGS.contains(securedAnnotationValue)) {
if (log.isDebugEnabled()) {
log.debug("Considering service with name: " + input.getServiceName() + " and port " + input.getPort()
+ " is secure since the service contains a true value for the 'secured' annotation");
log.debug("Considering service with name: " + input.getServiceName()
+ " and port " + input.getPort()
+ " is secure since the service contains a true value for the 'secured' annotation");
}
return true;
}
if (input.getPort() != null && properties.getKnownSecurePorts().contains(input.getPort())) {
if (input.getPort() != null
&& this.properties.getKnownSecurePorts().contains(input.getPort())) {
if (log.isDebugEnabled()) {
log.debug("Considering service with name: " + input.getServiceName() + " and port " + input.getPort()
+ " is secure due to the port being a known https port");
log.debug("Considering service with name: " + input.getServiceName()
+ " and port " + input.getPort()
+ " is secure due to the port being a known https port");
}
return true;
}
@@ -83,38 +90,45 @@ class DefaultIsServicePortSecureResolver {
}
static class Input {
private final Integer port;
private final String serviceName;
private final Map<String, String> serviceLabels;
private final Map<String, String> serviceAnnotations;
//used only for testing
// used only for testing
Input(Integer port, String serviceName) {
this(port, serviceName, null, null);
}
Input(Integer port, String serviceName,
Map<String, String> serviceLabels, Map<String, String> serviceAnnotations) {
Input(Integer port, String serviceName, Map<String, String> serviceLabels,
Map<String, String> serviceAnnotations) {
this.port = port;
this.serviceName = serviceName;
this.serviceLabels = serviceLabels == null ? new HashMap<>() : serviceLabels;
this.serviceAnnotations = serviceAnnotations == null ? new HashMap<>() : serviceAnnotations;
this.serviceAnnotations = serviceAnnotations == null ? new HashMap<>()
: serviceAnnotations;
}
public String getServiceName() {
return serviceName;
return this.serviceName;
}
public Map<String, String> getServiceLabels() {
return serviceLabels;
return this.serviceLabels;
}
public Map<String, String> getServiceAnnotations() {
return serviceAnnotations;
return this.serviceAnnotations;
}
public Integer getPort() {
return port;
return this.port;
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,11 +12,16 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.discovery;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import io.fabric8.kubernetes.api.model.EndpointAddress;
import io.fabric8.kubernetes.api.model.EndpointSubset;
import io.fabric8.kubernetes.api.model.Endpoints;
@@ -24,29 +29,25 @@ import io.fabric8.kubernetes.api.model.ObjectReference;
import io.fabric8.kubernetes.client.KubernetesClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.client.discovery.event.HeartbeatEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.scheduling.annotation.Scheduled;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
/**
* @author Oleg Vyukov
*/
public class KubernetesCatalogWatch implements ApplicationEventPublisherAware {
private static final Logger logger = LoggerFactory.getLogger(KubernetesCatalogWatch.class);
private static final Logger logger = LoggerFactory
.getLogger(KubernetesCatalogWatch.class);
private final KubernetesClient kubernetesClient;
private final AtomicReference<List<String>> catalogEndpointsState = new AtomicReference<>();
private ApplicationEventPublisher publisher;
private final AtomicReference<List<String>> catalogEndpointsState = new AtomicReference<>();
private ApplicationEventPublisher publisher;
public KubernetesCatalogWatch(KubernetesClient kubernetesClient) {
this.kubernetesClient = kubernetesClient;
@@ -60,33 +61,32 @@ public class KubernetesCatalogWatch implements ApplicationEventPublisherAware {
@Scheduled(fixedDelayString = "${spring.cloud.kubernetes.discovery.catalogServicesWatchDelay:30000}")
public void catalogServicesWatch() {
try {
List<String> previousState = catalogEndpointsState.get();
List<String> previousState = this.catalogEndpointsState.get();
//not all pods participate in the service discovery. only those that have endpoints.
List<Endpoints> endpoints = kubernetesClient.endpoints().list().getItems();
List<String> endpointsPodNames =
endpoints.stream()
.map(Endpoints::getSubsets)
.filter(Objects::nonNull)
.flatMap(Collection::stream)
.map(EndpointSubset::getAddresses)
.filter(Objects::nonNull)
.flatMap(Collection::stream)
.map(EndpointAddress::getTargetRef)
.filter(Objects::nonNull)
.map(ObjectReference::getName) // pod name unique in namespace
// not all pods participate in the service discovery. only those that have
// endpoints.
List<Endpoints> endpoints = this.kubernetesClient.endpoints().list()
.getItems();
List<String> endpointsPodNames = endpoints.stream().map(Endpoints::getSubsets)
.filter(Objects::nonNull).flatMap(Collection::stream)
.map(EndpointSubset::getAddresses).filter(Objects::nonNull)
.flatMap(Collection::stream).map(EndpointAddress::getTargetRef)
.filter(Objects::nonNull).map(ObjectReference::getName) // pod name
// unique in
// namespace
.sorted(String::compareTo).collect(Collectors.toList());
catalogEndpointsState.set(endpointsPodNames);
this.catalogEndpointsState.set(endpointsPodNames);
if (!endpointsPodNames.equals(previousState)) {
logger.trace("Received endpoints update from kubernetesClient: {}", endpointsPodNames);
publisher.publishEvent(new HeartbeatEvent(this, endpointsPodNames));
logger.trace("Received endpoints update from kubernetesClient: {}",
endpointsPodNames);
this.publisher.publishEvent(new HeartbeatEvent(this, endpointsPodNames));
}
} catch (Exception e) {
}
catch (Exception e) {
logger.error("Error watching Kubernetes Services", e);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,10 +12,12 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.discovery;
import java.util.function.Function;
import io.fabric8.kubernetes.api.model.Service;
import io.fabric8.kubernetes.api.model.ServiceList;
import io.fabric8.kubernetes.client.KubernetesClient;
@@ -23,23 +25,25 @@ import io.fabric8.kubernetes.client.Watch;
import io.fabric8.kubernetes.client.Watcher;
import io.fabric8.kubernetes.client.dsl.FilterWatchListDeletable;
import java.util.function.Function;
/**
* A regular java.util.function that is used to hide the complexity of the KubernetesClient interfaces
* A regular java.util.function that is used to hide the complexity of the
* KubernetesClient interfaces.
*
* It's meant to be used to abstract things like:
*
* client.services()
* client.services().withLabel("key", "value")
* client.services() client.services().withLabel("key", "value")
* client.services().withoutLabel("key")
*
* The result of the application of the function can then be used for example to list the services like so:
* The result of the application of the function can then be used for example to list the
* services like so:
*
* function.apply(client).list()
*
* See KubernetesDiscoveryClientAutoConfiguration.servicesFunction
*
* @author Georgios Andrianakis
*/
public interface KubernetesClientServicesFunction extends
Function<KubernetesClient, FilterWatchListDeletable<Service, ServiceList, Boolean, Watch, Watcher<Service>>> {
Function<KubernetesClient, FilterWatchListDeletable<Service, ServiceList, Boolean, Watch, Watcher<Service>>> {
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,10 +12,17 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.discovery;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import io.fabric8.kubernetes.api.model.EndpointAddress;
import io.fabric8.kubernetes.api.model.EndpointPort;
import io.fabric8.kubernetes.api.model.EndpointSubset;
@@ -24,6 +31,7 @@ import io.fabric8.kubernetes.api.model.Service;
import io.fabric8.kubernetes.client.KubernetesClient;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.expression.Expression;
@@ -32,42 +40,42 @@ import org.springframework.expression.spel.support.SimpleEvaluationContext;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import static java.util.stream.Collectors.toMap;
/**
* Kubeneretes implementation of {@link DiscoveryClient}.
*
* @author Ioannis Canellos
*/
public class KubernetesDiscoveryClient implements DiscoveryClient {
private static final Log log = LogFactory.getLog(KubernetesDiscoveryClient.class);
private KubernetesClient client;
private final KubernetesDiscoveryProperties properties;
private final DefaultIsServicePortSecureResolver isServicePortSecureResolver;
private final KubernetesClientServicesFunction kubernetesClientServicesFunction;
private final SpelExpressionParser parser = new SpelExpressionParser();
private final SimpleEvaluationContext evalCtxt = SimpleEvaluationContext
.forReadOnlyDataBinding()
.withInstanceMethods()
.build();
.forReadOnlyDataBinding().withInstanceMethods().build();
private KubernetesClient client;
public KubernetesDiscoveryClient(KubernetesClient client,
KubernetesDiscoveryProperties kubernetesDiscoveryProperties,
KubernetesClientServicesFunction kubernetesClientServicesFunction) {
KubernetesDiscoveryProperties kubernetesDiscoveryProperties,
KubernetesClientServicesFunction kubernetesClientServicesFunction) {
this(client, kubernetesDiscoveryProperties, kubernetesClientServicesFunction,
new DefaultIsServicePortSecureResolver(kubernetesDiscoveryProperties));
new DefaultIsServicePortSecureResolver(kubernetesDiscoveryProperties));
}
KubernetesDiscoveryClient(KubernetesClient client,
KubernetesDiscoveryProperties kubernetesDiscoveryProperties,
KubernetesClientServicesFunction kubernetesClientServicesFunction,
DefaultIsServicePortSecureResolver isServicePortSecureResolver) {
KubernetesDiscoveryProperties kubernetesDiscoveryProperties,
KubernetesClientServicesFunction kubernetesClientServicesFunction,
DefaultIsServicePortSecureResolver isServicePortSecureResolver) {
this.client = client;
this.properties = kubernetesDiscoveryProperties;
@@ -76,7 +84,7 @@ public class KubernetesDiscoveryClient implements DiscoveryClient {
}
public KubernetesClient getClient() {
return client;
return this.client;
}
public void setClient(KubernetesClient client) {
@@ -93,62 +101,64 @@ public class KubernetesDiscoveryClient implements DiscoveryClient {
Assert.notNull(serviceId,
"[Assertion failed] - the object argument must be null");
Endpoints endpoints = client.endpoints().withName(serviceId).get();
Endpoints endpoints = this.client.endpoints().withName(serviceId).get();
List<EndpointSubset> subsets = getSubsetsFromEndpoints(endpoints);
List<ServiceInstance> instances = new ArrayList<>();
if (!subsets.isEmpty()) {
final Service service = client.services().withName(serviceId).get();
final Service service = this.client.services().withName(serviceId).get();
final Map<String, String> serviceMetadata = new HashMap<>();
KubernetesDiscoveryProperties.Metadata metadataProps = properties.getMetadata();
if(metadataProps.isAddLabels()) {
KubernetesDiscoveryProperties.Metadata metadataProps = this.properties
.getMetadata();
if (metadataProps.isAddLabels()) {
Map<String, String> labelMetadata = getMapWithPrefixedKeys(
service.getMetadata().getLabels(), metadataProps.getLabelsPrefix());
service.getMetadata().getLabels(),
metadataProps.getLabelsPrefix());
if (log.isDebugEnabled()) {
log.debug("Adding label metadata: "+labelMetadata);
log.debug("Adding label metadata: " + labelMetadata);
}
serviceMetadata.putAll(labelMetadata);
}
if(metadataProps.isAddAnnotations()) {
if (metadataProps.isAddAnnotations()) {
Map<String, String> annotationMetadata = getMapWithPrefixedKeys(
service.getMetadata().getAnnotations(), metadataProps.getAnnotationsPrefix());
service.getMetadata().getAnnotations(),
metadataProps.getAnnotationsPrefix());
if (log.isDebugEnabled()) {
log.debug("Adding annotation metadata: "+annotationMetadata);
log.debug("Adding annotation metadata: " + annotationMetadata);
}
serviceMetadata.putAll(annotationMetadata);
}
for (EndpointSubset s : subsets) {
// Extend the service metadata map with per-endpoint port information (if requested)
// Extend the service metadata map with per-endpoint port information (if
// requested)
Map<String, String> endpointMetadata = new HashMap<>(serviceMetadata);
if(metadataProps.isAddPorts()) {
if (metadataProps.isAddPorts()) {
Map<String, String> ports = s.getPorts().stream()
.filter(port -> !StringUtils.isEmpty(port.getName()))
.collect(toMap(EndpointPort::getName, port -> Integer.toString(port.getPort())));
Map<String, String> portMetadata = getMapWithPrefixedKeys(ports, metadataProps.getPortsPrefix());
.filter(port -> !StringUtils.isEmpty(port.getName()))
.collect(toMap(EndpointPort::getName,
port -> Integer.toString(port.getPort())));
Map<String, String> portMetadata = getMapWithPrefixedKeys(ports,
metadataProps.getPortsPrefix());
if (log.isDebugEnabled()) {
log.debug("Adding port metadata: "+portMetadata);
log.debug("Adding port metadata: " + portMetadata);
}
endpointMetadata.putAll(portMetadata);
}
List<EndpointAddress> addresses = s.getAddresses();
for (EndpointAddress endpointAddress : addresses) {
final EndpointPort endpointPort =
s.getPorts().stream().findFirst().orElseThrow(IllegalStateException::new);
final EndpointPort endpointPort = s.getPorts().stream().findFirst()
.orElseThrow(IllegalStateException::new);
instances.add(new KubernetesServiceInstance(serviceId,
endpointAddress,
endpointPort,
endpointMetadata,
isServicePortSecureResolver.resolve(
new DefaultIsServicePortSecureResolver.Input(
endpointAddress, endpointPort, endpointMetadata,
this.isServicePortSecureResolver
.resolve(new DefaultIsServicePortSecureResolver.Input(
endpointPort.getPort(),
service.getMetadata().getName(),
service.getMetadata().getLabels(),
service.getMetadata().getAnnotations()
)
)));
service.getMetadata().getAnnotations()))));
}
}
}
@@ -170,7 +180,8 @@ public class KubernetesDiscoveryClient implements DiscoveryClient {
// returns a new map that contain all the entries of the original map
// but with the keys prefixed
// if the prefix is null or empty, the map itself is returned (unchanged of course)
private Map<String, String> getMapWithPrefixedKeys(Map<String, String> map, String prefix) {
private Map<String, String> getMapWithPrefixedKeys(Map<String, String> map,
String prefix) {
if (map == null) {
return new HashMap<>();
}
@@ -188,14 +199,16 @@ public class KubernetesDiscoveryClient implements DiscoveryClient {
@Override
public List<String> getServices() {
String spelExpression = properties.getFilter();
String spelExpression = this.properties.getFilter();
Predicate<Service> filteredServices;
if (spelExpression == null || spelExpression.isEmpty()) {
filteredServices = (Service instance) -> true;
} else {
Expression filterExpr = parser.parseExpression(spelExpression);
}
else {
Expression filterExpr = this.parser.parseExpression(spelExpression);
filteredServices = (Service instance) -> {
Boolean include = filterExpr.getValue(evalCtxt, instance, Boolean.class);
Boolean include = filterExpr.getValue(this.evalCtxt, instance,
Boolean.class);
if (include == null) {
return false;
}
@@ -206,11 +219,8 @@ public class KubernetesDiscoveryClient implements DiscoveryClient {
}
public List<String> getServices(Predicate<Service> filter) {
return kubernetesClientServicesFunction.apply(client).list()
.getItems()
.stream()
.filter(filter)
.map(s -> s.getMetadata().getName())
return this.kubernetesClientServicesFunction.apply(this.client).list().getItems()
.stream().filter(filter).map(s -> s.getMetadata().getName())
.collect(Collectors.toList());
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,12 +12,12 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.discovery;
import io.fabric8.kubernetes.client.KubernetesClient;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -28,21 +28,28 @@ import org.springframework.cloud.kubernetes.registry.KubernetesServiceRegistry;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Auto configuration for discovery clients.
*
* @author Mauricio Salatino
*/
@Configuration
@ConditionalOnProperty(name="spring.cloud.kubernetes.enabled", matchIfMissing = true)
@ConditionalOnProperty(name = "spring.cloud.kubernetes.enabled", matchIfMissing = true)
@AutoConfigureBefore({ SimpleDiscoveryClientAutoConfiguration.class,
CommonsClientAutoConfiguration.class, })
CommonsClientAutoConfiguration.class })
public class KubernetesDiscoveryClientAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public DefaultIsServicePortSecureResolver isServicePortSecureResolver(KubernetesDiscoveryProperties properties) {
public DefaultIsServicePortSecureResolver isServicePortSecureResolver(
KubernetesDiscoveryProperties properties) {
return new DefaultIsServicePortSecureResolver(properties);
}
@Bean
public KubernetesClientServicesFunction servicesFunction(KubernetesDiscoveryProperties properties) {
if (properties.getServiceLabels().isEmpty()) {
public KubernetesClientServicesFunction servicesFunction(
KubernetesDiscoveryProperties properties) {
if (properties.getServiceLabels().isEmpty()) {
return KubernetesClient::services;
}
@@ -51,12 +58,13 @@ public class KubernetesDiscoveryClientAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(name = "spring.cloud.kubernetes.discovery.enabled",matchIfMissing = true)
@ConditionalOnProperty(name = "spring.cloud.kubernetes.discovery.enabled", matchIfMissing = true)
public KubernetesDiscoveryClient kubernetesDiscoveryClient(KubernetesClient client,
KubernetesDiscoveryProperties properties,
KubernetesClientServicesFunction kubernetesClientServicesFunction,
DefaultIsServicePortSecureResolver isServicePortSecureResolver) {
return new KubernetesDiscoveryClient(client, properties, kubernetesClientServicesFunction, isServicePortSecureResolver);
KubernetesDiscoveryProperties properties,
KubernetesClientServicesFunction kubernetesClientServicesFunction,
DefaultIsServicePortSecureResolver isServicePortSecureResolver) {
return new KubernetesDiscoveryClient(client, properties,
kubernetesClientServicesFunction, isServicePortSecureResolver);
}
@Bean
@@ -66,7 +74,7 @@ public class KubernetesDiscoveryClientAutoConfiguration {
@Bean
public KubernetesRegistration getRegistration(KubernetesClient client,
KubernetesDiscoveryProperties properties) {
KubernetesDiscoveryProperties properties) {
return new KubernetesRegistration(client, properties);
}
@@ -81,4 +89,5 @@ public class KubernetesDiscoveryClientAutoConfiguration {
public KubernetesCatalogWatch kubernetesCatalogWatch(KubernetesClient client) {
return new KubernetesCatalogWatch(client);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,8 +12,8 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.discovery;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -21,8 +21,15 @@ import org.springframework.cloud.kubernetes.KubernetesAutoConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* Bootstrap config for Kubernetes discovery config client.
*
* @author Zhanwei Wang
*/
@Configuration
@ConditionalOnProperty(value = "spring.cloud.config.discovery.enabled")
@Import({KubernetesAutoConfiguration.class,
KubernetesDiscoveryClientAutoConfiguration.class, })
public class KubernetesDiscoveryClientConfigClientBootstrapConfiguration {}
@ConditionalOnProperty("spring.cloud.config.discovery.enabled")
@Import({ KubernetesAutoConfiguration.class,
KubernetesDiscoveryClientAutoConfiguration.class })
public class KubernetesDiscoveryClientConfigClientBootstrapConfiguration {
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,20 +12,24 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.discovery;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.core.style.ToStringCreator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.core.style.ToStringCreator;
/**
* Kubernetes discovery properties.
*
* @author Ioannis Canellos
*/
@ConfigurationProperties("spring.cloud.kubernetes.discovery")
public class KubernetesDiscoveryProperties {
@@ -36,23 +40,30 @@ public class KubernetesDiscoveryProperties {
@Value("${spring.application.name:unknown}")
private String serviceName = "unknown";
/** SpEL expression to filter services AFTER they have been retrieved from the Kubernetes API server. */
/**
* SpEL expression to filter services AFTER they have been retrieved from the
* Kubernetes API server.
*/
private String filter;
/** Set the port numbers that are considered secure and use HTTPS. */
private Set<Integer> knownSecurePorts = new HashSet<Integer>() {
{
add(443);
add(8443);
}
};
/** Set the port numbers that are considered secure and use HTTPS */
private Set<Integer> knownSecurePorts = new HashSet<Integer>() {{
add(443);
add(8443);
}};
/** If set, then only the services matching these labels will be fetched from the Kubernetes API server */
/**
* If set, then only the services matching these labels will be fetched from the
* Kubernetes API server.
*/
private Map<String, String> serviceLabels = new HashMap<>();
private Metadata metadata = new Metadata();
public boolean isEnabled() {
return enabled;
return this.enabled;
}
public void setEnabled(boolean enabled) {
@@ -60,7 +71,7 @@ public class KubernetesDiscoveryProperties {
}
public String getServiceName() {
return serviceName;
return this.serviceName;
}
public void setServiceName(String serviceName) {
@@ -68,15 +79,15 @@ public class KubernetesDiscoveryProperties {
}
public String getFilter() {
return filter;
return this.filter;
}
public void setFilter(String filter){
public void setFilter(String filter) {
this.filter = filter;
}
public Set<Integer> getKnownSecurePorts() {
return knownSecurePorts;
return this.knownSecurePorts;
}
public void setKnownSecurePorts(Set<Integer> knownSecurePorts) {
@@ -84,7 +95,7 @@ public class KubernetesDiscoveryProperties {
}
public Map<String, String> getServiceLabels() {
return serviceLabels;
return this.serviceLabels;
}
public void setServiceLabels(Map<String, String> serviceLabels) {
@@ -92,7 +103,7 @@ public class KubernetesDiscoveryProperties {
}
public Metadata getMetadata() {
return metadata;
return this.metadata;
}
public void setMetadata(Metadata metadata) {
@@ -101,37 +112,56 @@ public class KubernetesDiscoveryProperties {
@Override
public String toString() {
return new ToStringCreator(this)
.append("enabled", enabled)
.append("serviceName", serviceName)
.append("filter", filter)
.append("knownSecurePorts", knownSecurePorts)
.append("serviceLabels", serviceLabels)
.append("metadata", metadata)
.toString();
return new ToStringCreator(this).append("enabled", this.enabled)
.append("serviceName", this.serviceName).append("filter", this.filter)
.append("knownSecurePorts", this.knownSecurePorts)
.append("serviceLabels", this.serviceLabels)
.append("metadata", this.metadata).toString();
}
/**
* Metadata properties.
*/
public class Metadata {
/** When set, the Kubernetes labels of the services will be included as metadata of the returned ServiceInstance. */
/**
* When set, the Kubernetes labels of the services will be included as metadata of
* the returned ServiceInstance.
*/
private boolean addLabels = true;
/** When addLabels is set, then this will be used as a prefix to the key names in the metadata map. */
/**
* When addLabels is set, then this will be used as a prefix to the key names in
* the metadata map.
*/
private String labelsPrefix;
/** When set, the Kubernetes annotations of the services will be included as metadata of the returned ServiceInstance. */
/**
* When set, the Kubernetes annotations of the services will be included as
* metadata of the returned ServiceInstance.
*/
private boolean addAnnotations = true;
/** When addAnnotations is set, then this will be used as a prefix to the key names in the metadata map. */
/**
* When addAnnotations is set, then this will be used as a prefix to the key names
* in the metadata map.
*/
private String annotationsPrefix;
/** When set, any named Kubernetes service ports will be included as metadata of the returned ServiceInstance. */
/**
* When set, any named Kubernetes service ports will be included as metadata of
* the returned ServiceInstance.
*/
private boolean addPorts = true;
/** When addPorts is set, then this will be used as a prefix to the key names in the metadata map. */
/**
* When addPorts is set, then this will be used as a prefix to the key names in
* the metadata map.
*/
private String portsPrefix = "port.";
public boolean isAddLabels() {
return addLabels;
return this.addLabels;
}
public void setAddLabels(boolean addLabels) {
@@ -139,7 +169,7 @@ public class KubernetesDiscoveryProperties {
}
public String getLabelsPrefix() {
return labelsPrefix;
return this.labelsPrefix;
}
public void setLabelsPrefix(String labelsPrefix) {
@@ -147,7 +177,7 @@ public class KubernetesDiscoveryProperties {
}
public boolean isAddAnnotations() {
return addAnnotations;
return this.addAnnotations;
}
public void setAddAnnotations(boolean addAnnotations) {
@@ -155,7 +185,7 @@ public class KubernetesDiscoveryProperties {
}
public String getAnnotationsPrefix() {
return annotationsPrefix;
return this.annotationsPrefix;
}
public void setAnnotationsPrefix(String annotationsPrefix) {
@@ -163,7 +193,7 @@ public class KubernetesDiscoveryProperties {
}
public boolean isAddPorts() {
return addPorts;
return this.addPorts;
}
public void setAddPorts(boolean addPorts) {
@@ -171,7 +201,7 @@ public class KubernetesDiscoveryProperties {
}
public String getPortsPrefix() {
return portsPrefix;
return this.portsPrefix;
}
public void setPortsPrefix(String portsPrefix) {
@@ -180,14 +210,14 @@ public class KubernetesDiscoveryProperties {
@Override
public String toString() {
return new ToStringCreator(this)
.append("addLabels", addLabels)
.append("labelsPrefix", labelsPrefix)
.append("addAnnotations", addAnnotations)
.append("annotationsPrefix", annotationsPrefix)
.append("addPorts", addPorts)
.append("portsPrefix", portsPrefix)
.toString();
return new ToStringCreator(this).append("addLabels", this.addLabels)
.append("labelsPrefix", this.labelsPrefix)
.append("addAnnotations", this.addAnnotations)
.append("annotationsPrefix", this.annotationsPrefix)
.append("addPorts", this.addPorts)
.append("portsPrefix", this.portsPrefix).toString();
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,7 +12,6 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.discovery;
@@ -23,25 +22,34 @@ import java.util.Map;
import io.fabric8.kubernetes.api.model.EndpointAddress;
import io.fabric8.kubernetes.api.model.EndpointPort;
import org.springframework.cloud.client.ServiceInstance;
/**
* Kubernetes {@link ServiceInstance}.
*
* @author Ioannis Canellos
*/
public class KubernetesServiceInstance implements ServiceInstance {
private static final String HTTP_PREFIX = "http://";
private static final String HTTPS_PREFIX = "https://";
private static final String COLN = ":";
private final String serviceId;
private final EndpointAddress endpointAddress;
private final EndpointPort endpointPort;
private final Boolean secure;
private final Map<String, String> metadata;
public KubernetesServiceInstance(String serviceId,
EndpointAddress endpointAddress,
EndpointPort endpointPort,
Map<String, String> metadata,
Boolean secure) {
public KubernetesServiceInstance(String serviceId, EndpointAddress endpointAddress,
EndpointPort endpointPort, Map<String, String> metadata, Boolean secure) {
this.serviceId = serviceId;
this.endpointAddress = endpointAddress;
this.endpointPort = endpointPort;
@@ -51,22 +59,22 @@ public class KubernetesServiceInstance implements ServiceInstance {
@Override
public String getServiceId() {
return serviceId;
return this.serviceId;
}
@Override
public String getHost() {
return endpointAddress.getIp();
return this.endpointAddress.getIp();
}
@Override
public int getPort() {
return endpointPort.getPort();
return this.endpointPort.getPort();
}
@Override
public boolean isSecure() {
return secure;
return this.secure;
}
@Override
@@ -75,19 +83,22 @@ public class KubernetesServiceInstance implements ServiceInstance {
if (isSecure()) {
sb.append(HTTPS_PREFIX);
} else {
}
else {
sb.append(HTTP_PREFIX);
}
sb.append(getHost()).append(COLN).append(getPort());
try {
return new URI(sb.toString());
} catch (URISyntaxException e) {
}
catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
public Map<String, String> getMetadata() {
return metadata;
return this.metadata;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,7 +12,6 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.registry;
@@ -22,6 +21,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.web.servlet.context.ServletWebServerInitializedEvent;
import org.springframework.cloud.client.discovery.event.InstanceRegisteredEvent;
import org.springframework.cloud.client.serviceregistry.AutoServiceRegistration;
@@ -31,11 +31,16 @@ import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.core.Ordered;
public class KubernetesAutoServiceRegistration implements AutoServiceRegistration,
SmartLifecycle,
Ordered {
/**
* Auto service registration for Kubernetes.
*
* @author Mauricio Salatino
*/
public class KubernetesAutoServiceRegistration
implements AutoServiceRegistration, SmartLifecycle, Ordered {
private static final Log log = LogFactory.getLog(KubernetesAutoServiceRegistration.class);
private static final Log log = LogFactory
.getLog(KubernetesAutoServiceRegistration.class);
private AtomicBoolean running = new AtomicBoolean(false);
@@ -50,8 +55,8 @@ public class KubernetesAutoServiceRegistration implements AutoServiceRegistratio
private KubernetesRegistration registration;
public KubernetesAutoServiceRegistration(ApplicationContext context,
KubernetesServiceRegistry serviceRegistry,
KubernetesRegistration registration) {
KubernetesServiceRegistry serviceRegistry,
KubernetesRegistration registration) {
this.context = context;
this.serviceRegistry = serviceRegistry;
this.registration = registration;
@@ -73,8 +78,7 @@ public class KubernetesAutoServiceRegistration implements AutoServiceRegistratio
this.serviceRegistry.register(this.registration);
this.context.publishEvent(
new InstanceRegisteredEvent<>(this,
this.registration.getProperties()));
new InstanceRegisteredEvent<>(this, this.registration.getProperties()));
this.running.set(true);
}
@@ -105,16 +109,16 @@ public class KubernetesAutoServiceRegistration implements AutoServiceRegistratio
int localPort = event.getWebServer().getPort();
if (this.port.get() == 0) {
log.info("Updating port to " + localPort);
this.port.compareAndSet(0,
localPort);
this.port.compareAndSet(0, localPort);
start();
}
}
@EventListener(ContextClosedEvent.class)
public void onApplicationEvent(ContextClosedEvent event) {
if (event.getApplicationContext() == context) {
if (event.getApplicationContext() == this.context) {
stop();
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,7 +12,6 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.registry;
@@ -25,18 +24,25 @@ import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import io.fabric8.kubernetes.client.KubernetesClient;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.cloud.kubernetes.discovery.KubernetesDiscoveryProperties;
/**
* Kubernetes implementation of a {@link Registration}.
*
* @author Mauricio Salatino
*/
public class KubernetesRegistration implements Registration, Closeable {
private final KubernetesClient client;
private KubernetesDiscoveryProperties properties;
private AtomicBoolean running = new AtomicBoolean(false);
public KubernetesRegistration(KubernetesClient client,
KubernetesDiscoveryProperties properties) {
KubernetesDiscoveryProperties properties) {
this.client = client;
this.properties = properties;
}
@@ -48,12 +54,12 @@ public class KubernetesRegistration implements Registration, Closeable {
@Override
public String getServiceId() {
return properties.getServiceName();
return this.properties.getServiceName();
}
@Override
public String getHost() {
return client.getMasterUrl().getHost();
return this.client.getMasterUrl().getHost();
}
@Override
@@ -69,15 +75,16 @@ public class KubernetesRegistration implements Registration, Closeable {
@Override
public URI getUri() {
try {
return client.getMasterUrl().toURI();
} catch (URISyntaxException e) {
return this.client.getMasterUrl().toURI();
}
catch (URISyntaxException e) {
e.printStackTrace();
}
return null;
}
public KubernetesDiscoveryProperties getProperties() {
return properties;
return this.properties;
}
@Override
@@ -87,10 +94,8 @@ public class KubernetesRegistration implements Registration, Closeable {
@Override
public String toString() {
return "KubernetesRegistration{" +
"client=" + client +
", properties=" + properties +
", running=" + running +
'}';
return "KubernetesRegistration{" + "client=" + this.client + ", properties="
+ this.properties + ", running=" + this.running + '}';
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,16 +12,22 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.registry;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
public class KubernetesServiceRegistry implements ServiceRegistry<KubernetesRegistration> {
/**
* Kubernetes {@link ServiceRegistry}.
*
* @author Mauricio Salatino
*/
public class KubernetesServiceRegistry
implements ServiceRegistry<KubernetesRegistration> {
private static final Log log = LogFactory.getLog(KubernetesServiceRegistry.class);
@@ -44,15 +50,15 @@ public class KubernetesServiceRegistry implements ServiceRegistry<KubernetesRegi
}
@Override
public void setStatus(KubernetesRegistration registration,
String status) {
public void setStatus(KubernetesRegistration registration, String status) {
log.info("Set Status for : " + registration + " Status: " + status);
}
@Override
public <T> T getStatus(KubernetesRegistration registration) {
log.info("Get Status for : " + registration );
log.info("Get Status for : " + registration);
return null;
}
}

View File

@@ -1,5 +1,4 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.kubernetes.discovery.KubernetesDiscoveryClientAutoConfiguration
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.kubernetes.discovery.KubernetesDiscoveryClientConfigClientBootstrapConfiguration

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,7 +12,6 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.discovery
@@ -23,9 +22,10 @@ import io.fabric8.kubernetes.api.model.ServiceListBuilder
import io.fabric8.kubernetes.client.Config
import io.fabric8.kubernetes.client.KubernetesClient
import io.fabric8.kubernetes.server.mock.KubernetesMockServer
import spock.lang.Specification
import org.springframework.cloud.client.ServiceInstance
import org.springframework.cloud.client.discovery.DiscoveryClient
import spock.lang.Specification
import static org.assertj.core.api.Assertions.assertThat
@@ -40,10 +40,12 @@ class KubernetesDiscoveryClientTest extends Specification {
mockClient = mockServer.createClient()
//Configure the kubernetes master url to point to the mock server
System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY, mockClient.getConfiguration().getMasterUrl())
System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY, mockClient.
getConfiguration().getMasterUrl())
System.setProperty(Config.KUBERNETES_TRUST_CERT_SYSTEM_PROPERTY, "true")
System.setProperty(Config.KUBERNETES_AUTH_TRYKUBECONFIG_SYSTEM_PROPERTY, "false")
System.setProperty(Config.KUBERNETES_AUTH_TRYSERVICEACCOUNT_SYSTEM_PROPERTY, "false")
System.
setProperty(Config.KUBERNETES_AUTH_TRYSERVICEACCOUNT_SYSTEM_PROPERTY, "false")
}
def cleanupSpec() {
@@ -52,156 +54,186 @@ class KubernetesDiscoveryClientTest extends Specification {
def "getInstances should be able to handle endpoints single address"() {
given:
mockServer.expect().get().withPath("/api/v1/namespaces/test/endpoints/endpoint").andReturn(200, new EndpointsBuilder()
.withNewMetadata()
mockServer.expect().get().
withPath("/api/v1/namespaces/test/endpoints/endpoint").
andReturn(200, new EndpointsBuilder()
.withNewMetadata()
.withName("endpoint")
.endMetadata()
.addNewSubset()
.endMetadata()
.addNewSubset()
.addNewAddress()
.withIp("ip1")
.withIp("ip1")
.endAddress()
.addNewPort("http",80,"TCP")
.endSubset()
.build()).once()
.addNewPort("http", 80, "TCP")
.endSubset()
.build()).once()
and:
mockServer.expect().get().withPath("/api/v1/namespaces/test/services/endpoint").andReturn(200, new ServiceBuilder()
.withNewMetadata()
.withName("endpoint")
.withLabels(new HashMap<String, String>() {{
put("l", "v")
}})
.endMetadata()
.build()).once()
mockServer.expect().get().
withPath("/api/v1/namespaces/test/services/endpoint").
andReturn(200, new ServiceBuilder()
.withNewMetadata()
.withName("endpoint")
.withLabels(new HashMap<String, String>() {
{
put("l", "v")
}
})
.endMetadata()
.build()).once()
final properties = new KubernetesDiscoveryProperties()
DiscoveryClient discoveryClient = new KubernetesDiscoveryClient(
mockClient, properties, {client -> client.services()}, new DefaultIsServicePortSecureResolver(properties))
mockClient, properties, { client ->
client.services()
}, new DefaultIsServicePortSecureResolver(properties))
when:
List<ServiceInstance> instances = discoveryClient.getInstances("endpoint")
List<ServiceInstance> instances = discoveryClient.getInstances("endpoint")
then:
instances != null
instances.size() == 1
instances.find({s -> s.host == "ip1" && !s.secure})
instances != null
instances.size() == 1
instances.find({ s -> s.host == "ip1" && !s.secure })
}
def "getInstances should be able to handle endpoints multiple addresses"() {
given:
mockServer.expect().get().withPath("/api/v1/namespaces/test/endpoints/endpoint").andReturn(200, new EndpointsBuilder()
.withNewMetadata()
mockServer.expect().get().
withPath("/api/v1/namespaces/test/endpoints/endpoint").
andReturn(200, new EndpointsBuilder()
.withNewMetadata()
.withName("endpoint")
.endMetadata()
.addNewSubset()
.endMetadata()
.addNewSubset()
.addNewAddress()
.withIp("ip1")
.withIp("ip1")
.endAddress()
.addNewAddress()
.withIp("ip2")
.withIp("ip2")
.endAddress()
.addNewPort("https",443,"TCP")
.endSubset()
.build()).once()
.addNewPort("https", 443, "TCP")
.endSubset()
.build()).once()
and:
mockServer.expect().get().withPath("/api/v1/namespaces/test/services/endpoint").andReturn(200, new ServiceBuilder()
.withNewMetadata()
.withName("endpoint")
.withLabels(new HashMap<String, String>() {{
put("l", "v")
}})
.endMetadata()
.build()).once()
mockServer.expect().get().
withPath("/api/v1/namespaces/test/services/endpoint").
andReturn(200, new ServiceBuilder()
.withNewMetadata()
.withName("endpoint")
.withLabels(new HashMap<String, String>() {
{
put("l", "v")
}
})
.endMetadata()
.build()).once()
final properties = new KubernetesDiscoveryProperties()
DiscoveryClient discoveryClient = new KubernetesDiscoveryClient(
mockClient, properties, {client -> client.services()}, new DefaultIsServicePortSecureResolver(properties))
mockClient, properties, { client ->
client.services()
}, new DefaultIsServicePortSecureResolver(properties))
when:
List<ServiceInstance> instances = discoveryClient.getInstances("endpoint")
List<ServiceInstance> instances = discoveryClient.getInstances("endpoint")
then:
instances != null
instances.size() == 2
instances.find({s -> s.host == "ip1" && s.secure})
instances.find({s -> s.host == "ip2" && s.secure})
instances != null
instances.size() == 2
instances.find({ s -> s.host == "ip1" && s.secure })
instances.find({ s -> s.host == "ip2" && s.secure })
}
def "getServices should return all services when no labels are applied to the client"() {
given:
mockServer.expect().get().withPath("/api/v1/namespaces/test/services").andReturn(200, new ServiceListBuilder()
.addNewItem()
.withNewMetadata()
.withName("s1")
.withLabels(new HashMap<String, String>() {{
put("label", "value")
}})
.endMetadata()
.endItem()
.addNewItem()
.withNewMetadata()
.withName("s2")
.withLabels(new HashMap<String, String>() {{
put("label", "value")
put("label2", "value2")
}})
.endMetadata()
.endItem()
.addNewItem()
.withNewMetadata()
.withName("s3")
.endMetadata()
.endItem()
.build()).once()
mockServer.expect().get().withPath("/api/v1/namespaces/test/services").
andReturn(200, new ServiceListBuilder()
.addNewItem()
.withNewMetadata()
.withName("s1")
.withLabels(new HashMap<String, String>() {
{
put("label", "value")
}
})
.endMetadata()
.endItem()
.addNewItem()
.withNewMetadata()
.withName("s2")
.withLabels(new HashMap<String, String>() {
{
put("label", "value")
put("label2", "value2")
}
})
.endMetadata()
.endItem()
.addNewItem()
.withNewMetadata()
.withName("s3")
.endMetadata()
.endItem()
.build()).once()
and:
DiscoveryClient discoveryClient = new KubernetesDiscoveryClient(
mockClient, new KubernetesDiscoveryProperties(), {client -> client.services()})
DiscoveryClient discoveryClient = new KubernetesDiscoveryClient(
mockClient, new KubernetesDiscoveryProperties(), { client ->
client.services()
})
when:
List<String> instances = discoveryClient.getServices()
List<String> instances = discoveryClient.getServices()
then:
assertThat(instances).containsOnly("s1", "s2", "s3")
assertThat(instances).containsOnly("s1", "s2", "s3")
}
def "getServices should return only matching services when labels are applied to the client"() {
given:
// this is the URL that is created by the KubernetesClient when a a label named 'label'
// with a value of 'value' is specified
mockServer.expect().get().withPath("/api/v1/namespaces/test/services?labelSelector=label%3Dvalue").andReturn(200, new ServiceListBuilder()
.addNewItem()
.withNewMetadata()
.withName("s1")
.withLabels(new HashMap<String, String>() {{
put("label", "value")
}})
.endMetadata()
.endItem()
.addNewItem()
.withNewMetadata()
.withName("s2")
.withLabels(new HashMap<String, String>() {{
put("label", "value")
put("label2", "value2")
}})
.endMetadata()
.endItem()
.build()).once()
// this is the URL that is created by the KubernetesClient when a a label named 'label'
// with a value of 'value' is specified
mockServer.expect().get().
withPath("/api/v1/namespaces/test/services?labelSelector=label%3Dvalue").
andReturn(200, new ServiceListBuilder()
.addNewItem()
.withNewMetadata()
.withName("s1")
.withLabels(new HashMap<String, String>() {
{
put("label", "value")
}
})
.endMetadata()
.endItem()
.addNewItem()
.withNewMetadata()
.withName("s2")
.withLabels(new HashMap<String, String>() {
{
put("label", "value")
put("label2", "value2")
}
})
.endMetadata()
.endItem()
.build()).once()
and:
DiscoveryClient discoveryClient = new KubernetesDiscoveryClient(
mockClient,
new KubernetesDiscoveryProperties(), {client -> client.services().withLabels(["label": "value"])})
DiscoveryClient discoveryClient = new KubernetesDiscoveryClient(
mockClient,
new KubernetesDiscoveryProperties(), { client ->
client.services().withLabels(["label": "value"])
})
when:
List<String> instances = discoveryClient.getServices()
List<String> instances = discoveryClient.getServices()
then:
assertThat(instances).containsOnly("s1", "s2")
assertThat(instances).containsOnly("s1", "s2")
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,70 +12,74 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.discovery;
import org.junit.Test;
import java.util.HashMap;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class DefaultIsServicePortSecureResolverTest {
@Test
public void testPortNumbersOnly() {
final KubernetesDiscoveryProperties properties = new KubernetesDiscoveryProperties();
properties.getKnownSecurePorts().add(12345);
final DefaultIsServicePortSecureResolver sut = new DefaultIsServicePortSecureResolver(properties);
final DefaultIsServicePortSecureResolver sut = new DefaultIsServicePortSecureResolver(
properties);
assertFalse(sut.resolve(new DefaultIsServicePortSecureResolver.Input(null, "dummy")));
assertFalse(sut.resolve(new DefaultIsServicePortSecureResolver.Input(8080, "dummy")));
assertFalse(sut.resolve(new DefaultIsServicePortSecureResolver.Input(1234, "dummy")));
assertThat(
sut.resolve(new DefaultIsServicePortSecureResolver.Input(null, "dummy")))
.isFalse();
assertThat(
sut.resolve(new DefaultIsServicePortSecureResolver.Input(8080, "dummy")))
.isFalse();
assertThat(
sut.resolve(new DefaultIsServicePortSecureResolver.Input(1234, "dummy")))
.isFalse();
assertTrue(sut.resolve(new DefaultIsServicePortSecureResolver.Input(443, "dummy")));
assertTrue(sut.resolve(new DefaultIsServicePortSecureResolver.Input(8443, "dummy")));
assertTrue(sut.resolve(new DefaultIsServicePortSecureResolver.Input(12345, "dummy")));
assertThat(
sut.resolve(new DefaultIsServicePortSecureResolver.Input(443, "dummy")))
.isTrue();
assertThat(
sut.resolve(new DefaultIsServicePortSecureResolver.Input(8443, "dummy")))
.isTrue();
assertThat(
sut.resolve(new DefaultIsServicePortSecureResolver.Input(12345, "dummy")))
.isTrue();
}
@Test
public void testLabelsAndAnnotations() {
final DefaultIsServicePortSecureResolver sut
= new DefaultIsServicePortSecureResolver(new KubernetesDiscoveryProperties());
final DefaultIsServicePortSecureResolver sut = new DefaultIsServicePortSecureResolver(
new KubernetesDiscoveryProperties());
assertTrue(sut.resolve(new DefaultIsServicePortSecureResolver.Input(
8080,
"dummy",
new HashMap<String, String>() {{
put("secured", "true");
put("other", "value");
}},
new HashMap<>()))
);
assertTrue(sut.resolve(new DefaultIsServicePortSecureResolver.Input(
1234,
"dummy",
new HashMap<String, String>() {{
put("other", "value");
put("secured", "1");
}},
new HashMap<>()))
);
assertTrue(sut.resolve(new DefaultIsServicePortSecureResolver.Input(
4321,
"dummy",
new HashMap<>(),
new HashMap<String, String>() {{
put("other1", "value1");
put("secured", "yes");
put("other2", "value2");
}}))
);
assertThat(sut.resolve(new DefaultIsServicePortSecureResolver.Input(8080, "dummy",
new HashMap<String, String>() {
{
put("secured", "true");
put("other", "value");
}
}, new HashMap<>()))).isTrue();
assertThat(sut.resolve(new DefaultIsServicePortSecureResolver.Input(1234, "dummy",
new HashMap<String, String>() {
{
put("other", "value");
put("secured", "1");
}
}, new HashMap<>()))).isTrue();
assertThat(sut.resolve(new DefaultIsServicePortSecureResolver.Input(4321, "dummy",
new HashMap<>(), new HashMap<String, String>() {
{
put("other1", "value1");
put("secured", "yes");
put("other2", "value2");
}
}))).isTrue();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,7 +12,6 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.discovery;
@@ -20,6 +19,7 @@ package org.springframework.cloud.kubernetes.discovery;
import io.fabric8.kubernetes.client.KubernetesClient;
import org.junit.After;
import org.junit.Test;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
@@ -27,7 +27,7 @@ import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static org.junit.Assert.*;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
/**
@@ -47,31 +47,29 @@ public class KubernetesCatalogServicesWatchConfigurationTest {
@Test
public void kubernetesCatalogWatchDisabled() throws Exception {
setup("spring.cloud.kubernetes.discovery.catalog-services-watch.enabled=false");
assertFalse(context.containsBean("kubernetesCatalogWatch"));
assertThat(this.context.containsBean("kubernetesCatalogWatch")).isFalse();
}
@Test
public void kubernetesCatalogWatchWhenKubernetesDisabled() throws Exception {
setup("spring.cloud.kubernetes.enabled=false");
assertFalse(context.containsBean("kubernetesCatalogWatch"));
assertThat(this.context.containsBean("kubernetesCatalogWatch")).isFalse();
}
@Test
public void kubernetesCatalogWatchDefaultEnabled() throws Exception {
setup();
assertTrue(context.containsBean("kubernetesCatalogWatch"));
assertThat(this.context.containsBean("kubernetesCatalogWatch")).isTrue();
}
private void setup(String... env) {
this.context = new SpringApplicationBuilder(
PropertyPlaceholderAutoConfiguration.class,
KubernetesClientTestConfiguration.class,
KubernetesDiscoveryClientAutoConfiguration.class).web(WebApplicationType.NONE)
.properties(env).run();
PropertyPlaceholderAutoConfiguration.class,
KubernetesClientTestConfiguration.class,
KubernetesDiscoveryClientAutoConfiguration.class)
.web(WebApplicationType.NONE).properties(env).run();
}
@Configuration
static class KubernetesClientTestConfiguration {
@@ -80,6 +78,6 @@ public class KubernetesCatalogServicesWatchConfigurationTest {
return mock(KubernetesClient.class);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,12 +12,21 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.discovery;
import io.fabric8.kubernetes.api.model.*;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import io.fabric8.kubernetes.api.model.DoneableEndpoints;
import io.fabric8.kubernetes.api.model.EndpointAddress;
import io.fabric8.kubernetes.api.model.EndpointSubset;
import io.fabric8.kubernetes.api.model.Endpoints;
import io.fabric8.kubernetes.api.model.EndpointsList;
import io.fabric8.kubernetes.api.model.ObjectReference;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.dsl.MixedOperation;
import io.fabric8.kubernetes.client.dsl.Resource;
@@ -29,19 +38,15 @@ import org.mockito.Captor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.cloud.client.discovery.event.HeartbeatEvent;
import org.springframework.context.ApplicationEventPublisher;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import static java.util.Arrays.stream;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.*;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* @author Oleg Vyukov
@@ -66,52 +71,57 @@ public class KubernetesCatalogWatchTest {
@Before
public void setUp() throws Exception {
underTest.setApplicationEventPublisher(applicationEventPublisher);
this.underTest.setApplicationEventPublisher(this.applicationEventPublisher);
}
@Test
public void testRandomOrderChangePods() throws Exception {
when(endpointsOperation.list())
.thenReturn(createSingleEndpointEndpointListByPodName("api-pod", "other-pod"))
.thenReturn(createSingleEndpointEndpointListByPodName("other-pod", "api-pod"));
when(kubernetesClient.endpoints()).thenReturn(endpointsOperation);
when(this.endpointsOperation.list())
.thenReturn(
createSingleEndpointEndpointListByPodName("api-pod", "other-pod"))
.thenReturn(createSingleEndpointEndpointListByPodName("other-pod",
"api-pod"));
when(this.kubernetesClient.endpoints()).thenReturn(this.endpointsOperation);
underTest.catalogServicesWatch();
this.underTest.catalogServicesWatch();
// second execution on shuffleServices
underTest.catalogServicesWatch();
this.underTest.catalogServicesWatch();
verify(applicationEventPublisher).publishEvent(any(HeartbeatEvent.class));
verify(this.applicationEventPublisher).publishEvent(any(HeartbeatEvent.class));
}
@Test
public void testRandomOrderChangeServices() throws Exception {
when(endpointsOperation.list())
.thenReturn(createEndpointsListByServiceName("api-service", "other-service"))
.thenReturn(createEndpointsListByServiceName("other-service", "api-service"));
when(kubernetesClient.endpoints()).thenReturn(endpointsOperation);
when(this.endpointsOperation.list())
.thenReturn(
createEndpointsListByServiceName("api-service", "other-service"))
.thenReturn(
createEndpointsListByServiceName("other-service", "api-service"));
when(this.kubernetesClient.endpoints()).thenReturn(this.endpointsOperation);
underTest.catalogServicesWatch();
this.underTest.catalogServicesWatch();
// second execution on shuffleServices
underTest.catalogServicesWatch();
this.underTest.catalogServicesWatch();
verify(applicationEventPublisher).publishEvent(any(HeartbeatEvent.class));
verify(this.applicationEventPublisher).publishEvent(any(HeartbeatEvent.class));
}
@Test
public void testEventBody() throws Exception {
when(endpointsOperation.list())
.thenReturn(createSingleEndpointEndpointListByPodName("api-pod", "other-pod"));
when(kubernetesClient.endpoints()).thenReturn(endpointsOperation);
when(this.endpointsOperation.list()).thenReturn(
createSingleEndpointEndpointListByPodName("api-pod", "other-pod"));
when(this.kubernetesClient.endpoints()).thenReturn(this.endpointsOperation);
underTest.catalogServicesWatch();
this.underTest.catalogServicesWatch();
verify(applicationEventPublisher).publishEvent(heartbeatEventArgumentCaptor.capture());
verify(this.applicationEventPublisher)
.publishEvent(this.heartbeatEventArgumentCaptor.capture());
HeartbeatEvent event = heartbeatEventArgumentCaptor.getValue();
assertThat(event.getValue(), instanceOf(List.class));
HeartbeatEvent event = this.heartbeatEventArgumentCaptor.getValue();
assertThat(event.getValue()).isInstanceOf(List.class);
List<String> expectedPodsList = Arrays.asList("api-pod", "other-pod");
assertEquals(expectedPodsList, event.getValue());
assertThat(event.getValue()).isEqualTo(expectedPodsList);
}
@Test
@@ -119,14 +129,14 @@ public class KubernetesCatalogWatchTest {
EndpointsList endpoints = createSingleEndpointEndpointListWithoutSubsets();
when(endpointsOperation.list()).thenReturn(endpoints);
when(kubernetesClient.endpoints()).thenReturn(endpointsOperation);
when(this.endpointsOperation.list()).thenReturn(endpoints);
when(this.kubernetesClient.endpoints()).thenReturn(this.endpointsOperation);
underTest.catalogServicesWatch();
this.underTest.catalogServicesWatch();
// second execution on shuffleServices
underTest.catalogServicesWatch();
this.underTest.catalogServicesWatch();
verify(applicationEventPublisher).publishEvent(any(HeartbeatEvent.class));
verify(this.applicationEventPublisher).publishEvent(any(HeartbeatEvent.class));
}
@Test
@@ -135,37 +145,37 @@ public class KubernetesCatalogWatchTest {
EndpointsList endpoints = createSingleEndpointEndpointListByPodName("api-pod");
endpoints.getItems().get(0).getSubsets().get(0).setAddresses(null);
when(endpointsOperation.list()).thenReturn(endpoints);
when(kubernetesClient.endpoints()).thenReturn(endpointsOperation);
when(this.endpointsOperation.list()).thenReturn(endpoints);
when(this.kubernetesClient.endpoints()).thenReturn(this.endpointsOperation);
underTest.catalogServicesWatch();
this.underTest.catalogServicesWatch();
// second execution on shuffleServices
underTest.catalogServicesWatch();
this.underTest.catalogServicesWatch();
verify(applicationEventPublisher).publishEvent(any(HeartbeatEvent.class));
verify(this.applicationEventPublisher).publishEvent(any(HeartbeatEvent.class));
}
@Test
public void testEndpointsWithoutTargetRefs() {
EndpointsList endpoints = createSingleEndpointEndpointListByPodName("api-pod");
endpoints.getItems().get(0).getSubsets().get(0).getAddresses().get(0).setTargetRef(null);
endpoints.getItems().get(0).getSubsets().get(0).getAddresses().get(0)
.setTargetRef(null);
when(endpointsOperation.list()).thenReturn(endpoints);
when(kubernetesClient.endpoints()).thenReturn(endpointsOperation);
when(this.endpointsOperation.list()).thenReturn(endpoints);
when(this.kubernetesClient.endpoints()).thenReturn(this.endpointsOperation);
underTest.catalogServicesWatch();
this.underTest.catalogServicesWatch();
// second execution on shuffleServices
underTest.catalogServicesWatch();
this.underTest.catalogServicesWatch();
verify(applicationEventPublisher).publishEvent(any(HeartbeatEvent.class));
verify(this.applicationEventPublisher).publishEvent(any(HeartbeatEvent.class));
}
private EndpointsList createEndpointsListByServiceName(String... serviceNames) {
List<Endpoints> endpoints = stream(serviceNames)
.map(s -> createEndpointsByPodName(s + "-singlePodUniqueId"))
.collect(Collectors.toList());
.map(s -> createEndpointsByPodName(s + "-singlePodUniqueId"))
.collect(Collectors.toList());
EndpointsList endpointsList = new EndpointsList();
endpointsList.setItems(endpoints);
@@ -189,14 +199,12 @@ public class KubernetesCatalogWatchTest {
return endpointsList;
}
private Endpoints createEndpointsByPodName(String podName) {
Endpoints endpoints = new Endpoints();
endpoints.setSubsets(createSubsetsByPodName(podName));
return endpoints;
}
private List<EndpointSubset> createSubsetsByPodName(String... names) {
EndpointSubset endpointSubset = new EndpointSubset();
endpointSubset.setAddresses(createEndpointAddressByPodNames(names));
@@ -213,5 +221,4 @@ public class KubernetesCatalogWatchTest {
}).collect(Collectors.toList());
}
}

View File

@@ -1,3 +1,19 @@
/*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.discovery;
import io.fabric8.kubernetes.client.KubernetesClient;
@@ -29,35 +45,35 @@ public class KubernetesDiscoveryClientAutoConfigurationPropertiesTests {
@Test
public void kubernetesDiscoveryDisabled() throws Exception {
setup("spring.cloud.kubernetes.discovery.enabled=false","spring.cloud.kubernetes.discovery.catalog-services-watch.enabled=false");
assertThat(context.getBeanNamesForType(KubernetesDiscoveryClient.class))
.isEmpty();
setup("spring.cloud.kubernetes.discovery.enabled=false",
"spring.cloud.kubernetes.discovery.catalog-services-watch.enabled=false");
assertThat(this.context.getBeanNamesForType(KubernetesDiscoveryClient.class))
.isEmpty();
}
@Test
public void kubernetesDiscoveryWhenKubernetesDisabled() throws Exception {
setup("spring.cloud.kubernetes.enabled=false");
assertThat(context.getBeanNamesForType(KubernetesDiscoveryClient.class))
.isEmpty();
assertThat(this.context.getBeanNamesForType(KubernetesDiscoveryClient.class))
.isEmpty();
}
@Test
public void kubernetesDiscoveryDefaultEnabled() throws Exception {
setup("spring.cloud.kubernetes.enabled=true");
assertThat(context.getBeanNamesForType(KubernetesDiscoveryClient.class))
.hasSize(1);
assertThat(this.context.getBeanNamesForType(KubernetesDiscoveryClient.class))
.hasSize(1);
}
private void setup(String... env) {
this.context = new SpringApplicationBuilder(
PropertyPlaceholderAutoConfiguration.class,
KubernetesClientTestConfiguration.class,
KubernetesDiscoveryClientAutoConfiguration.class).web(org.springframework.boot.WebApplicationType.NONE)
.properties(env).run();
PropertyPlaceholderAutoConfiguration.class,
KubernetesClientTestConfiguration.class,
KubernetesDiscoveryClientAutoConfiguration.class)
.web(org.springframework.boot.WebApplicationType.NONE)
.properties(env).run();
}
@Configuration
static class KubernetesClientTestConfiguration {
@@ -66,6 +82,6 @@ public class KubernetesDiscoveryClientAutoConfigurationPropertiesTests {
return mock(KubernetesClient.class);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,7 +12,6 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.discovery;
@@ -40,15 +39,18 @@ public class KubernetesDiscoveryClientAutoConfigurationTests {
@Test
public void kubernetesDiscoveryClientCreated() {
assertThat(discoveryClient).isNotNull()
.isInstanceOf(CompositeDiscoveryClient.class);
assertThat(this.discoveryClient).isNotNull()
.isInstanceOf(CompositeDiscoveryClient.class);
CompositeDiscoveryClient composite = (CompositeDiscoveryClient) discoveryClient;
CompositeDiscoveryClient composite = (CompositeDiscoveryClient) this.discoveryClient;
assertThat(composite.getDiscoveryClients().stream()
.anyMatch(dc -> dc instanceof KubernetesDiscoveryClient)).isTrue();
.anyMatch(dc -> dc instanceof KubernetesDiscoveryClient)).isTrue();
}
@SpringBootConfiguration
@EnableAutoConfiguration
protected static class TestConfig {}
protected static class TestConfig {
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,8 +12,8 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.discovery;
import java.util.Collections;
@@ -44,6 +44,7 @@ import static org.mockito.Mockito.verify;
* @author Zhanwei Wang
*/
public class KubernetesDiscoveryClientConfigClientBootstrapConfigurationTests {
private AnnotationConfigApplicationContext context;
@After
@@ -59,17 +60,15 @@ public class KubernetesDiscoveryClientConfigClientBootstrapConfigurationTests {
@Test
public void onWhenRequested() throws Exception {
setup("server.port=7000", "spring.cloud.config.discovery.enabled=true",
"spring.cloud.kubernetes.discovery.enabled:true",
"spring.cloud.kubernetes.enabled:true",
"spring.application.name:test",
"spring.cloud.config.discovery.service-id:configserver");
assertEquals( 1, this.context.getParent()
.getBeanNamesForType(DiscoveryClient.class).length);
DiscoveryClient client = this.context.getParent().getBean(
DiscoveryClient.class);
"spring.cloud.kubernetes.discovery.enabled:true",
"spring.cloud.kubernetes.enabled:true", "spring.application.name:test",
"spring.cloud.config.discovery.service-id:configserver");
assertEquals(1, this.context.getParent()
.getBeanNamesForType(DiscoveryClient.class).length);
DiscoveryClient client = this.context.getParent().getBean(DiscoveryClient.class);
verify(client, atLeast(2)).getInstances("configserver");
ConfigClientProperties locator = this.context
.getBean(ConfigClientProperties.class);
.getBean(ConfigClientProperties.class);
assertEquals("http://fake:8888/", locator.getUri()[0]);
}
@@ -77,16 +76,16 @@ public class KubernetesDiscoveryClientConfigClientBootstrapConfigurationTests {
AnnotationConfigApplicationContext parent = new AnnotationConfigApplicationContext();
TestPropertyValues.of(env).applyTo(parent);
parent.register(UtilAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class, EnvironmentKnobbler.class,
KubernetesDiscoveryClientConfigClientBootstrapConfiguration.class,
DiscoveryClientConfigServiceBootstrapConfiguration.class,
ConfigClientProperties.class);
PropertyPlaceholderAutoConfiguration.class, EnvironmentKnobbler.class,
KubernetesDiscoveryClientConfigClientBootstrapConfiguration.class,
DiscoveryClientConfigServiceBootstrapConfiguration.class,
ConfigClientProperties.class);
parent.refresh();
this.context = new AnnotationConfigApplicationContext();
this.context.setParent(parent);
this.context.register(PropertyPlaceholderAutoConfiguration.class,
KubernetesAutoConfiguration.class,
KubernetesDiscoveryClientAutoConfiguration.class);
KubernetesAutoConfiguration.class,
KubernetesDiscoveryClientAutoConfiguration.class);
this.context.refresh();
}
@@ -97,10 +96,12 @@ public class KubernetesDiscoveryClientConfigClientBootstrapConfigurationTests {
public KubernetesDiscoveryClient kubernetesDiscoveryClient() {
KubernetesDiscoveryClient client = mock(KubernetesDiscoveryClient.class);
ServiceInstance instance = new DefaultServiceInstance("configserver1",
"configserver", "fake", 8888, false);
"configserver", "fake", 8888, false);
given(client.getInstances("configserver"))
.willReturn(Collections.singletonList(instance));
.willReturn(Collections.singletonList(instance));
return client;
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,10 +12,14 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.discovery;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import io.fabric8.kubernetes.api.model.DoneableEndpoints;
import io.fabric8.kubernetes.api.model.DoneableService;
import io.fabric8.kubernetes.api.model.EndpointPort;
@@ -38,11 +42,8 @@ import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.cloud.client.ServiceInstance;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.cloud.client.ServiceInstance;
import static java.util.stream.Collectors.toList;
import static org.assertj.core.api.Assertions.assertThat;
@@ -83,26 +84,28 @@ public class KubernetesDiscoveryClientFilterMetadataTest {
public void testAllExtraMetadataDisabled() {
final String serviceId = "s";
when(properties.getMetadata()).thenReturn(metadata);
when(metadata.isAddLabels()).thenReturn(false);
when(metadata.isAddAnnotations()).thenReturn(false);
when(metadata.isAddPorts()).thenReturn(false);
when(this.properties.getMetadata()).thenReturn(this.metadata);
when(this.metadata.isAddLabels()).thenReturn(false);
when(this.metadata.isAddAnnotations()).thenReturn(false);
when(this.metadata.isAddPorts()).thenReturn(false);
setupServiceWithLabelsAndAnnotationsAndPorts(
serviceId,
new HashMap<String, String>() {{
put("l1", "lab");
}},
new HashMap<String, String>() {{
put("l1", "lab");
}},
new HashMap<Integer, String>() {{
put(80, "http");
put(5555, "");
}}
);
setupServiceWithLabelsAndAnnotationsAndPorts(serviceId,
new HashMap<String, String>() {
{
put("l1", "lab");
}
}, new HashMap<String, String>() {
{
put("l1", "lab");
}
}, new HashMap<Integer, String>() {
{
put(80, "http");
put(5555, "");
}
});
final List<ServiceInstance> instances = underTest.getInstances(serviceId);
final List<ServiceInstance> instances = this.underTest.getInstances(serviceId);
assertThat(instances).hasSize(1);
assertThat(instances.get(0).getMetadata()).isEmpty();
}
@@ -111,144 +114,159 @@ public class KubernetesDiscoveryClientFilterMetadataTest {
public void testLabelsEnabled() {
final String serviceId = "s";
when(properties.getMetadata()).thenReturn(metadata);
when(metadata.isAddLabels()).thenReturn(true);
when(metadata.isAddAnnotations()).thenReturn(false);
when(metadata.isAddPorts()).thenReturn(false);
when(this.properties.getMetadata()).thenReturn(this.metadata);
when(this.metadata.isAddLabels()).thenReturn(true);
when(this.metadata.isAddAnnotations()).thenReturn(false);
when(this.metadata.isAddPorts()).thenReturn(false);
setupServiceWithLabelsAndAnnotationsAndPorts(
serviceId,
new HashMap<String, String>() {{
put("l1", "v1");
put("l2", "v2");
}},
new HashMap<String, String>() {{
put("l1", "lab");
}},
new HashMap<Integer, String>() {{
put(80, "http");
put(5555, "");
}}
);
setupServiceWithLabelsAndAnnotationsAndPorts(serviceId,
new HashMap<String, String>() {
{
put("l1", "v1");
put("l2", "v2");
}
}, new HashMap<String, String>() {
{
put("l1", "lab");
}
}, new HashMap<Integer, String>() {
{
put(80, "http");
put(5555, "");
}
});
final List<ServiceInstance> instances = underTest.getInstances(serviceId);
final List<ServiceInstance> instances = this.underTest.getInstances(serviceId);
assertThat(instances).hasSize(1);
assertThat(instances.get(0).getMetadata()).containsOnly(entry("l1", "v1"), entry("l2", "v2"));
assertThat(instances.get(0).getMetadata()).containsOnly(entry("l1", "v1"),
entry("l2", "v2"));
}
@Test
public void testLabelsEnabledWithPrefix() {
final String serviceId = "s";
when(properties.getMetadata()).thenReturn(metadata);
when(metadata.isAddLabels()).thenReturn(true);
when(metadata.getLabelsPrefix()).thenReturn("l_");
when(metadata.isAddAnnotations()).thenReturn(false);
when(metadata.isAddPorts()).thenReturn(false);
when(this.properties.getMetadata()).thenReturn(this.metadata);
when(this.metadata.isAddLabels()).thenReturn(true);
when(this.metadata.getLabelsPrefix()).thenReturn("l_");
when(this.metadata.isAddAnnotations()).thenReturn(false);
when(this.metadata.isAddPorts()).thenReturn(false);
setupServiceWithLabelsAndAnnotationsAndPorts(
serviceId,
new HashMap<String, String>() {{
put("l1", "v1");
put("l2", "v2");
}},
new HashMap<String, String>() {{
put("l1", "lab");
}},
new HashMap<Integer, String>() {{
put(80, "http");
put(5555, "");
}});
setupServiceWithLabelsAndAnnotationsAndPorts(serviceId,
new HashMap<String, String>() {
{
put("l1", "v1");
put("l2", "v2");
}
}, new HashMap<String, String>() {
{
put("l1", "lab");
}
}, new HashMap<Integer, String>() {
{
put(80, "http");
put(5555, "");
}
});
final List<ServiceInstance> instances = underTest.getInstances(serviceId);
final List<ServiceInstance> instances = this.underTest.getInstances(serviceId);
assertThat(instances).hasSize(1);
assertThat(instances.get(0).getMetadata()).containsOnly(entry("l_l1", "v1"), entry("l_l2", "v2"));
assertThat(instances.get(0).getMetadata()).containsOnly(entry("l_l1", "v1"),
entry("l_l2", "v2"));
}
@Test
public void testAnnotationsEnabled() {
final String serviceId = "s";
when(properties.getMetadata()).thenReturn(metadata);
when(metadata.isAddLabels()).thenReturn(false);
when(metadata.isAddAnnotations()).thenReturn(true);
when(metadata.isAddPorts()).thenReturn(false);
when(this.properties.getMetadata()).thenReturn(this.metadata);
when(this.metadata.isAddLabels()).thenReturn(false);
when(this.metadata.isAddAnnotations()).thenReturn(true);
when(this.metadata.isAddPorts()).thenReturn(false);
setupServiceWithLabelsAndAnnotationsAndPorts(
serviceId,
new HashMap<String, String>() {{
put("l1", "v1");
}},
new HashMap<String, String>() {{
put("a1", "v1");
put("a2", "v2");
}},
new HashMap<Integer, String>() {{
put(80, "http");
put(5555, "");
}}
);
setupServiceWithLabelsAndAnnotationsAndPorts(serviceId,
new HashMap<String, String>() {
{
put("l1", "v1");
}
}, new HashMap<String, String>() {
{
put("a1", "v1");
put("a2", "v2");
}
}, new HashMap<Integer, String>() {
{
put(80, "http");
put(5555, "");
}
});
final List<ServiceInstance> instances = underTest.getInstances(serviceId);
final List<ServiceInstance> instances = this.underTest.getInstances(serviceId);
assertThat(instances).hasSize(1);
assertThat(instances.get(0).getMetadata()).containsOnly(entry("a1", "v1"), entry("a2", "v2"));
assertThat(instances.get(0).getMetadata()).containsOnly(entry("a1", "v1"),
entry("a2", "v2"));
}
@Test
public void testAnnotationsEnabledWithPrefix() {
final String serviceId = "s";
when(properties.getMetadata()).thenReturn(metadata);
when(metadata.isAddLabels()).thenReturn(false);
when(metadata.isAddAnnotations()).thenReturn(true);
when(metadata.getAnnotationsPrefix()).thenReturn("a_");
when(metadata.isAddPorts()).thenReturn(false);
when(this.properties.getMetadata()).thenReturn(this.metadata);
when(this.metadata.isAddLabels()).thenReturn(false);
when(this.metadata.isAddAnnotations()).thenReturn(true);
when(this.metadata.getAnnotationsPrefix()).thenReturn("a_");
when(this.metadata.isAddPorts()).thenReturn(false);
setupServiceWithLabelsAndAnnotationsAndPorts(
serviceId,
new HashMap<String, String>() {{
put("l1", "v1");
}},
new HashMap<String, String>() {{
put("a1", "v1");
put("a2", "v2");
}},
new HashMap<Integer, String>() {{
put(80, "http");
put(5555, "");
}}
);
setupServiceWithLabelsAndAnnotationsAndPorts(serviceId,
new HashMap<String, String>() {
{
put("l1", "v1");
}
}, new HashMap<String, String>() {
{
put("a1", "v1");
put("a2", "v2");
}
}, new HashMap<Integer, String>() {
{
put(80, "http");
put(5555, "");
}
});
final List<ServiceInstance> instances = underTest.getInstances(serviceId);
final List<ServiceInstance> instances = this.underTest.getInstances(serviceId);
assertThat(instances).hasSize(1);
assertThat(instances.get(0).getMetadata()).containsOnly(entry("a_a1", "v1"), entry("a_a2", "v2"));
assertThat(instances.get(0).getMetadata()).containsOnly(entry("a_a1", "v1"),
entry("a_a2", "v2"));
}
@Test
public void testPortsEnabled() {
final String serviceId = "s";
when(properties.getMetadata()).thenReturn(metadata);
when(metadata.isAddLabels()).thenReturn(false);
when(metadata.isAddAnnotations()).thenReturn(false);
when(metadata.isAddPorts()).thenReturn(true);
when(this.properties.getMetadata()).thenReturn(this.metadata);
when(this.metadata.isAddLabels()).thenReturn(false);
when(this.metadata.isAddAnnotations()).thenReturn(false);
when(this.metadata.isAddPorts()).thenReturn(true);
setupServiceWithLabelsAndAnnotationsAndPorts(
serviceId,
new HashMap<String, String>() {{
put("l1", "v1");
}},
new HashMap<String, String>() {{
put("a1", "v1");
put("a2", "v2");
}},
new HashMap<Integer, String>() {{
put(80, "http");
put(5555, "");
}}
);
setupServiceWithLabelsAndAnnotationsAndPorts(serviceId,
new HashMap<String, String>() {
{
put("l1", "v1");
}
}, new HashMap<String, String>() {
{
put("a1", "v1");
put("a2", "v2");
}
}, new HashMap<Integer, String>() {
{
put(80, "http");
put(5555, "");
}
});
final List<ServiceInstance> instances = underTest.getInstances(serviceId);
final List<ServiceInstance> instances = this.underTest.getInstances(serviceId);
assertThat(instances).hasSize(1);
assertThat(instances.get(0).getMetadata()).containsOnly(entry("http", "80"));
}
@@ -257,28 +275,30 @@ public class KubernetesDiscoveryClientFilterMetadataTest {
public void testPortsEnabledWithPrefix() {
final String serviceId = "s";
when(properties.getMetadata()).thenReturn(metadata);
when(metadata.isAddLabels()).thenReturn(false);
when(metadata.isAddAnnotations()).thenReturn(false);
when(metadata.isAddPorts()).thenReturn(true);
when(metadata.getPortsPrefix()).thenReturn("p_");
when(this.properties.getMetadata()).thenReturn(this.metadata);
when(this.metadata.isAddLabels()).thenReturn(false);
when(this.metadata.isAddAnnotations()).thenReturn(false);
when(this.metadata.isAddPorts()).thenReturn(true);
when(this.metadata.getPortsPrefix()).thenReturn("p_");
setupServiceWithLabelsAndAnnotationsAndPorts(
serviceId,
new HashMap<String, String>() {{
put("l1", "v1");
}},
new HashMap<String, String>() {{
put("a1", "v1");
put("a2", "v2");
}},
new HashMap<Integer, String>() {{
put(80, "http");
put(5555, "");
}}
);
setupServiceWithLabelsAndAnnotationsAndPorts(serviceId,
new HashMap<String, String>() {
{
put("l1", "v1");
}
}, new HashMap<String, String>() {
{
put("a1", "v1");
put("a2", "v2");
}
}, new HashMap<Integer, String>() {
{
put(80, "http");
put(5555, "");
}
});
final List<ServiceInstance> instances = underTest.getInstances(serviceId);
final List<ServiceInstance> instances = this.underTest.getInstances(serviceId);
assertThat(instances).hasSize(1);
assertThat(instances.get(0).getMetadata()).containsOnly(entry("p_http", "80"));
}
@@ -287,90 +307,77 @@ public class KubernetesDiscoveryClientFilterMetadataTest {
public void testLabelsAndAnnotationsAndPortsEnabledWithPrefix() {
final String serviceId = "s";
when(properties.getMetadata()).thenReturn(metadata);
when(metadata.isAddLabels()).thenReturn(true);
when(metadata.getLabelsPrefix()).thenReturn("l_");
when(metadata.isAddAnnotations()).thenReturn(true);
when(metadata.getAnnotationsPrefix()).thenReturn("a_");
when(metadata.isAddPorts()).thenReturn(true);
when(metadata.getPortsPrefix()).thenReturn("p_");
when(this.properties.getMetadata()).thenReturn(this.metadata);
when(this.metadata.isAddLabels()).thenReturn(true);
when(this.metadata.getLabelsPrefix()).thenReturn("l_");
when(this.metadata.isAddAnnotations()).thenReturn(true);
when(this.metadata.getAnnotationsPrefix()).thenReturn("a_");
when(this.metadata.isAddPorts()).thenReturn(true);
when(this.metadata.getPortsPrefix()).thenReturn("p_");
setupServiceWithLabelsAndAnnotationsAndPorts(
serviceId,
new HashMap<String, String>() {{
put("l1", "la1");
}},
new HashMap<String, String>() {{
put("a1", "an1");
put("a2", "an2");
}},
new HashMap<Integer, String>() {{
put(80, "http");
put(5555, "");
}}
);
setupServiceWithLabelsAndAnnotationsAndPorts(serviceId,
new HashMap<String, String>() {
{
put("l1", "la1");
}
}, new HashMap<String, String>() {
{
put("a1", "an1");
put("a2", "an2");
}
}, new HashMap<Integer, String>() {
{
put(80, "http");
put(5555, "");
}
});
final List<ServiceInstance> instances = underTest.getInstances(serviceId);
final List<ServiceInstance> instances = this.underTest.getInstances(serviceId);
assertThat(instances).hasSize(1);
assertThat(instances.get(0).getMetadata()).containsOnly(
entry("a_a1", "an1"), entry("a_a2", "an2"), entry("l_l1", "la1"), entry("p_http", "80"));
assertThat(instances.get(0).getMetadata()).containsOnly(entry("a_a1", "an1"),
entry("a_a2", "an2"), entry("l_l1", "la1"), entry("p_http", "80"));
}
private void setupServiceWithLabelsAndAnnotationsAndPorts(String serviceId,
Map<String, String> labels, Map<String, String> annotations, Map<Integer, String> ports) {
final Service service =
new ServiceBuilder()
.withNewMetadata()
.withLabels(labels)
.withAnnotations(annotations)
.endMetadata()
.withNewSpec()
.withPorts(getServicePorts(ports))
.endSpec()
.build();
when(serviceOperation.withName(serviceId)).thenReturn(serviceResource);
when(serviceResource.get()).thenReturn(service);
when(kubernetesClient.services()).thenReturn(serviceOperation);
Map<String, String> labels, Map<String, String> annotations,
Map<Integer, String> ports) {
final Service service = new ServiceBuilder().withNewMetadata().withLabels(labels)
.withAnnotations(annotations).endMetadata().withNewSpec()
.withPorts(getServicePorts(ports)).endSpec().build();
when(this.serviceOperation.withName(serviceId)).thenReturn(this.serviceResource);
when(this.serviceResource.get()).thenReturn(service);
when(this.kubernetesClient.services()).thenReturn(this.serviceOperation);
final Endpoints endpoints =
new EndpointsBuilder()
.addNewSubset()
.addAllToPorts(getEndpointPorts(ports))
.addNewAddress()
.endAddress()
.endSubset()
.build();
final Endpoints endpoints = new EndpointsBuilder().addNewSubset()
.addAllToPorts(getEndpointPorts(ports)).addNewAddress().endAddress()
.endSubset().build();
when(endpointsResource.get()).thenReturn(endpoints);
when(endpointsOperation.withName(serviceId)).thenReturn(endpointsResource);
when(kubernetesClient.endpoints()).thenReturn(endpointsOperation);
when(this.endpointsResource.get()).thenReturn(endpoints);
when(this.endpointsOperation.withName(serviceId))
.thenReturn(this.endpointsResource);
when(this.kubernetesClient.endpoints()).thenReturn(this.endpointsOperation);
}
private List<ServicePort> getServicePorts(Map<Integer, String> ports) {
return ports.entrySet().stream()
.map(e -> {
ServicePortBuilder servicePortBuilder = new ServicePortBuilder();
servicePortBuilder.withPort(e.getKey());
if (!Strings.isNullOrEmpty(e.getValue())) {
servicePortBuilder.withName(e.getValue());
}
return servicePortBuilder.build();
})
.collect(toList());
return ports.entrySet().stream().map(e -> {
ServicePortBuilder servicePortBuilder = new ServicePortBuilder();
servicePortBuilder.withPort(e.getKey());
if (!Strings.isNullOrEmpty(e.getValue())) {
servicePortBuilder.withName(e.getValue());
}
return servicePortBuilder.build();
}).collect(toList());
}
private List<EndpointPort> getEndpointPorts(Map<Integer, String> ports) {
return ports.entrySet().stream()
.map(e -> {
EndpointPortBuilder endpointPortBuilder = new EndpointPortBuilder();
endpointPortBuilder.withPort(e.getKey());
if (!Strings.isNullOrEmpty(e.getValue())) {
endpointPortBuilder.withName(e.getValue());
}
return endpointPortBuilder.build();
})
.collect(toList());
return ports.entrySet().stream().map(e -> {
EndpointPortBuilder endpointPortBuilder = new EndpointPortBuilder();
endpointPortBuilder.withPort(e.getKey());
if (!Strings.isNullOrEmpty(e.getValue())) {
endpointPortBuilder.withName(e.getValue());
}
return endpointPortBuilder.build();
}).collect(toList());
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* 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.
@@ -12,8 +12,8 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.kubernetes.discovery;
import java.util.ArrayList;
@@ -30,13 +30,10 @@ import io.fabric8.kubernetes.client.dsl.ServiceResource;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
@@ -57,7 +54,8 @@ public class KubernetesDiscoveryClientFilterTest {
@Before
public void setUp() {
underTest = new KubernetesDiscoveryClient(kubernetesClient, properties, kubernetesClientServicesFunction);
this.underTest = new KubernetesDiscoveryClient(this.kubernetesClient,
this.properties, this.kubernetesClientServicesFunction);
}
@Test
@@ -74,22 +72,23 @@ public class KubernetesDiscoveryClientFilterTest {
ServiceList serviceList = new ServiceList();
serviceList.setItems(services);
when(serviceOperation.list())
.thenReturn(serviceList);
when(kubernetesClient.services()).thenReturn(serviceOperation);
when(this.serviceOperation.list()).thenReturn(serviceList);
when(this.kubernetesClient.services()).thenReturn(this.serviceOperation);
when(properties.getFilter()).thenReturn("metadata.additionalProperties['spring-boot']");
when(this.properties.getFilter())
.thenReturn("metadata.additionalProperties['spring-boot']");
List<String> filteredServices = underTest.getServices();
List<String> filteredServices = this.underTest.getServices();
System.out.println("Filtered Services: " + filteredServices);
assertEquals(springBootServiceNames, filteredServices);
assertThat(filteredServices).isEqualTo(springBootServiceNames);
}
@Test
public void testFilteredServicesByPrefix() {
List<String> springBootServiceNames = Arrays.asList("serviceA", "serviceB", "serviceC");
List<String> springBootServiceNames = Arrays.asList("serviceA", "serviceB",
"serviceC");
List<Service> services = createSpringBootServiceByName(springBootServiceNames);
// Add non spring boot service
@@ -101,36 +100,36 @@ public class KubernetesDiscoveryClientFilterTest {
ServiceList serviceList = new ServiceList();
serviceList.setItems(services);
when(serviceOperation.list())
.thenReturn(serviceList);
when(kubernetesClient.services()).thenReturn(serviceOperation);
when(this.serviceOperation.list()).thenReturn(serviceList);
when(this.kubernetesClient.services()).thenReturn(this.serviceOperation);
when(properties.getFilter()).thenReturn("metadata.name.startsWith('service')");
when(this.properties.getFilter())
.thenReturn("metadata.name.startsWith('service')");
List<String> filteredServices = underTest.getServices();
List<String> filteredServices = this.underTest.getServices();
System.out.println("Filtered Services: " + filteredServices);
assertEquals(springBootServiceNames, filteredServices);
assertThat(filteredServices).isEqualTo(springBootServiceNames);
}
@Test
public void testNoExpression() {
List<String> springBootServiceNames = Arrays.asList("serviceA", "serviceB", "serviceC");
List<String> springBootServiceNames = Arrays.asList("serviceA", "serviceB",
"serviceC");
List<Service> services = createSpringBootServiceByName(springBootServiceNames);
ServiceList serviceList = new ServiceList();
serviceList.setItems(services);
when(serviceOperation.list())
.thenReturn(serviceList);
when(kubernetesClient.services()).thenReturn(serviceOperation);
when(this.serviceOperation.list()).thenReturn(serviceList);
when(this.kubernetesClient.services()).thenReturn(this.serviceOperation);
when(properties.getFilter()).thenReturn("");
when(this.properties.getFilter()).thenReturn("");
List<String> filteredServices = underTest.getServices();
List<String> filteredServices = this.underTest.getServices();
System.out.println("Filtered Services: " + filteredServices);
assertEquals(springBootServiceNames, filteredServices);
assertThat(filteredServices).isEqualTo(springBootServiceNames);
}

View File

@@ -16,58 +16,58 @@
~
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>kubernetes-circuitbreaker-ribbon-example</artifactId>
<version>1.0.1.BUILD-SNAPSHOT</version>
</parent>
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>kubernetes-circuitbreaker-ribbon-example</artifactId>
<version>1.0.1.BUILD-SNAPSHOT</version>
</parent>
<artifactId>greeting-service</artifactId>
<name>Circuit Breaker &amp; Load Balancer :: Greeting Service</name>
<description>Circuit Breaker &amp; Load Balancer :: Greeting Service</description>
<artifactId>greeting-service</artifactId>
<name>Circuit Breaker &amp; Load Balancer :: Greeting Service</name>
<description>Circuit Breaker &amp; Load Balancer :: Greeting Service</description>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>${maven-deploy-plugin.version}</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>${maven-deploy-plugin.version}</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes-ribbon</artifactId>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</dependency>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes-ribbon</artifactId>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</dependency>
</dependencies>
</dependencies>
</project>

View File

@@ -35,18 +35,20 @@ public class GreetingController {
}
/**
* Endpoint to get a greeting. This endpoint uses a name server to get a name for the greeting.
* Endpoint to get a greeting. This endpoint uses a name server to get a name for the
* greeting.
*
* Request to the name service is guarded with a circuit breaker. Therefore if a name service is not available or is too
* slow to response fallback name is used.
* Request to the name service is guarded with a circuit breaker. Therefore if a name
* service is not available or is too slow to response fallback name is used.
*
* Delay parameter can me used to make name service response slower.
*
* @param delay Milliseconds for how long the response from name service should be delayed.
* @param delay Milliseconds for how long the response from name service should be
* delayed.
* @return Greeting string.
*/
@RequestMapping("/greeting")
public String getGreeting(@RequestParam(value = "delay", defaultValue = "0") int delay) {
public String getGreeting(
@RequestParam(value = "delay", defaultValue = "0") int delay) {
return String.format("Hello from %s!", this.nameService.getName(delay));
}

View File

@@ -37,10 +37,10 @@ public class NameService {
}
@HystrixCommand(fallbackMethod = "getFallbackName", commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000")
})
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000") })
public String getName(int delay) {
return this.restTemplate.getForObject(String.format("http://name-service/name?delay=%d", delay), String.class);
return this.restTemplate.getForObject(
String.format("http://name-service/name?delay=%d", delay), String.class);
}
private String getFallbackName(int delay) {

View File

@@ -36,10 +36,10 @@ public class RibbonConfiguration {
IClientConfig ribbonClientConfig;
/**
* PingUrl will ping a URL to check the status of each server.
* Say Hello has, as youll recall, a method mapped to the /path; that means that Ribbon will get an HTTP 200 response when it pings a running Backend Server
*
* @param config Client configuration
* PingUrl will ping a URL to check the status of each server. Say Hello has, as
* youll recall, a method mapped to the /path; that means that Ribbon will get an
* HTTP 200 response when it pings a running Backend Server
* @param config Client configuration
* @return The URL to be used for the Ping
*/
@Bean
@@ -48,14 +48,16 @@ public class RibbonConfiguration {
}
/**
* AvailabilityFilteringRule will use Ribbons built-in circuit breaker functionality to filter out any servers in an “open-circuit” state:
* if a ping fails to connect to a given server, or if it gets a read failure for the server, Ribbon will consider that server “dead” until it begins to respond normally.
*
* @param config Client configuration
* AvailabilityFilteringRule will use Ribbons built-in circuit breaker functionality
* to filter out any servers in an “open-circuit” state: if a ping fails to connect to
* a given server, or if it gets a read failure for the server, Ribbon will consider
* that server “dead” until it begins to respond normally.
* @param config Client configuration
* @return The Load Balancer rule
*/
@Bean
public IRule ribbonRule(IClientConfig config) {
return new AvailabilityFilteringRule();
}
}

View File

@@ -10,7 +10,7 @@ backend:
eureka:
enabled: false
client:
enabled: true
enabled: true
ServerListRefreshInterval: 5000
hystrix.command.BackendCall.execution.isolation.thread.timeoutInMilliseconds: 5000

Some files were not shown because too many files have changed in this diff Show More