Initial commit

This commit is contained in:
spencergibb
2023-09-19 13:10:02 -04:00
parent 2e4d529f30
commit 8a57c69c88
1303 changed files with 8661 additions and 95772 deletions

View File

@@ -1,18 +0,0 @@
# EditorConfig is awesome: https://EditorConfig.org
# top-most EditorConfig file
root = 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

138
.flattened-pom.xml Normal file
View File

@@ -0,0 +1,138 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2013-2020 the original author or authors.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
~
-->
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-build</artifactId>
<version>4.0.4-SNAPSHOT</version>
<relativePath></relativePath>
</parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes</artifactId>
<version>3.0.4-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Spring Cloud Kubernetes</name>
<description>Spring Cloud parent pom, managing plugins and dependencies for Spring
Cloud projects</description>
<url>https://cloud.spring.io</url>
<inceptionYear>2017</inceptionYear>
<organization>
<name>Pivotal Software, Inc.</name>
<url>https://www.spring.io</url>
</organization>
<licenses>
<license>
<name>Apache License, Version 2.0</name>
<url>https://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
<developers>
<developer>
<id>dsyer</id>
<name>Dave Syer</name>
<email>dsyer at pivotal.io</email>
<organization>Pivotal Software, Inc.</organization>
<organizationUrl>https://www.spring.io</organizationUrl>
<roles>
<role>lead</role>
</roles>
</developer>
<developer>
<id>sgibb</id>
<name>Spencer Gibb</name>
<email>sgibb at pivotal.io</email>
<organization>Pivotal Software, Inc.</organization>
<organizationUrl>https://www.spring.io</organizationUrl>
<roles>
<role>lead</role>
</roles>
</developer>
<developer>
<id>mgrzejszczak</id>
<name>Marcin Grzejszczak</name>
<email>mgrzejszczak at pivotal.io</email>
<organization>Pivotal Software, Inc.</organization>
<organizationUrl>https://www.spring.io</organizationUrl>
<roles>
<role>developer</role>
</roles>
</developer>
<developer>
<id>rbaxter</id>
<name>Ryan Baxter</name>
<email>rbaxter at pivotal.io</email>
<organization>Pivotal Software, Inc.</organization>
<organizationUrl>https://www.spring.io</organizationUrl>
<roles>
<role>developer</role>
</roles>
</developer>
<developer>
<id>omaciaszeksharma</id>
<name>Olga Maciaszek-Sharma</name>
<email>omaciaszeksharma at pivotal.io</email>
<organization>Pivotal Software, Inc.</organization>
<organizationUrl>https://www.spring.io</organizationUrl>
<roles>
<role>developer</role>
</roles>
</developer>
</developers>
<scm>
<connection>scm:git:git://github.com/spring-cloud/spring-cloud-kubernetes.git</connection>
<developerConnection>scm:git:ssh://git@github.com/spring-cloud/spring-cloud-kubernetes.git</developerConnection>
<url>https://github.com/spring-cloud/spring-cloud-kubernetes</url>
</scm>
<profiles>
<profile>
<id>spring</id>
<repositories>
<repository>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
</repository>
<repository>
<snapshots>
<enabled>false</enabled>
</snapshots>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
<repository>
<snapshots>
<enabled>false</enabled>
</snapshots>
<id>spring-releases</id>
<name>Spring Releases</name>
<url>https://repo.spring.io/release</url>
</repository>
</repositories>
</profile>
</profiles>
</project>

View File

@@ -1,17 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
Please provide details of the problem, including the version of Spring Cloud that you
are using.
**Sample**
If possible, please provide a test case or sample application that reproduces
the problem. This makes it much easier for us to diagnose the problem and to verify that
we have fixed it.

View File

@@ -1,20 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@@ -1,11 +0,0 @@
name: build controllers project
description: build controllers project
runs:
using: "composite"
steps:
- name: build controllers project
shell: bash
run: |
cd spring-cloud-kubernetes-controllers
.././mvnw -T 1C -U clean install
cd ..

View File

@@ -1,17 +0,0 @@
name: build integration tests project without tests
description: build integration tests project without tests
runs:
using: "composite"
steps:
- name: build integration tests project without tests
shell: bash
run: |
cd spring-cloud-kubernetes-test-support
.././mvnw clean install -U
cd ..
cd spring-cloud-kubernetes-integration-tests
# build the images, but dont run the tests
.././mvnw -T 1C clean install -DskipTests
cd ..

View File

@@ -1,15 +0,0 @@
name: cache
description: cache
runs:
using: "composite"
steps:
- uses: Wandalen/wretry.action@master
with:
attempt_limit: 3
action: buildjet/cache@v3
with: |
path: ~/.m2/repository
key: ${{ runner.os }}-cache-${{ env.BRANCH_NAME }}-${{ hashFiles('**/pom.xml') }}
restore-keys: ${{ runner.os }}-cache-${{ env.BRANCH_NAME }}-${{ hashFiles('**/pom.xml') }}

View File

@@ -1,19 +0,0 @@
name: clean space
description: clean space
runs:
using: "composite"
steps:
- name: apt-update
shell: bash
run: |
sudo apt-get update
- name: Free Disk Space
uses: jlumbroso/free-disk-space@main
with:
tool-cache: true
android: true
dotnet: true
haskell: true
large-packages: true
swap-storage: true

View File

@@ -1,24 +0,0 @@
name: sets environment variables
description: sets environment variables
runs:
using: "composite"
steps:
- name: set env variables
shell: bash
run: |
baseRef=$GITHUB_BASE_REF
if [[ ! -z $baseRef ]]; then
echo "This is a PR to branch : $baseRef"
echo "BASE_BRANCH=$(echo $baseRef)" >> $GITHUB_ENV
else
raw=${{ github.ref }}
echo "${raw}"
branch=${raw##*/}
echo "This is a merge to branch : ${branch}"
echo "BASE_BRANCH=$(echo ${branch})" >> $GITHUB_ENV
fi
echo "BRANCH_NAME=$(echo $GITHUB_HEAD_REF)" >> $GITHUB_ENV
echo "BASE_BRANCH_NAME=$(echo $GITHUB_BASE_REF)" >> $GITHUB_ENV
echo "DOCKER_IMAGES_KEY=$(echo $GITHUB_RUN_ID)" >> $GITHUB_ENV

View File

@@ -1,129 +0,0 @@
########################################### find test times #######################################
# when cache of test times is present, compute matrix steps
# we do this because the number of steps required in a matrix is not stable
# it is computed as sum_of_times_of_all_tests / max_time_of_a_single_test
# For example if there is no test times cache, we will be using 32 steps in the matrix
# on the other hand if cache is present, we might need less steps:
# sum_of_times_of_all_tests = 8000 seconds
# max_time_of_a_single_test = 400
# we would require 20 steps in the matrix, as having 32 would make no sense
# because one step will run around 400 seconds and others will wait for this one to finish
# as they finish earlier
name: matrix bounds on test times cache hit
description: matrix bounds on test times cache hit
runs:
using: "composite"
steps:
- name: restore test times cache
uses: actions/cache/restore@v3
with:
path: /tmp/sorted.txt
key: ${{ runner.os }}-spring-cloud-k8s-existing-test-times-cache-${{ github.run_id }}
restore-keys: ${{ runner.os }}-spring-cloud-k8s-existing-test-times-cache-
- name: show cached test times
shell: bash
run: cat /tmp/sorted.txt
- name: compute matrix steps
shell: bash
run: |
PLAIN_TEST_CLASSNAMES=($(cat /tmp/tests.txt | grep -o 'spring.cloud.k8s.test.to.run -> org.*' | awk '{print $3}'))
#######################################################################################################
### split into tests that we know the running times for and in tests that we do not know the times for.
#######################################################################################################
for test in "${PLAIN_TEST_CLASSNAMES[@]}"; do
find_test_in_sorted=$(grep "$test " /tmp/sorted.txt || true)
if [[ -z "$find_test_in_sorted" ]]; then
echo $test >> /tmp/tests-without-times.txt
else
echo $find_test_in_sorted >> /tmp/tests-with-times.txt
fi
done
sort -t' ' -nk2 /tmp/tests-with-times.txt >> /tmp/tests-with-times-sorted.txt
# this is a work-around for the fact that 'actions/download-artifact@v3' does not support
# something like: 'ignore if downloaded file is missing'.
# so unless we create a dummy file for upload, download will fail
if [[ ! -f /tmp/tests-without-times.txt ]]; then touch /tmp/tests-without-times.txt; fi
echo '--------------------------------------------------------------'
cat /tmp/tests-with-times-sorted.txt
echo '--------------------------------------------------------------'
cat /tmp/tests-without-times.txt
echo '--------------------------------------------------------------'
sum_of_all_tests=$(awk -F' ' '{sum+=$2;} END{print sum;}' /tmp/tests-with-times-sorted.txt)
sum_of_all_tests_as_int=$(printf "%.0f\n" "$sum_of_all_tests")
echo "sum of all tests : $sum_of_all_tests_as_int"
max_test_time=$(tail -1 /tmp/tests-with-times-sorted.txt | awk '{print $2}')
max_test_time_as_int=$(printf "%.0f\n" "$max_test_time")
echo "max test time : $max_test_time_as_int"
number_of_instances=$(( $sum_of_all_tests_as_int / $max_test_time_as_int ))
number_of_instances_as_array=()
number_of_instances_as_array+='['
number_of_instances_as_array+=$number_of_instances
number_of_instances_as_array+=']'
average_time_per_instance=$(( sum_of_all_tests_as_int / $number_of_instances ))
average_time_per_instance_array=()
average_time_per_instance_array+='['
average_time_per_instance_array+=$average_time_per_instance
average_time_per_instance_array+=']'
matrix_array=()
matrix_array+='['
for ((i=0; i<=${number_of_instances}; i++)); do
if (( $i == $(( $number_of_instances )) )); then
matrix_array+=$i
else
matrix_array+=$i,
fi
done
matrix_array+=']'
echo "********************************************************************************************************"
echo "number of instances : $number_of_instances_as_array"
echo "average_time_per_instance_as_array: $average_time_per_instance_as_array"
echo "matrix_array : $matrix_array"
echo "********************************************************************************************************"
number_of_instances_json=$(jq -r -c . <<< $number_of_instances_as_array)
echo "number_of_instances_json : $number_of_instances_json"
matrix_array_json=$(jq -r -c . <<< $matrix_array)
echo "matrix_array_json : $matrix_array_json"
average_time_per_instance_json=$(jq -r -c . <<< $average_time_per_instance_array)
echo "average_time_per_instance_json : $average_time_per_instance_json"
echo "TEST_TIMES_CACHE_PRESENT=true" >> $GITHUB_ENV
echo "NUMBER_OF_MATRIX_INSTANCES=$(echo $number_of_instances_json)" >> $GITHUB_ENV
echo "MATRIX_ARRAY=$(echo $matrix_array_json)" >> $GITHUB_ENV
echo "AVERAGE_TIME_PER_INSTANCE=$(echo $average_time_per_instance_json)" >> $GITHUB_ENV
- name: upload test with times
uses: actions/upload-artifact@v3
with:
name: tests-with-times-sorted.txt
path: /tmp/tests-with-times-sorted.txt
- name: upload test without times
uses: actions/upload-artifact@v3
with:
name: tests-without-times.txt
path: /tmp/tests-without-times.txt

View File

@@ -1,37 +0,0 @@
name: maven-build-with-dry-run-for-tests
description: maven-build-with-dry-run-for-tests
runs:
using: "composite"
steps:
- name: run 'package' on the project
shell: bash
run: |
./mvnw install -B \
-Dskip.build.image=true \
-DskipTests -DskipITs \
-T 1C -q
- name: find all classpath entries
shell: bash
run: |
./mvnw -q exec:exec -Dexec.classpathScope="test" -Dexec.executable="echo" -Dexec.args="%classpath" | tr : "\n" > /tmp/deps.txt
- name: find all tests
shell: bash
run: |
cd spring-cloud-kubernetes-test-support
.././mvnw -q exec:java -Prun-on-github-actions -Dexec.mainClass="org.springframework.cloud.kubernetes.tests.discovery.TestsDiscovery" > /tmp/tests.txt
cd ..
- name: show result
if: always()
shell: bash
run: cat /tmp/tests.txt
- name: upload test
uses: actions/upload-artifact@v3
with:
name: tests.txt
path: /tmp/tests.txt

View File

@@ -1,39 +0,0 @@
# common steps before running actula tests for both
# when a cache of test times is presen and when it is not present
name: pre-test-actions
description: pre-test-actions
runs:
using: "composite"
steps:
- name: set env variables
uses: ./.github/workflows/composites/env-variables
- name: setup project jdk-17
uses: ./.github/workflows/composites/setup-jdk17
if: env.BASE_BRANCH == 'main'
- name: setup project jdk-8
uses: ./.github/workflows/composites/setup-jdk1.8
if: env.BASE_BRANCH == '2.1.x'
- name: cache local maven repository
uses: ./.github/workflows/composites/cache
- name: build controllers project
uses: ./.github/workflows/composites/build-controllers-project
- name: build integration tests project
uses: ./.github/workflows/composites/build-integration-tests-project
- name: download tests
uses: actions/download-artifact@v3
with:
name: tests.txt
path: /tmp

View File

@@ -1,97 +0,0 @@
########################################### find test times #######################################
# 1. find files that look like this: 'org.springframework.xxx.txt', these files are generated
# by surefire-plugin when tests run.
# 2. extract the time it took to run a certain test (from the above .txt file)
# 3. create a file that contains test times from this current matrix
name: run and save test times when cache missing
description: run and save test times when cache missing
runs:
using: "composite"
steps:
- name: run and save test times
shell: bash
run: |
# take only likes that look like :
# 'spring.cloud.k8s.test.to.run -> org.springframework.cloud.kubernetes.fabric8.discovery.ServicePortSecureResolverTest'
PLAIN_TEST_CLASSNAMES=($(cat /tmp/tests.txt | grep -o 'spring.cloud.k8s.test.to.run -> org.*' | awk '{print $3}'))
IFS=$'\n'
SORTED_TEST_CLASSNAMES=( $(sort <<< "${PLAIN_TEST_CLASSNAMES[*]}") )
unset IFS
start_from_name=start_from_${CURRENT_INDEX}
how_many_name=how_many_${CURRENT_INDEX}
start_from=${!start_from_name}
how_many=${!how_many_name}
echo "${SORTED_TEST_CLASSNAMES[@]}"
echo "$start_from_name : ${start_from}"
echo "$how_many_name : ${how_many}"
sliced_array=(${SORTED_TEST_CLASSNAMES[@]:$start_from:$how_many})
TEST_ARG=$(echo ${sliced_array[@]} | sed 's/ /,/g')
echo "$TEST_ARG"
if [[ $baseBranch == "2.1.x" ]]; then
./mvnw -s .settings.xml -pl '-:kubernetes-leader-election-example' -pl '-:kubernetes-hello-world-example' \
-pl '-:kubernetes-reload-example' -pl '-:kubernetes-loadbalancer-example' \
-pl '-:spring-cloud-kubernetes-configserver' -pl '-:spring-cloud-kubernetes-configuration-watcher' \
-pl '-:spring-cloud-kubernetes-discoveryserver' \
-DtestsToRun=${TEST_ARG[@]} \
-e clean install \
-U -P sonar -nsu --batch-mode \
-Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn \
-Dhttp.keepAlive=false \
-Dmaven.wagon.http.pool=false \
-Dmaven.wagon.http.retryHandler.class=standard \
-Dmaven.wagon.http.retryHandler.count=3 \
-Dskip.build.image=true
else
version=$(java -version)
echo "version of java: $version"
./mvnw -s .settings.xml \
-DtestsToRun=${TEST_ARG[@]} \
-e clean install \
-U -P sonar -nsu --batch-mode \
-Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn \
-Dhttp.keepAlive=false \
-Dmaven.wagon.http.pool=false \
-Dmaven.wagon.http.retryHandler.class=standard \
-Dmaven.wagon.http.retryHandler.count=3 \
-Dskip.build.image=true
fi
touch /tmp/test_times_${{ env.CURRENT_INDEX }}.txt
for i in "${sliced_array[@]}"; do
filename="${i}.txt"
echo "searching for filename: ${filename}"
file=$(find . -name "${filename}")
echo "found file: ${file}"
result=$(cat "${file}" | grep 'elapsed' | awk '{print $12, $13}')
echo "run test: ${i} in : ${result}" >> /tmp/test_times_${{ env.CURRENT_INDEX }}.txt
done
- name: show individual test times
shell: bash
if: env.BASE_BRANCH != '2.1.x'
run: cat /tmp/test_times_${{ env.CURRENT_INDEX }}.txt
- name: upload individual tests
if: env.BASE_BRANCH != '2.1.x'
uses: actions/upload-artifact@v3
with:
name: test_times_${{ env.CURRENT_INDEX }}.txt
path: /tmp/test_times_${{ env.CURRENT_INDEX }}.txt

View File

@@ -1,196 +0,0 @@
name: run and save test times when cache present
description: run and save test times when cache present
runs:
using: "composite"
steps:
- name: download tests with times
if: env.BASE_BRANCH != '2.1.x'
uses: actions/download-artifact@v3
with:
name: tests-with-times-sorted.txt
path: /tmp/
- name: download tests without times
if: env.BASE_BRANCH != '2.1.x'
uses: actions/download-artifact@v3
with:
name: tests-without-times.txt
path: /tmp/
- name: split tests into known times and un-known times
shell: bash
run: |
echo "------------------------------------------------------------------------------"
cat /tmp/tests-with-times-sorted.txt
echo "------------------------------------------------------------------------------"
if [[ -f /tmp/tests-without-times.txt ]]; then cat /tmp/tests-without-times.txt; fi
echo "------------------------------------------------------------------------------"
############################################################################################################
############################################################################################################
############################################################################################################
# For each number_of_instances, iterate from bottom to top ('for ((j=$number_of_lines_in_file; j>0; j--))')
# (starting from the test that has the max time). Easier to understand is via an example.
# Suppose our /tmp/sorted.txt looks like this (very artificial):
# testA 1 sec
# testB 2 sec
# testC 3 sec
# average_time_per_instance = 3.1 sec
# number_of_lines_in_file = 3
# we start iterating from bottom to top and first get 'testC' that has a running time of 3 sec
# since 3 sec < 3.1 sec ('if [[ $next_sum -lt $average_time_per_instance ]];')
# this test needs to be taken in 'tests_to_take_in_current_iteration', thus:
# tests_to_take_in_current_iteration=testC; also next_sum becomes 3sec
# we then drop this line from /tmp/sorted.txt because we have already processed it
# ('sed -i "" "${j}d" /tmp/tests-with-times-sorted.txt')
# we also decrement j, since we removed one line from the file
# we then take testB, add its time to next_sum, thus next_sum = 5 sec, but now the time is NOT < 3.1 sec
# This means we do not take testB in current iteration and skip it.
# Same goes for testA, next_sum = 4 sec, and it is NOT < 3.1 sec
# There are no more lines in file to iterate, thus tests_to_take_in_current_iteration=testC
# we repeat the process again and now tests_to_take_in_current_iteration=testB,testA because their cumulative
# sum will be 3 sec and it's < 3.1 sec.
number_of_instances=${NUMBER_OF_JOBS}
echo "number of instances $number_of_instances"
average_time_per_instance=${AVERAGE_TIME_PER_INSTANCE}
echo "average time per instance $average_time_per_instance"
number_of_lines_in_file=$(grep -c ^ /tmp/tests-with-times-sorted.txt)
echo "number of lines in fine : $number_of_lines_in_file"
tests_to_run_in_current_index=''
for ((i=0; i<=${number_of_instances}; i++)) ; do
sum=0
tests_to_take_in_current_iteration=''
for ((j=$number_of_lines_in_file; j>0; j--)) ; do
current_line_in_file=$(awk "NR == ${j}" /tmp/tests-with-times-sorted.txt)
current_test_time=$(echo $current_line_in_file | awk '{print $2}')
current_test_time=$(printf "%.0f\n" "$current_test_time")
current_test_name=$(echo $current_line_in_file | awk '{print $1}')
next_sum=$(( $sum + $current_test_time ))
if [[ $next_sum -lt $average_time_per_instance ]]; then
sum=$(( $sum + $current_test_time ))
if [[ -z $tests_to_take_in_current_iteration ]]; then
tests_to_take_in_current_iteration="$current_test_name"
else
tests_to_take_in_current_iteration="$tests_to_take_in_current_iteration,$current_test_name"
fi
sed -i "${j}d" /tmp/tests-with-times-sorted.txt
number_of_lines_in_file=$(( $number_of_lines_in_file-1 ))
continue
fi
done
if [[ $i = ${CURRENT_INDEX} ]]; then
echo "current index : ${CURRENT_INDEX}"
tests_to_run_in_current_index=$tests_to_take_in_current_iteration
echo "time of tests in current index : $sum"
# this can be the case when we delete some tests in some PR for example
# the previous cache will contain more tests then we currently have, so some
# matrix instances will have no tests to run
if [[ "$sum" -eq "0" ]]; then
echo "no tests (with known times) to run in current index"
tests_to_run_in_current_index='none'
fi
fi
done
# here we compute tests that are "new", these are tests that our cache is not aware of
# for example these can come from a new PR where new tests are added
# This last step can have already existing tests to run, so we need to be careful to append
# new tests here.
if [[ ${CURRENT_INDEX} = ${NUMBER_OF_JOBS} ]]; then
echo "last index spotted"
if [ ! -f /tmp/tests-without-times.txt ]; then
echo "no tests outside cache found"
if [ -z "$tests_to_run_in_current_index" ]; then
echo "no tests to run in the last index"
tests_to_run_in_current_index='none'
fi
else
TESTS_WITHOUT_TIMES=($(cat /tmp/tests-without-times.txt))
for test in "${TESTS_WITHOUT_TIMES[@]}"; do
if [[ -z "$tests_to_run_in_current_index" ]]; then
tests_to_run_in_current_index="$test"
else
tests_to_run_in_current_index="$tests_to_run_in_current_index,$test"
fi
done
fi
fi
echo "will run tests : $tests_to_run_in_current_index"
./mvnw -s .settings.xml \
-DtestsToRun=${tests_to_run_in_current_index} \
-e clean install \
-U -P sonar -nsu --batch-mode \
-Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn \
-Dhttp.keepAlive=false \
-Dmaven.wagon.http.pool=false \
-Dmaven.wagon.http.retryHandler.class=standard \
-Dmaven.wagon.http.retryHandler.count=3 \
-Dskip.build.image=true
touch /tmp/test_times_${{ env.CURRENT_INDEX }}.txt
# if there are no tests to run in the last index, don't parse anything
if [ $tests_to_run_in_current_index != "none" ]; then
IFS=',' read -r -a sliced_array <<< "$tests_to_run_in_current_index"
unset IFS
for i in "${sliced_array[@]}"; do
# can be present in the last index as 'none,testA,testB'
if [[ "$i" == "none" ]]; then
echo "skipping 'none'"
else
filename="${i}.txt"
echo "searching for filename: ${filename}"
file=$(find . -name "${filename}")
echo "found file: ${file}"
result=$(cat "${file}" | grep 'elapsed' | awk '{print $12, $13}')
echo "run test: ${i} in : ${result}" >> /tmp/test_times_${{ env.CURRENT_INDEX }}.txt
fi
done
fi
- name: show individual test times
shell: bash
if: env.BASE_BRANCH != '2.1.x'
run: cat /tmp/test_times_${{ env.CURRENT_INDEX }}.txt
- name: upload individual tests
if: env.BASE_BRANCH != '2.1.x'
uses: actions/upload-artifact@v3
with:
name: test_times_${{ env.CURRENT_INDEX }}.txt
path: /tmp/test_times_${{ env.CURRENT_INDEX }}.txt

View File

@@ -1,15 +0,0 @@
name: save controller docker images
description: save controller docker images
runs:
using: "composite"
steps:
- name: save controller docker images
shell: bash
run: |
mkdir -p /tmp/docker/images
TAG=$(./mvnw help:evaluate -Dexpression=project.version -q -DforceStdout)
cd spring-cloud-kubernetes-controllers
while read controller_image; do
docker save -o /tmp/docker/images/${controller_image}.tar docker.io/springcloud/${controller_image}:$TAG
done < <(mvn -Dexec.executable='echo' -Dexec.args='${project.artifactId}' exec:exec -q | grep -v 'spring-cloud-kubernetes-controllers')
cd ..

View File

@@ -1,20 +0,0 @@
name: save integration tests docker images
description: save integration tests docker images
runs:
using: "composite"
steps:
- name: save integration tests docker images
shell: bash
run: |
mkdir -p /tmp/docker/images
TAG=$(./mvnw help:evaluate -Dexpression=project.version -q -DforceStdout)
cd spring-cloud-kubernetes-integration-tests
while read integ_test; do
docker save -o /tmp/docker/images/${integ_test}.tar docker.io/springcloud/${integ_test}:$TAG
done < <(mvn -Dexec.executable='echo' -Dexec.args='${project.artifactId}' exec:exec -q \
| grep -v 'spring-cloud-kubernetes-integration-tests' \
| grep -v 'spring-cloud-kubernetes-client-configmap-event-reload-multiple-apps' \
| grep -v 'spring-cloud-kubernetes-client-configuration-watcher-configmap-test-app' \
| grep -v 'spring-cloud-kubernetes-client-secrets-event-reload-multiple-apps' \
| grep -v 'spring-cloud-kubernetes-client-configuration-watcher-secrets-test-app' )
cd ..

View File

@@ -1,9 +0,0 @@
name: setup project with jdk-8
description: setup project with jdk-8
runs:
using: "composite"
steps:
- uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '8'

View File

@@ -1,9 +0,0 @@
name: setup project with jdk-17
description: setup project with jdk-17
runs:
using: "composite"
steps:
- uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '17'

View File

@@ -1,65 +0,0 @@
name: test-bounds
description: test-bounds
runs:
using: "composite"
steps:
- name: test-bounds
shell: bash
run: |
PLAIN_TEST_CLASSNAMES=($(cat /tmp/tests.txt | grep -o 'spring.cloud.k8s.test.to.run -> org.*' | awk '{print $3}'))
NUMBER_OF_TESTS=${#PLAIN_TEST_CLASSNAMES[@]}
echo "current index : ${CURRENT_INDEX}"
echo "total number of tests: ${NUMBER_OF_TESTS} to be run on ${NUMBER_OF_JOBS} instances"
##########################################################################################################
##########################################################################################################
# Split tests per instance. The simplest form is when (for example), there are 100 tests and 10 instances.
# In this case, we will run 10 tests per a single instance. This is the case when reminder == 0 (100 / 10).
# In this case we will compute start_from as = current index * per_instance, that is: 0, 10, 20...
# and so on. How many tests are supposed to be taken in each matrix is equal to per_instance, that is 10.
# Slightly more interesting is when the tests are not perfectly distributed, when reminder != 0.
# For example 323 tests on 32 instances. In this case, we logically split this problem in two:
# when current_index < reminder and the rest.
# The batches in the example above are going to be [0, 11) [11, 22) [22, 33) [33, 43) [43, 53) ...
# So when current_index < reminder, we need to start at : current_index * per_instance + current_index,
# i.e.: 0, 11, 22
# and how many elements to take is : per_instance + 1, i.e. : 11
# On the other hand when current_index is not < reminder, how_many is easy, it's equal to per_instance.
# start_from on the other hand is : per_instance * current_index + reminder, i.e. : 33, 43, 53 and so on
##########################################################################################################
##########################################################################################################
per_instance=$((${NUMBER_OF_TESTS} / ${NUMBER_OF_JOBS}))
reminder=$((${NUMBER_OF_TESTS} - ${NUMBER_OF_JOBS} * ${per_instance}))
start_from=0
how_many=0
if [[ $reminder -eq 0 ]]; then
start_from=$(( ${CURRENT_INDEX} * ${per_instance} ))
how_many=${per_instance}
else
if [[ $CURRENT_INDEX -eq 0 ]]; then
start_from=0
how_many=$(( $per_instance + 1 ))
else
if [[ $CURRENT_INDEX -lt $reminder ]]; then
start_from=$(( $per_instance * $CURRENT_INDEX + $CURRENT_INDEX ))
how_many=$(( $per_instance +1 ))
else
start_from=$(( $per_instance * $CURRENT_INDEX + $reminder ))
how_many=$per_instance
fi
fi
fi
start_from_name=start_from_${CURRENT_INDEX}
how_many_name=how_many_${CURRENT_INDEX}
echo "$start_from_name : $start_from"
echo "$how_many_name : $how_many"
echo "$start_from_name=$start_from" >> $GITHUB_ENV
echo "$how_many_name=$how_many" >> $GITHUB_ENV

View File

@@ -1,58 +0,0 @@
# this stage sort all tests by their timings. For that we rely on the surefire-plugin reports
# that have the name as <TEST_NAME>.txt and inside it there is a field that holds the time it took
# We get a hold of that field via awk '{print $12, $13} in the previous step.
# That previous step stores only tests for one matrix step, which we uploaded.
# In the steps below we download all artifacts (thus all test times), put them into a single file
# called /tmp/times-of-all-tests.txt, then sort this file, thus get a histogram of all test names
# and the time it took to run them.
name: test-times
description: test-times
runs:
using: "composite"
steps:
# we omit the name, as such all artifacts are downloaded
- name: download all artifacts
if: env.BASE_BRANCH != '2.1.x'
uses: actions/download-artifact@v3
with:
path: /tmp/all-artifacts
- name: show all artifacts
if: env.BASE_BRANCH != '2.1.x'
shell: bash
run: ls -l /tmp/all-artifacts
- name: merge all artifacts into a single file and sort by time
if: env.BASE_BRANCH != '2.1.x'
shell: bash
run: |
arr=($(find /tmp/all-artifacts/ -type f -name "test_times*"))
for i in "${arr[@]}"; do
echo "parsing -> ${i}"
cat "${i}" | awk '{print $3" "$6}' >> /tmp/times-of-all-tests.txt
done
sort -t' ' -nk2 /tmp/times-of-all-tests.txt >> /tmp/sorted.txt
cat /tmp/sorted.txt
- name: show all tests in a sorted manner
if: env.BASE_BRANCH != '2.1.x'
shell: bash
run: cat /tmp/sorted.txt
# save with the current run_id, but restore it without it. This means two things:
# 1) if we re-run, cache will be available
# 2) if there is a new run, we restore as '${{ runner.os }}-spring-cloud-k8s-existing-test-times-cache-'
# meaning there could be many of them already present and this is not an exact match
# github in this case will pick up the latest one, exactly what we want.
- name: save test times in cache
uses: actions/cache/save@v3
if: env.BASE_BRANCH != '2.1.x'
with:
path: /tmp/sorted.txt
key: ${{ runner.os }}-spring-cloud-k8s-existing-test-times-cache-${{ github.run_id }}

View File

@@ -1,19 +0,0 @@
# we are not going to use upload/download actions, since it is slower than cache usage
# as key to the cache we are going to use DOCKER_IMAGES_KEY, which is the same as GITHUB_RUN_ID
name: upload docker images
description: upload docker images
runs:
using: "composite"
steps:
- uses: Wandalen/wretry.action@master
with:
attempt_limit: 3
action: buildjet/cache@v3
with: |
path: /tmp/docker/images
key: docker-images-cache-${{ env.DOCKER_IMAGES_KEY }}
restore-keys: docker-images-cache-${{ env.DOCKER_IMAGES_KEY }}

53
.github/workflows/deploy-docs.yml vendored Normal file
View File

@@ -0,0 +1,53 @@
name: Deploy Docs
run-name: ${{ format('{0} ({1})', github.workflow, github.event.inputs.build-refname || 'all') }}
on:
workflow_dispatch:
inputs:
build-refname:
description: Enter git refname to build (e.g., 5.7.x).
required: false
push:
branches: docs-build
env:
GRADLE_ENTERPRISE_SECRET_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
permissions:
contents: write
jobs:
build:
if: github.repository_owner == 'spring-cloud'
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 5
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- name: Set up refname build
if: github.event.inputs.build-refname
run: |
git fetch --depth 1 https://github.com/$GITHUB_REPOSITORY ${{ github.event.inputs.build-refname }}
export BUILD_REFNAME=${{ github.event.inputs.build-refname }}
echo "BUILD_REFNAME=$BUILD_REFNAME" >> $GITHUB_ENV
export BUILD_VERSION=$(git cat-file --textconv FETCH_HEAD:pom.xml | python3 -c "import xml.etree.ElementTree as xml; from sys import stdin; print(xml.parse(stdin).getroot().find('{http://maven.apache.org/POM/4.0.0}version').text)")
echo BUILD_VERSION=$BUILD_VERSION >> $GITHUB_ENV
- name: Run Antora
run: |
./mvnw --no-transfer-progress -B antora
- name: Publish Docs
uses: spring-io/spring-doc-actions/rsync-antora-reference@v0.0.11
with:
docs-username: ${{ secrets.DOCS_USERNAME }}
docs-host: ${{ secrets.DOCS_HOST }}
docs-ssh-key: ${{ secrets.DOCS_SSH_KEY }}
docs-ssh-host-key: ${{ secrets.DOCS_SSH_HOST_KEY }}
site-path: target/antora/site
- name: Bust Cloudflare Cache
uses: spring-io/spring-doc-actions/bust-cloudflare-antora-cache@v0.0.11
with:
context-root: spring-cloud-kubernetes
cloudflare-zone-id: ${{ secrets.CLOUDFLARE_ZONE_ID }}
cloudflare-cache-token: ${{ secrets.CLOUDFLARE_CACHE_TOKEN }}

View File

@@ -1,223 +0,0 @@
name: github-workflow
on:
push:
branches: [ main, 2.1.x, 3.0.x ]
pull_request:
branches: [ main, 2.1.x, 3.0.x ]
jobs:
build:
runs-on: ubuntu-latest
env:
# this might get set to true if there is an existing cache of test times
# this happens in 'matrix-bounds-on-test-times-cache-hit'
TEST_TIMES_CACHE_PRESENT: false
SEGMENT_DOWNLOAD_TIMEOUT_MINS: 30
# this job ('build') outputs a value from step 'test_times_cache_present_init', that has the name of:
# 'test_times_cache_present'. This can later be used by other jobs. For example, we use this one
# to skip the job responsible for running the tests, if a previous cache that has the test times is not present.
# Same for other two variables 'number_of_matrix_instances' and 'matrix_array'.
outputs:
test_times_cache_present: ${{ steps.test_times_cache_present_init.outputs.test_times_cache_present }}
number_of_matrix_instances: ${{ steps.test_times_cache_present_init.outputs.number_of_matrix_instances }}
matrix_array: ${{ steps.test_times_cache_present_init.outputs.matrix_array }}
average_time_per_instance: ${{ steps.test_times_cache_present_init.outputs.average_time_per_instance }}
steps:
- name: checkout project
uses: actions/checkout@v2
- name: clean space
uses: ./.github/workflows/composites/clean-space
- name: set env variables
uses: ./.github/workflows/composites/env-variables
- name: setup project jdk-17
id: jdk_17
uses: ./.github/workflows/composites/setup-jdk17
if: env.BASE_BRANCH == 'main' || env.BASE_BRANCH == '3.0.x'
- name: setup project jdk-8
uses: ./.github/workflows/composites/setup-jdk1.8
if: env.BASE_BRANCH == '2.1.x'
- name: cache local maven repository
uses: ./.github/workflows/composites/cache
- name: Show caches
uses: actions/github-script@v6
with:
script: |
const caches = await github.rest.actions.getActionsCacheList({
owner: context.repo.owner,
repo: context.repo.repo,
})
for (const cache of caches.data.actions_caches) {
console.log(cache)
}
- name: maven build with dry-run for tests
uses: ./.github/workflows/composites/maven-build-with-dry-run-for-tests
- name: restore test times cache if it exists
id: restore_test_times_cache
if: env.BASE_BRANCH != '2.1.x'
uses: actions/cache/restore@v3
with:
path: /tmp/sorted.txt
key: ${{ runner.os }}-spring-cloud-k8s-existing-test-times-cache-${{ github.run_id }}
restore-keys: ${{ runner.os }}-spring-cloud-k8s-existing-test-times-cache-
- name: check test times cache exists
id: check_files
uses: andstor/file-existence-action@v2
with:
files: /tmp/sorted.txt
- name: show existing cache of test times
if: steps.check_files.outputs.files_exists == 'true'
shell: bash
run: cat /tmp/sorted.txt
- name: compute matrix related fields when cache is present
if: steps.check_files.outputs.files_exists == 'true'
uses: ./.github/workflows/composites/matrix-bounds-on-test-times-cache-hit
- name: matrix related variables when cache is present
id: test_times_cache_present_init
run: |
echo "test_times_cache_present=${{ env.TEST_TIMES_CACHE_PRESENT }}" >> $GITHUB_OUTPUT
echo "number_of_matrix_instances=${{ env.NUMBER_OF_MATRIX_INSTANCES }}" >> $GITHUB_OUTPUT
echo "matrix_array=${{ env.MATRIX_ARRAY }}" >> $GITHUB_OUTPUT
echo "average_time_per_instance=${{ env.AVERAGE_TIME_PER_INSTANCE }}" >> $GITHUB_OUTPUT
test_when_cache_present:
needs: [ build ]
runs-on: ubuntu-latest
env:
SEGMENT_DOWNLOAD_TIMEOUT_MINS: 30
# only run this one if there is a previous cache of test times
if: needs.build.outputs.test_times_cache_present == 'true'
timeout-minutes: 60
strategy:
fail-fast: true
matrix:
current_index: [ "${{ fromJSON(needs.build.outputs.matrix_array) }}" ]
number_of_jobs: [ "${{ fromJSON(needs.build.outputs.number_of_matrix_instances) }}" ]
average_time_per_instance: [ "${{ fromJSON(needs.build.outputs.average_time_per_instance) }}" ]
steps:
- name: checkout project
uses: actions/checkout@v2
- name: clean space
uses: ./.github/workflows/composites/clean-space
- name: set env variables
uses: ./.github/workflows/composites/env-variables
- name: setup project jdk-17
uses: ./.github/workflows/composites/setup-jdk17
if: env.BASE_BRANCH == 'main' || env.BASE_BRANCH == '3.0.x'
- name: setup project jdk-8
uses: ./.github/workflows/composites/setup-jdk1.8
if: env.BASE_BRANCH == '2.1.x'
- name: pre-test-actions
uses: ./.github/workflows/composites/pre-test-actions
- name: testcontainers reuse support
shell: bash
run: echo "testcontainers.reuse.enable=true" > ~/.testcontainers.properties
- name: run and save test times when cache is present
uses: ./.github/workflows/composites/run-and-save-test-times-when-cache-present
env:
CURRENT_INDEX: ${{ matrix.current_index }}
NUMBER_OF_JOBS: ${{ matrix.number_of_jobs }}
AVERAGE_TIME_PER_INSTANCE: ${{ matrix.average_time_per_instance }}
test_when_cache_missing:
needs: [ build ]
runs-on: ubuntu-latest
env:
SEGMENT_DOWNLOAD_TIMEOUT_MINS: 30
timeout-minutes: 60
# only run this one if there is no previous cache of test times
if: needs.build.outputs.test_times_cache_present == 'false'
strategy:
fail-fast: true
matrix:
current_index: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]
number_of_jobs: [32]
steps:
- name: checkout project
uses: actions/checkout@v2
- name: clean space
uses: ./.github/workflows/composites/clean-space
- name: set env variables
uses: ./.github/workflows/composites/env-variables
- name: setup project jdk-17
uses: ./.github/workflows/composites/setup-jdk17
if: env.BASE_BRANCH == 'main' || env.BASE_BRANCH == '3.0.x'
- name: setup project jdk-8
uses: ./.github/workflows/composites/setup-jdk1.8
if: env.BASE_BRANCH == '2.1.x'
- name: pre-test-actions
uses: ./.github/workflows/composites/pre-test-actions
- name: compute single step test bounds
uses: ./.github/workflows/composites/test-bounds
env:
CURRENT_INDEX: ${{ matrix.current_index }}
NUMBER_OF_JOBS: ${{ matrix.number_of_jobs }}
- name: testcontainers reuse support
shell: bash
run: echo "testcontainers.reuse.enable=true" > ~/.testcontainers.properties
- name: run and save individual test times
env:
CURRENT_INDEX: ${{ matrix.current_index }}
if: env.BASE_BRANCH != '2.1.x'
uses: ./.github/workflows/composites/run-and-save-test-times-when-cache-missing
save_test_times_when_cache_missing:
runs-on: ubuntu-latest
needs: [build, test_when_cache_missing ]
steps:
- name: checkout project
uses: actions/checkout@v2
- name: compute and save running time of tests
if: env.BASE_BRANCH != '2.1.x'
uses: ./.github/workflows/composites/test-times
save_test_times_when_cache_present:
runs-on: ubuntu-latest
needs: [ build, test_when_cache_present ]
steps:
- name: checkout project
uses: actions/checkout@v2
- name: compute and save running time of tests
if: env.BASE_BRANCH != '2.1.x'
uses: ./.github/workflows/composites/test-times

98
.gitignore vendored
View File

@@ -1,86 +1,22 @@
# Created by .ignore support plugin (hsz.mobi)
### Maven template
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
.mvn/timing.properties
.flattened-pom.xml
### Java template
*.class
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.war
*.ear
# virtual machine crash logs, see https://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion
*.iml
## Directory-based project format:
.idea/
# if you remove the above rule, at least ignore the following:
# User-specific stuff:
# .idea/workspace.xml
# .idea/tasks.xml
# .idea/dictionaries
# Sensitive or high-churn files:
# .idea/dataSources.ids
# .idea/dataSources.xml
# .idea/sqlDataSources.xml
# .idea/dynamic.xml
# .idea/uiDesigner.xml
# Gradle:
# .idea/gradle.xml
# .idea/libraries
# Mongo Explorer plugin:
# .idea/mongoSettings.xml
## File-based project format:
*.ipr
*.iws
## Plugin-specific files:
# IntelliJ
/out/
.DS_Store
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
# vscode + eclipse
.settings/
.project
.classpath
*.orig
.springBeans
.factorypath
.attach_pid*
.vscode/
.java-version
*Dockerfile
.sts4-cache
.ant-targets-build.xml
src/ant/.ant-targets-upload-dist.xml
*.sonar4clipse*
.DS_Store
*.iml
*.ipr
*.iws
/.idea/
*.graphml
node
node_modules
build
package.json
package-lock.json

View File

@@ -1 +0,0 @@
-DaltSnapshotDeploymentRepository=repo.spring.io::default::https://repo.spring.io/libs-snapshot-local -P spring

206
.mvn/wrapper/MavenWrapperDownloader.java vendored Executable file → Normal file
View File

@@ -1,117 +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
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
* Copyright 2007-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* 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.net.*;
import java.io.*;
import java.nio.channels.*;
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";
private static final String WRAPPER_VERSION = "0.5.6";
/**
* 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/"
+ WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".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 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";
/**
* 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";
/**
* 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());
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);
// 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);
}
}
File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
if(!outputFile.getParentFile().exists()) {
if(!outputFile.getParentFile().mkdirs()) {
System.out.println(
"- ERROR creating output directory '" + 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();
}
private static void downloadFileFromURL(String urlString, File destination) throws Exception {
if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
String username = System.getenv("MVNW_USERNAME");
char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
Authenticator.setDefault(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
});
}
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 Executable file → Normal file

Binary file not shown.

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

@@ -1 +1,2 @@
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.5.4/apache-maven-3.5.4-bin.zip
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar

View File

@@ -1,3 +0,0 @@
# Enable auto-env through the sdkman_auto_env config
# Add key=value pairs of SDKs to use below
java=17.0.1-tem

View File

@@ -1,68 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<settings>
<servers>
<server>
<id>repo.spring.io</id>
<username>${env.CI_DEPLOY_USERNAME}</username>
<password>${env.CI_DEPLOY_PASSWORD}</password>
</server>
</servers>
<profiles>
<profile>
<!--
N.B. this profile is only here to support users and IDEs that do not use Maven 3.3.
It isn't needed on the command line if you use the wrapper script (mvnw) or if you use
a native Maven with the right version. Eclipse users should points their Maven tooling to
this settings file, or copy the profile into their ~/.m2/settings.xml.
-->
<id>spring</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-releases</id>
<name>Spring Releases</name>
<url>https://repo.spring.io/release</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</profile>
</profiles>
</settings>

View File

201
LICENSE
View File

@@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
https://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

5
NOTICE
View File

@@ -1,5 +0,0 @@
Copyright (C) 2016 to the original authors.
This project was originally developed by Red Hat Inc. under the Fabric8 project.
https://github.com/fabric8io/spring-cloud-kubernetes

File diff suppressed because it is too large Load Diff

View File

@@ -6,38 +6,39 @@ antora:
- '@antora/collector-extension'
- '@antora/atlas-extension'
- require: '@springio/antora-extensions/root-component-extension'
root_component_name: 'PROJECT_WITHOUT_SPRING'
# FIXME: Run antora once using this extension to migrate to the Asciidoc Tabs syntax
# and then remove this extension
- require: '@springio/antora-extensions/tabs-migration-extension'
unwrap_example_block: always
save_result: true
root_component_name: 'cloud-kubernetes'
site:
title: PROJECT_FULL_NAME
url: https://docs.spring.io/PROJECT_NAME/reference/
title: Spring Cloud Kubernetes
url: https://docs.spring.io/spring-cloud-kubernetes/reference
robots: allow
git:
ensure_git_suffix: false
content:
sources:
- url: ./..
branches: HEAD
- url: https://github.com/spring-cloud/spring-cloud-kubernetes
# Refname matching:
# https://docs.antora.org/antora/latest/playbook/content-refname-matching/
branches: [ main ]
tags: [ '({4..9}).+({1..9}).+({0..9})?(-{RC,M}+({0..9}))', '!4.1.0-M1' ]
start_path: docs
worktrees: true
asciidoc:
attributes:
page-stackoverflow-url: https://stackoverflow.com/tags/spring-cloud
page-pagination: ''
hide-uri-scheme: '@'
tabs-sync-option: '@'
chomp: 'all'
extensions:
- '@asciidoctor/tabs'
- '@springio/asciidoctor-extensions'
sourcemap: true
urls:
latest_version_segment_strategy: redirect:to
latest_version_segment: ''
redirect_facility: httpd
ui:
bundle:
url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.3.5/ui-bundle.zip
snapshot: true
runtime:
log:
failure_level: warn
format: pretty
ui:
bundle:
url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.3.5/ui-bundle.zip

View File

@@ -1,2 +0,0 @@
releaser.maven.build-command: ./scripts/integration-tests.sh
releaser.maven.deploy-command: ./scripts/deploy.sh {{systemProps}}

105
docs/.flattened-pom.xml Normal file
View File

@@ -0,0 +1,105 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes</artifactId>
<version>3.0.4-SNAPSHOT</version>
</parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-docs</artifactId>
<version>3.0.4-SNAPSHOT</version>
<name>Spring Cloud Kubernetes Docs</name>
<description>Spring Cloud Kubernetes Docs</description>
<url>https://cloud.spring.io/spring-cloud-kubernetes-docs</url>
<inceptionYear>2017</inceptionYear>
<organization>
<name>Pivotal Software, Inc.</name>
<url>https://www.spring.io</url>
</organization>
<licenses>
<license>
<name>Apache License, Version 2.0</name>
<url>https://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
<developers>
<developer>
<id>dsyer</id>
<name>Dave Syer</name>
<email>dsyer at pivotal.io</email>
<organization>Pivotal Software, Inc.</organization>
<organizationUrl>https://www.spring.io</organizationUrl>
<roles>
<role>lead</role>
</roles>
</developer>
<developer>
<id>sgibb</id>
<name>Spencer Gibb</name>
<email>sgibb at pivotal.io</email>
<organization>Pivotal Software, Inc.</organization>
<organizationUrl>https://www.spring.io</organizationUrl>
<roles>
<role>lead</role>
</roles>
</developer>
<developer>
<id>mgrzejszczak</id>
<name>Marcin Grzejszczak</name>
<email>mgrzejszczak at pivotal.io</email>
<organization>Pivotal Software, Inc.</organization>
<organizationUrl>https://www.spring.io</organizationUrl>
<roles>
<role>developer</role>
</roles>
</developer>
<developer>
<id>rbaxter</id>
<name>Ryan Baxter</name>
<email>rbaxter at pivotal.io</email>
<organization>Pivotal Software, Inc.</organization>
<organizationUrl>https://www.spring.io</organizationUrl>
<roles>
<role>developer</role>
</roles>
</developer>
<developer>
<id>omaciaszeksharma</id>
<name>Olga Maciaszek-Sharma</name>
<email>omaciaszeksharma at pivotal.io</email>
<organization>Pivotal Software, Inc.</organization>
<organizationUrl>https://www.spring.io</organizationUrl>
<roles>
<role>developer</role>
</roles>
</developer>
</developers>
<scm>
<connection>scm:git:git://github.com/spring-cloud/spring-cloud-kubernetes.git/spring-cloud-kubernetes-docs</connection>
<developerConnection>scm:git:ssh://git@github.com/spring-cloud/spring-cloud-kubernetes.git/spring-cloud-kubernetes-docs</developerConnection>
<url>https://github.com/spring-cloud/spring-cloud-kubernetes/spring-cloud-kubernetes-docs</url>
</scm>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes-fabric8</artifactId>
<version>3.0.4-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes-fabric8-all</artifactId>
<version>3.0.4-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes-fabric8-config</artifactId>
<version>3.0.4-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View File

@@ -1,32 +0,0 @@
name: Deploy Docs
on:
push:
branches-ignore: [ gh-pages ]
tags: '**'
repository_dispatch:
types: request-build-reference # legacy
#schedule:
#- cron: '0 10 * * *' # Once per day at 10am UTC
workflow_dispatch:
permissions:
actions: write
jobs:
build:
runs-on: ubuntu-latest
# if: github.repository_owner == 'spring-cloud'
steps:
- name: Checkout
uses: actions/checkout@v3
with:
ref: docs-build
fetch-depth: 1
- name: Dispatch (partial build)
if: github.ref_type == 'branch'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh workflow run deploy-docs.yml -r $(git rev-parse --abbrev-ref HEAD) -f build-refname=${{ github.ref_name }}
- name: Dispatch (full build)
if: github.ref_type == 'tag'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh workflow run deploy-docs.yml -r $(git rev-parse --abbrev-ref HEAD)

View File

@@ -1,12 +0,0 @@
name: PROJECT_WITHOUT_SPRING
version: true
title: PROJECT_NAME
nav:
- modules/ROOT/nav.adoc
ext:
collector:
run:
command: ./mvnw --no-transfer-progress -B process-resources -Pdocs -pl docs -Dantora-maven-plugin.phase=none -Dgenerate-docs.phase=none -Dgenerate-readme.phase=none -Dgenerate-cloud-resources.phase=none -Dmaven-dependency-plugin-for-docs.phase=none -Dmaven-dependency-plugin-for-docs-classes.phase=none -DskipTests
local: true
scan:
dir: ./target/classes/antora-resources/

View File

@@ -1,30 +0,0 @@
* xref:index.adoc[]
* xref:spring-cloud-kubernetes.adoc[]
* xref:_attributes.adoc[]
* xref:getting-started.adoc[]
* xref:discovery-client.adoc[]
* xref:discovery-kubernetes-native.adoc[]
* xref:property-source-config.adoc[]
** xref:property-source-config/configmap-propertysource.adoc[]
** xref:property-source-config/secrets-propertysource.adoc[]
** xref:property-source-config/namespace-resolution.adoc[]
** xref:property-source-config/order_of_configMaps_and_secrets.adoc[]
** xref:property-source-config/propertysource-reload.adoc[]
** xref:property-source-config/namespace-label-filtering.adoc[]
* xref:kubernetes-awareness.adoc[]
* xref:pod-health-indicator.adoc[]
* xref:info-contributor.adoc[]
* xref:leader-election.adoc[]
* xref:load-balancer.adoc[]
* xref:security-service-accounts.adoc[]
* xref:service-registry.adoc[]
* xref:spring-cloud-kubernetes-configuration-watcher.adoc[]
* xref:spring-cloud-kubernetes-configserver.adoc[]
* xref:spring-cloud-kubernetes-discoveryserver.adoc[]
* xref:examples.adoc[]
* xref:other-resources.adoc[]
* xref:README.adoc[]
* xref:_configprops.adoc[]
* xref:appendix.adoc[]
* xref:sagan-boot.adoc[]
* xref:sagan-index.adoc[]

View File

@@ -1,14 +0,0 @@
:doctype: book
:idprefix:
:idseparator: -
:tabsize: 4
:numbered:
:sectanchors:
:sectnums:
:icons: font
:hide-uri-scheme:
:docinfo: shared,private
:sc-ext: java
:project-full-name: Spring Cloud Kubernetes
:all: {asterisk}{asterisk}

View File

@@ -1,116 +0,0 @@
|===
|Name | Default | Description
|spring.cloud.kubernetes.client.api-version | |
|spring.cloud.kubernetes.client.apiVersion | `+++v1+++` | Kubernetes API Version
|spring.cloud.kubernetes.client.ca-cert-data | |
|spring.cloud.kubernetes.client.ca-cert-file | |
|spring.cloud.kubernetes.client.caCertData | | Kubernetes API CACertData
|spring.cloud.kubernetes.client.caCertFile | | Kubernetes API CACertFile
|spring.cloud.kubernetes.client.client-cert-data | |
|spring.cloud.kubernetes.client.client-cert-file | |
|spring.cloud.kubernetes.client.client-key-algo | |
|spring.cloud.kubernetes.client.client-key-data | |
|spring.cloud.kubernetes.client.client-key-file | |
|spring.cloud.kubernetes.client.client-key-passphrase | |
|spring.cloud.kubernetes.client.clientCertData | | Kubernetes API ClientCertData
|spring.cloud.kubernetes.client.clientCertFile | | Kubernetes API ClientCertFile
|spring.cloud.kubernetes.client.clientKeyAlgo | `+++RSA+++` | Kubernetes API ClientKeyAlgo
|spring.cloud.kubernetes.client.clientKeyData | | Kubernetes API ClientKeyData
|spring.cloud.kubernetes.client.clientKeyFile | | Kubernetes API ClientKeyFile
|spring.cloud.kubernetes.client.clientKeyPassphrase | `+++changeit+++` | Kubernetes API ClientKeyPassphrase
|spring.cloud.kubernetes.client.connection-timeout | |
|spring.cloud.kubernetes.client.connectionTimeout | `+++10s+++` | Connection timeout
|spring.cloud.kubernetes.client.http-proxy | |
|spring.cloud.kubernetes.client.https-proxy | |
|spring.cloud.kubernetes.client.logging-interval | |
|spring.cloud.kubernetes.client.loggingInterval | `+++20s+++` | Logging interval
|spring.cloud.kubernetes.client.master-url | |
|spring.cloud.kubernetes.client.masterUrl | `+++https://kubernetes.default.svc+++` | Kubernetes API Master Node URL
|spring.cloud.kubernetes.client.namespace | `+++true+++` | Kubernetes Namespace
|spring.cloud.kubernetes.client.no-proxy | |
|spring.cloud.kubernetes.client.oauth-token | |
|spring.cloud.kubernetes.client.oauthToken | | Kubernetes API Oauth Token
|spring.cloud.kubernetes.client.password | | Kubernetes API Password
|spring.cloud.kubernetes.client.proxy-password | |
|spring.cloud.kubernetes.client.proxy-username | |
|spring.cloud.kubernetes.client.request-timeout | |
|spring.cloud.kubernetes.client.requestTimeout | `+++10s+++` | Request timeout
|spring.cloud.kubernetes.client.rolling-timeout | |
|spring.cloud.kubernetes.client.rollingTimeout | `+++900s+++` | Rolling timeout
|spring.cloud.kubernetes.client.service-account-namespace-path | `+++/var/run/secrets/kubernetes.io/serviceaccount/namespace+++` |
|spring.cloud.kubernetes.client.trust-certs | |
|spring.cloud.kubernetes.client.trustCerts | `+++false+++` | Kubernetes API Trust Certificates
|spring.cloud.kubernetes.client.user-agent | `+++Spring-Cloud-Kubernetes-Application+++` |
|spring.cloud.kubernetes.client.username | | Kubernetes API Username
|spring.cloud.kubernetes.client.watch-reconnect-interval | |
|spring.cloud.kubernetes.client.watch-reconnect-limit | |
|spring.cloud.kubernetes.client.watchReconnectInterval | `+++1s+++` | Reconnect Interval
|spring.cloud.kubernetes.client.watchReconnectLimit | `+++-1+++` | Reconnect Interval limit retries
|spring.cloud.kubernetes.config.enable-api | `+++true+++` |
|spring.cloud.kubernetes.config.enabled | `+++true+++` | Enable the ConfigMap property source locator.
|spring.cloud.kubernetes.config.fail-fast | `+++false+++` |
|spring.cloud.kubernetes.config.include-profile-specific-sources | `+++true+++` |
|spring.cloud.kubernetes.config.labels | |
|spring.cloud.kubernetes.config.name | |
|spring.cloud.kubernetes.config.namespace | |
|spring.cloud.kubernetes.config.paths | |
|spring.cloud.kubernetes.config.retry | |
|spring.cloud.kubernetes.config.sources | |
|spring.cloud.kubernetes.config.use-name-as-prefix | `+++false+++` |
|spring.cloud.kubernetes.discovery.all-namespaces | `+++false+++` |
|spring.cloud.kubernetes.discovery.cache-loading-timeout-seconds | `+++60+++` |
|spring.cloud.kubernetes.discovery.enabled | `+++true+++` |
|spring.cloud.kubernetes.discovery.filter | |
|spring.cloud.kubernetes.discovery.include-external-name-services | `+++false+++` |
|spring.cloud.kubernetes.discovery.include-not-ready-addresses | `+++false+++` |
|spring.cloud.kubernetes.discovery.known-secure-ports | `+++[443, 8443]+++` |
|spring.cloud.kubernetes.discovery.metadata.add-annotations | `+++true+++` |
|spring.cloud.kubernetes.discovery.metadata.add-labels | `+++true+++` |
|spring.cloud.kubernetes.discovery.metadata.add-pod-annotations | `+++false+++` |
|spring.cloud.kubernetes.discovery.metadata.add-pod-labels | `+++false+++` |
|spring.cloud.kubernetes.discovery.metadata.add-ports | `+++true+++` |
|spring.cloud.kubernetes.discovery.metadata.annotations-prefix | |
|spring.cloud.kubernetes.discovery.metadata.labels-prefix | |
|spring.cloud.kubernetes.discovery.metadata.ports-prefix | `+++port.+++` |
|spring.cloud.kubernetes.discovery.namespaces | |
|spring.cloud.kubernetes.discovery.order | `+++0+++` |
|spring.cloud.kubernetes.discovery.primary-port-name | |
|spring.cloud.kubernetes.discovery.service-labels | |
|spring.cloud.kubernetes.discovery.use-endpoint-slices | `+++false+++` |
|spring.cloud.kubernetes.discovery.wait-cache-ready | `+++true+++` |
|spring.cloud.kubernetes.leader.auto-startup | `+++true+++` | Should leader election be started automatically on startup. Default: true
|spring.cloud.kubernetes.leader.config-map-name | `+++leaders+++` | Kubernetes ConfigMap where leaders information will be stored. Default: leaders
|spring.cloud.kubernetes.leader.create-config-map | `+++true+++` | Enable/disable creating ConfigMap if it does not exist. Default: true
|spring.cloud.kubernetes.leader.enabled | `+++true+++` | Should leader election be enabled. Default: true
|spring.cloud.kubernetes.leader.leader-id-prefix | `+++leader.id.+++` | Leader id property prefix for the ConfigMap. Default: leader.id.
|spring.cloud.kubernetes.leader.namespace | | Kubernetes namespace where the leaders ConfigMap and candidates are located.
|spring.cloud.kubernetes.leader.publish-failed-events | `+++false+++` | Enable/disable publishing events in case leadership acquisition fails. Default: false
|spring.cloud.kubernetes.leader.role | | Role for which leadership this candidate will compete.
|spring.cloud.kubernetes.leader.update-period | `+++60000ms+++` | Leadership status check period. Default: 60s
|spring.cloud.kubernetes.loadbalancer.cluster-domain | `+++cluster.local+++` | cluster domain.
|spring.cloud.kubernetes.loadbalancer.enabled | `+++true+++` | Load balancer enabled,default true.
|spring.cloud.kubernetes.loadbalancer.mode | | {@link KubernetesLoadBalancerMode} setting load balancer server list with ip of pod or service name. default value is POD.
|spring.cloud.kubernetes.loadbalancer.port-name | `+++http+++` | service port name.
|spring.cloud.kubernetes.reload.enable-reload-filtering | `+++false+++` |
|spring.cloud.kubernetes.reload.enabled | `+++false+++` |
|spring.cloud.kubernetes.reload.max-wait-for-restart | `+++2s+++` |
|spring.cloud.kubernetes.reload.mode | `+++EVENT+++` |
|spring.cloud.kubernetes.reload.monitoring-config-maps | `+++true+++` |
|spring.cloud.kubernetes.reload.monitoring-secrets | `+++false+++` |
|spring.cloud.kubernetes.reload.namespaces | |
|spring.cloud.kubernetes.reload.period | `+++15000ms+++` |
|spring.cloud.kubernetes.reload.strategy | `+++REFRESH+++` |
|spring.cloud.kubernetes.secrets.enable-api | `+++false+++` |
|spring.cloud.kubernetes.secrets.enabled | `+++true+++` | Enable the Secrets property source locator.
|spring.cloud.kubernetes.secrets.fail-fast | `+++false+++` |
|spring.cloud.kubernetes.secrets.include-profile-specific-sources | `+++true+++` |
|spring.cloud.kubernetes.secrets.labels | |
|spring.cloud.kubernetes.secrets.name | |
|spring.cloud.kubernetes.secrets.namespace | |
|spring.cloud.kubernetes.secrets.paths | |
|spring.cloud.kubernetes.secrets.retry | |
|spring.cloud.kubernetes.secrets.sources | |
|spring.cloud.kubernetes.secrets.use-name-as-prefix | `+++false+++` |
|===

View File

@@ -1,13 +0,0 @@
:numbered!:
[appendix]
[[common-application-properties]]
= Common application properties
:page-section-summary-toc: 1
Various properties can be specified inside your `application.properties` file, inside your `application.yml` file, or as command line switches.
This appendix provides a list of common {project-full-name} properties and references to the underlying classes that consume them.
NOTE: Property contributions can come from additional jar files on your classpath, so you should not consider this an exhaustive list.
Also, you can define your own properties.

View File

@@ -1,176 +0,0 @@
[[discoveryclient-for-kubernetes]]
= DiscoveryClient for Kubernetes
This project provides an implementation of https://github.com/spring-cloud/spring-cloud-commons/blob/master/spring-cloud-commons/src/main/java/org/springframework/cloud/client/discovery/DiscoveryClient.java[Discovery Client]
for https://kubernetes.io[Kubernetes].
This client lets you query Kubernetes endpoints (see https://kubernetes.io/docs/user-guide/services/[services]) by name.
A service is typically exposed by the Kubernetes API server as a collection of endpoints that represent `http` and `https` addresses and that a client can
access from a Spring Boot application running as a pod.
DiscoveryClient can also find services of type `ExternalName` (see https://kubernetes.io/docs/concepts/services-networking/service/#externalname[ExternalName services]). At the moment, external name support type of services is only available if the following property `spring.cloud.kubernetes.discovery.include-external-name-services` is set to `true` and only in the `fabric8` implementation. In a later release, support will be added for the kubernetes native client also.
This is something that you get for free by adding the following dependency inside your project:
====
HTTP Based `DiscoveryClient`
[source,xml]
----
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes-discoveryclient</artifactId>
</dependency>
----
====
NOTE: `spring-cloud-starter-kubernetes-discoveryclient` is designed to be used with the
xref:spring-cloud-kubernetes-discoveryserver.adoc#spring-cloud-kubernetes-discoveryserver[Spring Cloud Kubernetes DiscoveryServer].
====
Fabric8 Kubernetes Client
[source,xml]
----
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes-fabric8</artifactId>
</dependency>
----
====
====
Kubernetes Java Client
[source,xml]
----
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes-client</artifactId>
</dependency>
----
====
To enable loading of the `DiscoveryClient`, add `@EnableDiscoveryClient` to the according configuration or application class, as the following example shows:
====
[source,java]
----
@SpringBootApplication
@EnableDiscoveryClient
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
----
====
Then you can inject the client in your code simply by autowiring it, as the following example shows:
====
[source,java]
----
@Autowired
private DiscoveryClient discoveryClient;
----
====
You can choose to enable `DiscoveryClient` from all namespaces by setting the following property in `application.properties`:
====
[source]
----
spring.cloud.kubernetes.discovery.all-namespaces=true
----
====
To discover services and endpoints only from specified namespaces you should set property `all-namespaces` to `false` and set the following property in `application.properties` (in this example namespaces are: `ns1` and `ns2`).
====
[source]
----
spring.cloud.kubernetes.discovery.namespaces[0]=ns1
spring.cloud.kubernetes.discovery.namespaces[1]=ns2
----
====
To discover service endpoint addresses that are not marked as "ready" by the kubernetes api server, you can set the following property in `application.properties` (default: false):
====
[source]
----
spring.cloud.kubernetes.discovery.include-not-ready-addresses=true
----
NOTE: This might be useful when discovering services for monitoring purposes, and would enable inspecting the `/health` endpoint of not-ready service instances.
====
If your service exposes multiple ports, you will need to specify which port the `DiscoveryClient` should use.
The `DiscoveryClient` will choose the port using the following logic.
1. If the service has a label `primary-port-name` it will use the port with the name specified in the label's value.
2. If no label is present, then the port name specified in `spring.cloud.kubernetes.discovery.primary-port-name` will be used.
3. If neither of the above are specified it will use the port named `https`.
4. If none of the above conditions are met it will use the port named `http`.
5. As a last resort it wil pick the first port in the list of ports.
WARNING: The last option may result in non-deterministic behaviour.
Please make sure to configure your service and/or application accordingly.
By default all of the ports and their names will be added to the metadata of the `ServiceInstance`.
As said before, if you want to get the list of `ServiceInstance` to also include the `ExternalName` type services, you need to enable that support via: `spring.cloud.kubernetes.discovery.include-external-name-services=true`. As such, when calling `DiscoveryClient::getInstances` those will be returned also. You can distinguish between `ExternalName` and any other types by inspecting `ServiceInstance::getMetadata` and lookup for a field called `type`. This will be the type of the service returned : `ExternalName`/`ClusterIP`, etc.
`ServiceInstance` can include the labels and annotations of specific pods from the underlying service instance. To obtain such information, you need to also enable:
`spring.cloud.kubernetes.discovery.metadata.add-pod-labels=true` and/or `spring.cloud.kubernetes.discovery.metadata.add-pod-annotations=true`. At the moment, such functionality is present only in the fabric8 client implementation, but will be added to the kubernetes native client in a later release.
If, for any reason, you need to disable the `DiscoveryClient`, you can set the following property in `application.properties`:
====
[source]
----
spring.cloud.kubernetes.discovery.enabled=false
----
====
Some Spring Cloud components use the `DiscoveryClient` in order to obtain information about the local service instance. For
this to work, you need to align the Kubernetes service name with the `spring.application.name` property.
NOTE: `spring.application.name` has no effect as far as the name registered for the application within Kubernetes
Spring Cloud Kubernetes can also watch the Kubernetes service catalog for changes and update the
`DiscoveryClient` implementation accordingly. By "watch" we mean that we will publish a heartbeat event every `spring.cloud.kubernetes.discovery.catalog-services-watch-delay`
milliseconds (by default it is `30000`). The heartbeat event will contain the target references (and their namespaces of the addresses of all endpoints
(for the exact details of what will get returned you can take a look inside `KubernetesCatalogWatch`). This is an implementation detail, and listeners of the heartbeat event
should not rely on the details. Instead, they should see if there are differences between two subsequent heartbeats via `equals` method. We will take care to return a correct implementation that adheres to the equals contract.
The endpoints will be queried in either :
- all namespaces (enabled via `spring.cloud.kubernetes.discovery.all-namespaces=true`)
- specific namespaces (enabled via `spring.cloud.kubernetes.discovery.namespaces`), for example:
```
spring:
cloud:
kubernetes:
discovery:
namespaces:
- namespace-a
- namespace-b
```
- we will use: xref:property-source-config/namespace-resolution.adoc[Namespace Resolution] if the above two paths are not taken.
In order to enable this functionality you need to add
`@EnableScheduling` on a configuration class in your application.
By default, we use the `Endpoints`(see https://kubernetes.io/docs/concepts/services-networking/service/#endpoints) API to find out the current state of services. There is another way though, via `EndpointSlices` (https://kubernetes.io/docs/concepts/services-networking/endpoint-slices/). Such support can be enabled via a property: `spring.cloud.kubernetes.discovery.use-endpoint-slices=true` (by default it is `false`). Of course, your cluster has to support it also. As a matter of fact, if you enable this property, but your cluster does not support it, we will fail starting the application. If you decide to enable such support, you also need proper Role/ClusterRole set-up. For example:
```
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: namespace-reader
rules:
- apiGroups: ["discovery.k8s.io"]
resources: ["endpointslices"]
verbs: ["get", "list", "watch"]
```

View File

@@ -1,13 +0,0 @@
[[kubernetes-native-service-discovery]]
= Kubernetes native service discovery
:page-section-summary-toc: 1
Kubernetes itself is capable of (server side) service discovery (see: https://kubernetes.io/docs/concepts/services-networking/service/#discovering-services).
Using native kubernetes service discovery ensures compatibility with additional tooling, such as Istio (https://istio.io), a service mesh that is capable of load balancing, circuit breaker, failover, and much more.
The caller service then need only refer to names resolvable in a particular Kubernetes cluster. A simple implementation might use a spring `RestTemplate` that refers to a fully qualified domain name (FQDN), such as `https://{service-name}.{namespace}.svc.{cluster}.local:{service-port}`.
Additionally, you can use Hystrix for:
* Circuit breaker implementation on the caller side, by annotating the spring boot application class with `@EnableCircuitBreaker`
* Fallback functionality, by annotating the respective method with `@HystrixCommand(fallbackMethod=`

View File

@@ -1,19 +0,0 @@
[[examples]]
= Examples
:page-section-summary-toc: 1
Spring Cloud Kubernetes tries to make it transparent for your applications to consume Kubernetes Native Services by
following the Spring Cloud interfaces.
In your applications, you need to add the `spring-cloud-kubernetes-discovery` dependency to your classpath and remove any other dependency that contains a `DiscoveryClient` implementation (that is, a Eureka discovery client).
The same applies for `PropertySourceLocator`, where you need to add to the classpath the `spring-cloud-kubernetes-config` and remove any other dependency that contains a `PropertySourceLocator` implementation (that is, a configuration server client).
The following projects highlight the usage of these dependencies and demonstrate how you can use these libraries from any Spring Boot application:
* https://github.com/spring-cloud/spring-cloud-kubernetes/tree/master/spring-cloud-kubernetes-examples[Spring Cloud Kubernetes Examples]: the ones located inside this repository.
* Spring Cloud Kubernetes Full Example: Minions and Boss
** https://github.com/salaboy/spring-cloud-k8s-minion[Minion]
** https://github.com/salaboy/spring-cloud-k8s-boss[Boss]
* Spring Cloud Kubernetes Full Example: https://github.com/salaboy/s1p_docs[SpringOne Platform Tickets Service]
* https://github.com/salaboy/s1p_gateway[Spring Cloud Gateway with Spring Cloud Kubernetes Discovery and Config]
* https://github.com/salaboy/showcase-admin-tool[Spring Boot Admin with Spring Cloud Kubernetes Discovery and Config]

View File

@@ -1,75 +0,0 @@
[[starters]]
= Starters
Starters are convenient dependency descriptors you can include in your
application. Include a starter to get the dependencies and Spring Boot
auto-configuration for a feature set. Starters that begin with `spring-cloud-starter-kubernetes-fabric8`
provide implementations using the https://github.com/fabric8io/kubernetes-client[Fabric8 Kubernetes Java Client].
Starters that begin with
`spring-cloud-starter-kubernetes-client` provide implementations using the https://github.com/kubernetes-client/java[Kubernetes Java Client].
[cols="a,d"]
|===
| Starter | Features
| [source,xml]
.Fabric8 Dependency
----
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes-fabric8</artifactId>
</dependency>
----
[source,xml]
.Kubernetes Client Dependency
----
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes-client</artifactId>
</dependency>
----
| <<DiscoveryClient for Kubernetes,Discovery Client>> implementation that
resolves service names to Kubernetes Services.
| [source,xml]
.Fabric8 Dependency
----
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes-fabric8-config</artifactId>
</dependency>
----
[source,xml]
.Kubernetes Client Dependency
----
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes-client-config</artifactId>
</dependency>
----
| Load application properties from Kubernetes
xref:property-source-config/configmap-propertysource.adoc[ConfigMaps] and <<Secrets PropertySource,Secrets>>.
xref:property-source-config/propertysource-reload.adoc[Reload] application properties when a ConfigMap or
Secret changes.
| [source,xml]
.Fabric8 Dependency
----
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes-fabric8-all</artifactId>
</dependency>
----
[source,xml]
.Kubernetes Client Dependency
----
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes-client-all</artifactId>
</dependency>
----
| All Spring Cloud Kubernetes features.
|===

View File

@@ -1,9 +0,0 @@
[[info-contributor]]
= Info Contributor
:page-section-summary-toc: 1
Spring Cloud Kubernetes includes an `InfoContributor` which adds Pod information to
Spring Boot's `/info` Acturator endpoint.
You can disable this `InfoContributor` by setting `management.info.kubernetes.enabled`
to `false` in `application.[properties | yaml]`.

View File

@@ -1,46 +0,0 @@
[[kubernetes-ecosystem-awareness]]
= Kubernetes Ecosystem Awareness
All features described earlier in this guide work equally well, regardless of whether your application is running inside
Kubernetes. This is really helpful for development and troubleshooting.
From a development point of view, this lets you start your Spring Boot application and debug one
of the modules that is part of this project. You need not deploy it in Kubernetes,
as the code of the project relies on the
https://github.com/fabric8io/kubernetes-client[Fabric8 Kubernetes Java client], which is a fluent DSL that can
communicate by using `http` protocol to the REST API of the Kubernetes Server.
Kubernetes awareness is based on Spring Boot API, specifically on https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/autoconfigure/condition/ConditionalOnCloudPlatform.html[ConditionalOnCloudPlatform].
That property will auto-detect if your application is currently deployed in kubernetes or not. It is possible to override
that setting via `spring.main.cloud-platform`.
For example, if you need to test some features, but do not want to deploy to a cluster, it is enough to set the:
`spring.main.cloud-platform=KUBERNETES`. This will make `spring-cloud-kubernetes` act as-if it is deployed in a real cluster.
NOTE: If you have `spring-cloud-starter-bootstrap` on your classpath or are setting `spring.cloud.bootstrap.enabled=true` then
you will have to set `spring.main.cloud-platform` should be set in `bootstrap.{properties|yml}`
(or the profile specific one). Also note that these properties: `spring.cloud.kubernetes.config.enabled` and `spring.cloud.kubernetes.secrets.enabled`
will only take effect when set in `bootstrap.{properties|yml}` when you have `spring-cloud-starter-bootstrap` on your classpath or are setting `spring.cloud.bootstrap.enabled=true`.
[[breaking-changes-in-3-0-x]]
== Breaking Changes In 3.0.x
In versions of Spring Cloud Kubernetes prior to `3.0.x`, Kubernetes awareness was implemented using `spring.cloud.kubernetes.enabled` property. This
property was removed and is un-supported. Instead, we use Spring Boot API: https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/autoconfigure/condition/ConditionalOnCloudPlatform.html[ConditionalOnCloudPlatform].
If it is needed to explicitly enable or disable this awareness, use `spring.main.cloud-platform=NONE/KUBERNETES`.
[[kubernetes-profile-autoconfiguration]]
== Kubernetes Profile Autoconfiguration
When the application runs as a pod inside Kubernetes, a Spring profile named `kubernetes` automatically gets activated.
This lets you customize the configuration, to define beans that are applied when the Spring Boot application is deployed
within the Kubernetes platform (for example, different development and production configuration).
[[istio-awareness]]
== Istio Awareness
When you include the `spring-cloud-kubernetes-fabric8-istio` module in the application classpath, a new profile is added to the application,
provided the application is running inside a Kubernetes Cluster with https://istio.io[Istio] installed. You can then use
spring `@Profile("istio")` annotations in your Beans and `@Configuration` classes.
The Istio awareness module uses `me.snowdrop:istio-client` to interact with Istio APIs, letting us discover traffic rules, circuit breakers, and so on,
making it easy for our Spring Boot applications to consume this data to dynamically configure themselves according to the environment.

View File

@@ -1,31 +0,0 @@
[[leader-election]]
= Leader Election
The Spring Cloud Kubernetes leader election mechanism implements the leader election API of Spring Integration using a Kubernetes ConfigMap.
Multiple application instances compete for leadership, but leadership will only be granted to one.
When granted leadership, a leader application receives an `OnGrantedEvent` application event with leadership `Context`.
Applications periodically attempt to gain leadership, with leadership granted to the first caller.
A leader will remain a leader until either it is removed from the cluster, or it yields its leadership.
When leadership removal occurs, the previous leader receives `OnRevokedEvent` application event.
After removal, any instances in the cluster may become the new leader, including the old leader.
To include it in your project, add the following dependency.
====
Fabric8 Leader Implementation
[source,xml]
----
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-fabric8-leader</artifactId>
</dependency>
----
====
To specify the name of the configmap used for leader election use the following property.
====
[source,properties]
----
spring.cloud.kubernetes.leader.config-map-name=leader
----
====

View File

@@ -1,44 +0,0 @@
[[loadbalancer-for-kubernetes]]
= LoadBalancer for Kubernetes
This project includes Spring Cloud Load Balancer for load balancing based on Kubernetes Endpoints and provides implementation of load balancer based on Kubernetes Service.
To include it to your project add the following dependency.
====
Fabric8 Implementation
[source,xml]
----
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes-fabric8-loadbalancer</artifactId>
</dependency>
----
====
====
Kubernetes Java Client Implementation
[source,xml]
----
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes-client-loadbalancer</artifactId>
</dependency>
----
====
To enable load balancing based on Kubernetes Service name use the following property. Then load balancer would try to call application using address, for example `service-a.default.svc.cluster.local`
====
[source]
----
spring.cloud.kubernetes.loadbalancer.mode=SERVICE
----
====
To enabled load balancing across all namespaces use the following property. Property from `spring-cloud-kubernetes-discovery` module is respected.
====
[source]
----
spring.cloud.kubernetes.discovery.all-namespaces=true
----
====
If a service needs to be accessed over HTTPS you need to add a label or annotation to your service definition with the name `secured` and the value `true` and the load balancer will then use HTTPS to make requests to the service.

View File

@@ -1,11 +0,0 @@
[[other-resources]]
= Other Resources
:page-section-summary-toc: 1
This section lists other resources, such as presentations (slides) and videos about Spring Cloud Kubernetes.
* https://salaboy.com/2018/09/27/the-s1p-experience/[S1P Spring Cloud on PKS]
* https://salaboy.com/2018/07/18/ljc-july-18-spring-cloud-docker-k8s/[Spring Cloud, Docker, Kubernetes -> London Java Community July 2018]
Please feel free to submit other resources through pull requests to https://github.com/spring-cloud/spring-cloud-kubernetes[this repository].

View File

@@ -1,14 +0,0 @@
[[pod-health-indicator]]
= Pod Health Indicator
:page-section-summary-toc: 1
Spring Boot uses https://github.com/spring-projects/spring-boot/blob/master/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthEndpoint.java[`HealthIndicator`] to expose info about the health of an application.
That makes it really useful for exposing health-related information to the user and makes it a good fit for use as https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/[readiness probes].
The Kubernetes health indicator (which is part of the core module) exposes the following info:
* Pod name, IP address, namespace, service account, node name, and its IP address
* A flag that indicates whether the Spring Boot application is internal or external to Kubernetes
You can disable this `HealthContributor` by setting `management.health.kubernetes.enabled`
to `false` in `application.[properties | yaml]`.

View File

@@ -1,18 +0,0 @@
[[kubernetes-propertysource-implementations]]
= Kubernetes PropertySource implementations
:page-section-summary-toc: 1
The most common approach to configuring your Spring Boot application is to create an `application.properties` or `application.yaml` or
an `application-profile.properties` or `application-profile.yaml` file that contains key-value pairs that provide customization values to your
application or Spring Boot starters. You can override these properties by specifying system properties or environment
variables.
To enable this functionality you need to set `spring.config.import=kubernetes:` in your application's configuration properties.
Currently you can not specify a ConfigMap or Secret to load using `spring.config.import`, by default Spring Cloud Kubernetes
will load a ConfigMap and/or Secret based on the `spring.application.name` property. If `spring.application.name` is not set it will
load a ConfigMap and/or Secret with the name `application`.
If you would like to load Kubernetes ``PropertySource``s during the bootstrap phase like it worked prior to the 3.0.x release
you can either add `spring-cloud-starter-bootstrap` to your application's classpath or set `spring.cloud.bootstrap.enabled=true`
as an environment variable.

View File

@@ -1,620 +0,0 @@
[[configmap-propertysource]]
= Using a `ConfigMap` `PropertySource`
Kubernetes provides a resource named https://kubernetes.io/docs/user-guide/configmap/[`ConfigMap`] to externalize the
parameters to pass to your application in the form of key-value pairs or embedded `application.properties` or `application.yaml` files.
The link:https://github.com/spring-cloud/spring-cloud-kubernetes/tree/master/spring-cloud-kubernetes-fabric8-config[Spring Cloud Kubernetes Config] project makes Kubernetes `ConfigMap` instances available
during application startup and triggers hot reloading of beans or Spring context when changes are detected on
observed `ConfigMap` instances.
Everything that follows is explained mainly referring to examples using ConfigMaps, but the same stands for
Secrets, i.e.: every feature is supported for both.
The default behavior is to create a `Fabric8ConfigMapPropertySource` (or a `KubernetesClientConfigMapPropertySource`) based on a Kubernetes `ConfigMap` that has a `metadata.name` value of either the name of
your Spring application (as defined by its `spring.application.name` property) or a custom name defined within the
`application.properties` file under the following key: `spring.cloud.kubernetes.config.name`.
However, more advanced configuration is possible where you can use multiple `ConfigMap` instances.
The `spring.cloud.kubernetes.config.sources` list makes this possible.
For example, you could define the following `ConfigMap` instances:
====
[source,yaml]
----
spring:
application:
name: cloud-k8s-app
cloud:
kubernetes:
config:
name: default-name
namespace: default-namespace
sources:
# Spring Cloud Kubernetes looks up a ConfigMap named c1 in namespace default-namespace
- name: c1
# Spring Cloud Kubernetes looks up a ConfigMap named default-name in whatever namespace n2
- namespace: n2
# Spring Cloud Kubernetes looks up a ConfigMap named c3 in namespace n3
- namespace: n3
name: c3
----
====
In the preceding example, if `spring.cloud.kubernetes.config.namespace` had not been set,
the `ConfigMap` named `c1` would be looked up in the namespace that the application runs.
See xref:property-source-config/namespace-resolution.adoc[Namespace resolution] to get a better understanding of how the namespace
of the application is resolved.
Any matching `ConfigMap` that is found is processed as follows:
* Apply individual configuration properties.
* Apply as `yaml` (or `properties`) the content of any property that is named by the value of `spring.application.name`
(if it's not present, by `application.yaml/properties`)
* Apply as a properties file the content of the above name + each active profile.
An example should make a lot more sense. Let's suppose that `spring.application.name=my-app` and that
we have a single active profile called `k8s`. For a configuration as below:
====
[source]
----
kind: ConfigMap
apiVersion: v1
metadata:
name: my-app
data:
my-app.yaml: |-
...
my-app-k8s.yaml: |-
..
my-app-dev.yaml: |-
..
someProp: someValue
----
====
These is what we will end-up loading:
- `my-app.yaml` treated as a file
- `my-app-k8s.yaml` treated as a file
- `my-app-dev.yaml` _ignored_, since `dev` is _not_ an active profile
- `someProp: someValue` plain property
The single exception to the aforementioned flow is when the `ConfigMap` contains a *single* key that indicates
the file is a YAML or properties file. In that case, the name of the key does NOT have to be `application.yaml` or
`application.properties` (it can be anything) and the value of the property is treated correctly.
This features facilitates the use case where the `ConfigMap` was created by using something like the following:
====
[source]
----
kubectl create configmap game-config --from-file=/path/to/app-config.yaml
----
====
Assume that we have a Spring Boot application named `demo` that uses the following properties to read its thread pool
configuration.
* `pool.size.core`
* `pool.size.maximum`
This can be externalized to config map in `yaml` format as follows:
====
[source,yaml]
----
kind: ConfigMap
apiVersion: v1
metadata:
name: demo
data:
pool.size.core: 1
pool.size.max: 16
----
====
Individual properties work fine for most cases. However, sometimes, embedded `yaml` is more convenient. In this case, we
use a single property named `application.yaml` to embed our `yaml`, as follows:
====
[source,yaml]
----
kind: ConfigMap
apiVersion: v1
metadata:
name: demo
data:
application.yaml: |-
pool:
size:
core: 1
max:16
----
====
The following example also works:
====
[source,yaml]
----
kind: ConfigMap
apiVersion: v1
metadata:
name: demo
data:
custom-name.yaml: |-
pool:
size:
core: 1
max:16
----
====
You can also define the search to happen based on labels, for example:
====
[source,yaml]
----
spring:
application:
name: labeled-configmap-with-prefix
cloud:
kubernetes:
config:
enableApi: true
useNameAsPrefix: true
namespace: spring-k8s
sources:
- labels:
letter: a
----
====
This will search for every configmap in namespace `spring-k8s` that has labels `{letter : a}`. The important
thing to notice here is that unlike reading a configmap by name, this can result in _multiple_ config maps read.
As usual, the same feature is supported for secrets.
You can also configure Spring Boot applications differently depending on active profiles that are merged together
when the `ConfigMap` is read. You can provide different property values for different profiles by using an
`application.properties` or `application.yaml` property, specifying profile-specific values, each in their own document
(indicated by the `---` sequence), as follows:
====
[source,yaml]
----
kind: ConfigMap
apiVersion: v1
metadata:
name: demo
data:
application.yml: |-
greeting:
message: Say Hello to the World
farewell:
message: Say Goodbye
---
spring:
profiles: development
greeting:
message: Say Hello to the Developers
farewell:
message: Say Goodbye to the Developers
---
spring:
profiles: production
greeting:
message: Say Hello to the Ops
----
====
In the preceding case, the configuration loaded into your Spring Application with the `development` profile is as follows:
====
[source,yaml]
----
greeting:
message: Say Hello to the Developers
farewell:
message: Say Goodbye to the Developers
----
====
However, if the `production` profile is active, the configuration becomes:
====
[source,yaml]
----
greeting:
message: Say Hello to the Ops
farewell:
message: Say Goodbye
----
====
If both profiles are active, the property that appears last within the `ConfigMap` overwrites any preceding values.
Another option is to create a different config map per profile and spring boot will automatically fetch it based
on active profiles
====
[source,yaml]
----
kind: ConfigMap
apiVersion: v1
metadata:
name: demo
data:
application.yml: |-
greeting:
message: Say Hello to the World
farewell:
message: Say Goodbye
----
====
====
[source,yaml]
----
kind: ConfigMap
apiVersion: v1
metadata:
name: demo-development
data:
application.yml: |-
spring:
profiles: development
greeting:
message: Say Hello to the Developers
farewell:
message: Say Goodbye to the Developers
----
====
====
[source,yaml]
----
kind: ConfigMap
apiVersion: v1
metadata:
name: demo-production
data:
application.yml: |-
spring:
profiles: production
greeting:
message: Say Hello to the Ops
farewell:
message: Say Goodbye
----
====
To tell Spring Boot which `profile` should be enabled see the https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.profiles[Spring Boot documentation].
One option for activating a specific profile when deploying to Kubernetes is to launch your Spring Boot application with an environment variable that you can define in the PodSpec at the container specification.
Deployment resource file, as follows:
====
[source,yaml]
----
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment-name
labels:
app: deployment-name
spec:
replicas: 1
selector:
matchLabels:
app: deployment-name
template:
metadata:
labels:
app: deployment-name
spec:
containers:
- name: container-name
image: your-image
env:
- name: SPRING_PROFILES_ACTIVE
value: "development"
----
====
You could run into a situation where there are multiple configs maps that have the same property names. For example:
====
[source,yaml]
----
kind: ConfigMap
apiVersion: v1
metadata:
name: config-map-one
data:
application.yml: |-
greeting:
message: Say Hello from one
----
====
and
====
[source,yaml]
----
kind: ConfigMap
apiVersion: v1
metadata:
name: config-map-two
data:
application.yml: |-
greeting:
message: Say Hello from two
----
====
Depending on the order in which you place these in `bootstrap.yaml|properties`, you might end up with an un-expected result (the last config map wins). For example:
====
[source,yaml]
----
spring:
application:
name: cloud-k8s-app
cloud:
kubernetes:
config:
namespace: default-namespace
sources:
- name: config-map-two
- name: config-map-one
----
====
will result in property `greetings.message` being `Say Hello from one`.
There is a way to change this default configuration by specifying `useNameAsPrefix`. For example:
====
[source,yaml]
----
spring:
application:
name: with-prefix
cloud:
kubernetes:
config:
useNameAsPrefix: true
namespace: default-namespace
sources:
- name: config-map-one
useNameAsPrefix: false
- name: config-map-two
----
====
Such a configuration will result in two properties being generated:
- `greetings.message` equal to `Say Hello from one`.
- `config-map-two.greetings.message` equal to `Say Hello from two`
Notice that `spring.cloud.kubernetes.config.useNameAsPrefix` has a _lower_ priority than `spring.cloud.kubernetes.config.sources.useNameAsPrefix`.
This allows you to set a "default" strategy for all sources, at the same time allowing to override only a few.
If using the config map name is not an option, you can specify a different strategy, called : `explicitPrefix`. Since this is an _explicit_ prefix that
you select, it can only be supplied to the `sources` level. At the same time it has a higher priority than `useNameAsPrefix`. Let's suppose we have a third config map with these entries:
====
[source,yaml]
----
kind: ConfigMap
apiVersion: v1
metadata:
name: config-map-three
data:
application.yml: |-
greeting:
message: Say Hello from three
----
====
A configuration like the one below:
====
[source,yaml]
----
spring:
application:
name: with-prefix
cloud:
kubernetes:
config:
useNameAsPrefix: true
namespace: default-namespace
sources:
- name: config-map-one
useNameAsPrefix: false
- name: config-map-two
explicitPrefix: two
- name: config-map-three
----
====
will result in three properties being generated:
- `greetings.message` equal to `Say Hello from one`.
- `two.greetings.message` equal to `Say Hello from two`.
- `config-map-three.greetings.message` equal to `Say Hello from three`.
The same way you configure a prefix for configmaps, you can do it for secrets also; both for secrets that are based on name
and the ones based on labels. For example:
====
[source.yaml]
----
spring:
application:
name: prefix-based-secrets
cloud:
kubernetes:
secrets:
enableApi: true
useNameAsPrefix: true
namespace: spring-k8s
sources:
- labels:
letter: a
useNameAsPrefix: false
- labels:
letter: b
explicitPrefix: two
- labels:
letter: c
- labels:
letter: d
useNameAsPrefix: true
- name: my-secret
----
====
The same processing rules apply when generating property source as for config maps. The only difference is that
potentially, looking up secrets by labels can mean that we find more than one source. In such a case, prefix (if specified via `useNameAsPrefix`)
will be the names of all secrets found for those particular labels.
One more thing to bear in mind is that we support `prefix` per _source_, not per secret. The easiest way to explain this is via an example:
====
[source.yaml]
----
spring:
application:
name: prefix-based-secrets
cloud:
kubernetes:
secrets:
enableApi: true
useNameAsPrefix: true
namespace: spring-k8s
sources:
- labels:
color: blue
useNameAsPrefix: true
----
====
Suppose that a query matching such a label will provide two secrets as a result: `secret-a` and `secret-b`.
Both of these secrets have the same property name: `color=sea-blue` and `color=ocean-blue`. It is undefined which
`color` will end-up as part of property sources, but the prefix for it will be `secret-a.secret-b`
(concatenated sorted naturally, names of the secrets).
If you need more fine-grained results, adding more labels to identify the secret uniquely would be an option.
By default, besides reading the config map that is specified in the `sources` configuration, Spring will also try to read
all properties from "profile aware" sources. The easiest way to explain this is via an example. Let's suppose your application
enables a profile called "dev" and you have a configuration like the one below:
====
[source,yaml]
----
spring:
application:
name: spring-k8s
cloud:
kubernetes:
config:
namespace: default-namespace
sources:
- name: config-map-one
----
====
Besides reading the `config-map-one`, Spring will also try to read `config-map-one-dev`; in this particular order. Each active profile
generates such a profile aware config map.
Though your application should not be impacted by such a config map, it can be disabled if needed:
====
[source,yaml]
----
spring:
application:
name: spring-k8s
cloud:
kubernetes:
config:
includeProfileSpecificSources: false
namespace: default-namespace
sources:
- name: config-map-one
includeProfileSpecificSources: false
----
====
Notice that just like before, there are two levels where you can specify this property: for all config maps or
for individual ones; the latter having a higher priority.
NOTE: You should check the security configuration section. To access config maps from inside a pod you need to have the correct
Kubernetes service accounts, roles and role bindings.
Another option for using `ConfigMap` instances is to mount them into the Pod by running the Spring Cloud Kubernetes application
and having Spring Cloud Kubernetes read them from the file system.
NOTE: This feature is deprecated and will be removed in a future release (Use `spring.config.import` instead).
This behavior is controlled by the `spring.cloud.kubernetes.config.paths` property. You can use it in
addition to or instead of the mechanism described earlier.
`spring.cloud.kubernetes.config.paths` expects a List of full paths to each property file, because directories are not being recursively parsed. For example:
```
spring:
cloud:
kubernetes:
config:
paths:
- /tmp/application.properties
- /var/application.yaml
```
NOTE: If you use `spring.cloud.kubernetes.config.paths` or `spring.cloud.kubernetes.secrets.path` the automatic reload
functionality will not work. You will need to make a `POST` request to the `/actuator/refresh` endpoint or
restart/redeploy the application.
[#config-map-fail-fast]
In some cases, your application may be unable to load some of your `ConfigMaps` using the Kubernetes API.
If you want your application to fail the start-up process in such cases, you can set
`spring.cloud.kubernetes.config.fail-fast=true` to make the application start-up fail with an Exception.
[#config-map-retry]
You can also make your application retry loading `ConfigMap` property sources on a failure. First, you need to
set `spring.cloud.kubernetes.config.fail-fast=true`. Then you need to add `spring-retry`
and `spring-boot-starter-aop` to your classpath. You can configure retry properties such as
the maximum number of attempts, backoff options like initial interval, multiplier, max interval by setting the
`spring.cloud.kubernetes.config.retry.*` properties.
NOTE: If you already have `spring-retry` and `spring-boot-starter-aop` on the classpath for some reason
and want to enable fail-fast, but do not want retry to be enabled; you can disable retry for `ConfigMap` `PropertySources`
by setting `spring.cloud.kubernetes.config.retry.enabled=false`.
.Properties:
[options="header,footer"]
|===
| Name | Type | Default | Description
| `spring.cloud.kubernetes.config.enabled` | `Boolean` | `true` | Enable ConfigMaps `PropertySource`
| `spring.cloud.kubernetes.config.name` | `String` | `${spring.application.name}` | Sets the name of `ConfigMap` to look up
| `spring.cloud.kubernetes.config.namespace` | `String` | Client namespace | Sets the Kubernetes namespace where to lookup
| `spring.cloud.kubernetes.config.paths` | `List` | `null` | Sets the paths where `ConfigMap` instances are mounted
| `spring.cloud.kubernetes.config.enableApi` | `Boolean` | `true` | Enable or disable consuming `ConfigMap` instances through APIs
| `spring.cloud.kubernetes.config.fail-fast` | `Boolean` | `false` | Enable or disable failing the application start-up when an error occurred while loading a `ConfigMap`
| `spring.cloud.kubernetes.config.retry.enabled` | `Boolean` | `true` | Enable or disable config retry.
| `spring.cloud.kubernetes.config.retry.initial-interval` | `Long` | `1000` | Initial retry interval in milliseconds.
| `spring.cloud.kubernetes.config.retry.max-attempts` | `Integer` | `6` | Maximum number of attempts.
| `spring.cloud.kubernetes.config.retry.max-interval` | `Long` | `2000` | Maximum interval for backoff.
| `spring.cloud.kubernetes.config.retry.multiplier` | `Double` | `1.1` | Multiplier for next interval.
|===

View File

@@ -1,70 +0,0 @@
[[namespace-label-filtering]]
= Reload namespace and label filtering
By default, a namespace chosen using the steps outlined in xref:property-source-config/namespace-resolution.adoc[Namespace resolution] will be used to listen to changes
in configmaps and secrets. i.e.: if you do not tell reload what namespaces and configmaps/secrets to watch for,
it will watch all configmaps/secrets from the namespace that will be computed using the above algorithm.
On the other hand, you can define a more fine-grained approach. For example, you can specify the namespaces where
changes will be monitored:
====
[source,yaml]
----
spring:
application:
name: event-reload
cloud:
kubernetes:
reload:
enabled: true
strategy: shutdown
mode: event
namespaces:
- my-namespace
----
====
Such a configuration will make the app watch changes only in the `my-namespace` namespace. Mind that this will
watch _all_ configmaps/secrets (depending on which one you enable). If you want an even more fine-grained approach,
you can enable "label-filtering". First we need to enable such support via : `enable-reload-filtering: true`
====
[source,yaml]
----
spring:
application:
name: event-reload
cloud:
kubernetes:
reload:
enabled: true
strategy: shutdown
mode: event
namespaces:
- my-namespaces
monitoring-config-maps: true
enable-reload-filtering: true
----
====
What this will do, is watch configmaps/secrets that only have the `spring.cloud.kubernetes.config.informer.enabled: true` label.
.Properties:
[options="header,footer"]
|===
| Name | Type | Default | Description
| `spring.cloud.kubernetes.reload.enabled` | `Boolean` | `false` | Enables monitoring of property sources and configuration reload
| `spring.cloud.kubernetes.reload.monitoring-config-maps` | `Boolean` | `true` | Allow monitoring changes in config maps
| `spring.cloud.kubernetes.reload.monitoring-secrets` | `Boolean` | `false` | Allow monitoring changes in secrets
| `spring.cloud.kubernetes.reload.strategy` | `Enum` | `refresh` | The strategy to use when firing a reload (`refresh`, `restart_context`, or `shutdown`)
| `spring.cloud.kubernetes.reload.mode` | `Enum` | `event` | Specifies how to listen for changes in property sources (`event` or `polling`)
| `spring.cloud.kubernetes.reload.period` | `Duration`| `15s` | The period for verifying changes when using the `polling` strategy
| `spring.cloud.kubernetes.reload.namespaces` | `String[]`| | namespaces where we should watch for changes
| `spring.cloud.kubernetes.reload.enable-reload-filtering` | `String` | | enabled labeled filtering for reload functionality
|===
Notes:
* You should not use properties under `spring.cloud.kubernetes.reload` in config maps or secrets. Changing such properties at runtime may lead to unexpected results.
* Deleting a property or the whole config map does not restore the original state of the beans when you use the `refresh` level.

View File

@@ -1,40 +0,0 @@
[[namespace-resolution]]
= Namespace resolution
Finding an application namespace happens on a best-effort basis. There are some steps that we iterate in order
to find it. The easiest and most common one, is to specify it in the proper configuration, for example:
====
[source,yaml]
----
spring:
application:
name: app
cloud:
kubernetes:
secrets:
name: secret
namespace: default
sources:
# Spring Cloud Kubernetes looks up a Secret named 'a' in namespace 'default'
- name: a
# Spring Cloud Kubernetes looks up a Secret named 'secret' in namespace 'b'
- namespace: b
# Spring Cloud Kubernetes looks up a Secret named 'd' in namespace 'c'
- namespace: c
name: d
----
====
Remember that the same can be done for config maps. If such a namespace is not specified, it will be read (in this order):
1. from property `spring.cloud.kubernetes.client.namespace`
2. from a String residing in a file denoted by `spring.cloud.kubernetes.client.serviceAccountNamespacePath` property
3. from a String residing in `/var/run/secrets/kubernetes.io/serviceaccount/namespace` file
(kubernetes default namespace path)
4. from a designated client method call (for example fabric8's : `KubernetesClient::getNamespace`), if the client provides
such a method. This, in turn, could be configured via environment properties. For example fabric8 client can be configured via
"KUBERNETES_NAMESPACE" property; consult the client documentation for exact details.
Failure to find a namespace from the above steps will result in an Exception being raised.

View File

@@ -1,6 +0,0 @@
[[order_of_configMaps_and_secrets]]
= Order of ConfigMaps and Secrets
:page-section-summary-toc: 1
If, for whatever reason, you enabled both configmaps and secrets, and there is a common property between them, the value from the ConfigMap will have a higher precedence. That is: it will override whatever values are found in secrets.

View File

@@ -1,107 +0,0 @@
[[propertysource-reload]]
= `PropertySource` Reload
WARNING: This functionality has been deprecated in the 2020.0 release. Please see
the xref:spring-cloud-kubernetes-configuration-watcher.adoc#spring-cloud-kubernetes-configuration-watcher[null] controller for an alternative way
to achieve the same functionality.
Some applications may need to detect changes on external property sources and update their internal status to reflect the new configuration.
The reload feature of Spring Cloud Kubernetes is able to trigger an application reload when a related `ConfigMap` or
`Secret` changes.
By default, this feature is disabled. You can enable it by using the `spring.cloud.kubernetes.reload.enabled=true` configuration property (for example, in the `application.properties` file).
Please notice that this will enable monitoring of configmaps only (i.e.: `spring.cloud.kubernetes.reload.monitoring-config-maps` will be set to `true`).
If you want to enable monitoring of secrets, this must be done explicitly via : `spring.cloud.kubernetes.reload.monitoring-secrets=true`.
The following levels of reload are supported (by setting the `spring.cloud.kubernetes.reload.strategy` property):
* `refresh` (default): Only configuration beans annotated with `@ConfigurationProperties` or `@RefreshScope` are reloaded.
This reload level leverages the refresh feature of Spring Cloud Context.
* `restart_context`: the whole Spring `ApplicationContext` is gracefully restarted. Beans are recreated with the new configuration.
In order for the restart context functionality to work properly you must enable and expose the restart actuator endpoint
[source,yaml]
====
----
management:
endpoint:
restart:
enabled: true
endpoints:
web:
exposure:
include: restart
----
====
* `shutdown`: the Spring `ApplicationContext` is shut down to activate a restart of the container.
When you use this level, make sure that the lifecycle of all non-daemon threads is bound to the `ApplicationContext`
and that a replication controller or replica set is configured to restart the pod.
Assuming that the reload feature is enabled with default settings (`refresh` mode), the following bean is refreshed when the config map changes:
====
[java, source]
----
@Configuration
@ConfigurationProperties(prefix = "bean")
public class MyConfig {
private String message = "a message that can be changed live";
// getter and setters
}
----
====
To see that changes effectively happen, you can create another bean that prints the message periodically, as follows
====
[source,java]
----
@Component
public class MyBean {
@Autowired
private MyConfig config;
@Scheduled(fixedDelay = 5000)
public void hello() {
System.out.println("The message is: " + config.getMessage());
}
}
----
====
You can change the message printed by the application by using a `ConfigMap`, as follows:
====
[source,yaml]
----
apiVersion: v1
kind: ConfigMap
metadata:
name: reload-example
data:
application.properties: |-
bean.message=Hello World!
----
====
Any change to the property named `bean.message` in the `ConfigMap` associated with the pod is reflected in the
output. More generally speaking, changes associated to properties prefixed with the value defined by the `prefix`
field of the `@ConfigurationProperties` annotation are detected and reflected in the application.
xref:property-source-config/configmap-propertysource.adoc[Associating a `ConfigMap` with a pod] is explained earlier in this chapter.
The reload feature supports two operating modes:
* Event (default): Watches for changes in config maps or secrets by using the Kubernetes API (web socket).
Any event produces a re-check on the configuration and, in case of changes, a reload.
The `view` role on the service account is required in order to listen for config map changes. A higher level role (such as `edit`) is required for secrets
(by default, secrets are not monitored).
* Polling: Periodically re-creates the configuration from config maps and secrets to see if it has changed.
You can configure the polling period by using the `spring.cloud.kubernetes.reload.period` property and defaults to 15 seconds.
It requires the same role as the monitored property source.
This means, for example, that using polling on file-mounted secret sources does not require particular privileges.

View File

@@ -1,195 +0,0 @@
[[secrets-propertysource]]
= Secrets PropertySource
Kubernetes has the notion of https://kubernetes.io/docs/concepts/configuration/secret/[Secrets] for storing
sensitive data such as passwords, OAuth tokens, and so on. This project provides integration with `Secrets` to make secrets
accessible by Spring Boot applications. You can explicitly enable or disable This feature by setting the `spring.cloud.kubernetes.secrets.enabled` property.
When enabled, the `Fabric8SecretsPropertySource` looks up Kubernetes for `Secrets` from the following sources:
. Reading recursively from secrets mounts
. Named after the application (as defined by `spring.application.name`)
. Matching some labels
*Note:*
By default, consuming Secrets through the API (points 2 and 3 above) *is not enabled* for security reasons. The permission 'list' on secrets allows clients to inspect secrets values in the specified namespace.
Further, we recommend that containers share secrets through mounted volumes.
If you enable consuming Secrets through the API, we recommend that you limit access to Secrets by using an authorization policy, such as RBAC.
For more information about risks and best practices when consuming Secrets through the API refer to https://kubernetes.io/docs/concepts/configuration/secret/#best-practices[this doc].
If the secrets are found, their data is made available to the application.
Assume that we have a spring boot application named `demo` that uses properties to read its database
configuration. We can create a Kubernetes secret by using the following command:
====
[source]
----
kubectl create secret generic db-secret --from-literal=username=user --from-literal=password=p455w0rd
----
====
The preceding command would create the following secret (which you can see by using `kubectl get secrets db-secret -o yaml`):
====
[source,yaml]
----
apiVersion: v1
data:
password: cDQ1NXcwcmQ=
username: dXNlcg==
kind: Secret
metadata:
creationTimestamp: 2017-07-04T09:15:57Z
name: db-secret
namespace: default
resourceVersion: "357496"
selfLink: /api/v1/namespaces/default/secrets/db-secret
uid: 63c89263-6099-11e7-b3da-76d6186905a8
type: Opaque
----
====
Note that the data contains Base64-encoded versions of the literal provided by the `create` command.
Your application can then use this secret -- for example, by exporting the secret's value as environment variables:
====
[source,yaml]
----
apiVersion: v1
kind: Deployment
metadata:
name: ${project.artifactId}
spec:
template:
spec:
containers:
- env:
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: db-secret
key: username
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: password
----
====
You can select the Secrets to consume in a number of ways:
. By listing the directories where secrets are mapped:
+
====
[source,bash]
----
-Dspring.cloud.kubernetes.secrets.paths=/etc/secrets/db-secret,etc/secrets/postgresql
----
====
+
If you have all the secrets mapped to a common root, you can set them like:
+
====
[source,bash]
----
-Dspring.cloud.kubernetes.secrets.paths=/etc/secrets
----
====
. By setting a named secret:
+
====
[source,bash]
----
-Dspring.cloud.kubernetes.secrets.name=db-secret
----
====
. By defining a list of labels:
+
====
[source,bash]
----
-Dspring.cloud.kubernetes.secrets.labels.broker=activemq
-Dspring.cloud.kubernetes.secrets.labels.db=postgresql
----
====
As the case with `ConfigMap`, more advanced configuration is also possible where you can use multiple `Secret`
instances. The `spring.cloud.kubernetes.secrets.sources` list makes this possible.
For example, you could define the following `Secret` instances:
====
[source,yaml]
----
spring:
application:
name: cloud-k8s-app
cloud:
kubernetes:
secrets:
name: default-name
namespace: default-namespace
sources:
# Spring Cloud Kubernetes looks up a Secret named s1 in namespace default-namespace
- name: s1
# Spring Cloud Kubernetes looks up a Secret named default-name in namespace n2
- namespace: n2
# Spring Cloud Kubernetes looks up a Secret named s3 in namespace n3
- namespace: n3
name: s3
----
====
In the preceding example, if `spring.cloud.kubernetes.secrets.namespace` had not been set,
the `Secret` named `s1` would be looked up in the namespace that the application runs.
See xref:property-source-config/namespace-resolution.adoc[namespace-resolution] to get a better understanding of how the namespace
of the application is resolved.
xref:property-source-config/configmap-propertysource.adoc#config-map-fail-fast[Similar to the `ConfigMaps`]; if you want your application to fail to start
when it is unable to load `Secrets` property sources, you can set `spring.cloud.kubernetes.secrets.fail-fast=true`.
It is also possible to enable retry for `Secret` property sources xref:property-source-config/configmap-propertysource.adoc#config-map-retry[like the `ConfigMaps`].
As with the `ConfigMap` property sources, first you need to set `spring.cloud.kubernetes.secrets.fail-fast=true`.
Then you need to add `spring-retry` and `spring-boot-starter-aop` to your classpath.
Retry behavior of the `Secret` property sources can be configured by setting the `spring.cloud.kubernetes.secrets.retry.*`
properties.
NOTE: If you already have `spring-retry` and `spring-boot-starter-aop` on the classpath for some reason
and want to enable fail-fast, but do not want retry to be enabled; you can disable retry for `Secrets` `PropertySources`
by setting `spring.cloud.kubernetes.secrets.retry.enabled=false`.
.Properties:
[options="header,footer"]
|===
| Name | Type | Default | Description
| `spring.cloud.kubernetes.secrets.enabled` | `Boolean` | `true` | Enable Secrets `PropertySource`
| `spring.cloud.kubernetes.secrets.name` | `String` | `${spring.application.name}` | Sets the name of the secret to look up
| `spring.cloud.kubernetes.secrets.namespace` | `String` | Client namespace | Sets the Kubernetes namespace where to look up
| `spring.cloud.kubernetes.secrets.labels` | `Map` | `null` | Sets the labels used to lookup secrets
| `spring.cloud.kubernetes.secrets.paths` | `List` | `null` | Sets the paths where secrets are mounted (example 1)
| `spring.cloud.kubernetes.secrets.enableApi` | `Boolean` | `false` | Enables or disables consuming secrets through APIs (examples 2 and 3)
| `spring.cloud.kubernetes.secrets.fail-fast` | `Boolean` | `false` | Enable or disable failing the application start-up when an error occurred while loading a `Secret`
| `spring.cloud.kubernetes.secrets.retry.enabled` | `Boolean` | `true` | Enable or disable secrets retry.
| `spring.cloud.kubernetes.secrets.retry.initial-interval` | `Long` | `1000` | Initial retry interval in milliseconds.
| `spring.cloud.kubernetes.secrets.retry.max-attempts` | `Integer` | `6` | Maximum number of attempts.
| `spring.cloud.kubernetes.secrets.retry.max-interval` | `Long` | `2000` | Maximum interval for backoff.
| `spring.cloud.kubernetes.secrets.retry.multiplier` | `Double` | `1.1` | Multiplier for next interval.
|===
Notes:
* The `spring.cloud.kubernetes.secrets.labels` property behaves as defined by
https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-Configuration-Binding#map-based-binding[Map-based binding].
* The `spring.cloud.kubernetes.secrets.paths` property behaves as defined by
https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-Configuration-Binding#collection-based-binding[Collection-based binding].
* Access to secrets through the API may be restricted for security reasons. The preferred way is to mount secrets to the Pod.
You can find an example of an application that uses secrets (though it has not been updated to use the new `spring-cloud-kubernetes` project) at
https://github.com/fabric8-quickstarts/spring-boot-camel-config[spring-boot-camel-config]

View File

@@ -1,12 +0,0 @@
Spring Cloud Kubernetes provide Spring Cloud common interface implementations that consume Kubernetes native services.
The main objective of the projects provided in this repository is to facilitate the integration of Spring Cloud and Spring Boot applications running inside Kubernetes.
## Features
* Kubernetes awareness
* `DiscoveryClient` implementation
* `PropertySource` objects configured via ConfigMaps
## Getting Started
The easiest way to get started is by including the Spring Cloud BOM and then adding `spring-cloud-starter-kubernetes-client-all` to your application's classpath. If you don't want to include all of the Spring Cloud Kubernetes features you can add individual starters for the features you would like. By default Spring Cloud Kubernetes will enable the `kubernetes` profile when it detects it is running inside a Kubernetes cluster. You can take advantage of this by creating a `kubernetes-application` configuration properties for anything specific to Kubernetes you might want to configure. Once the starter is on the classpath the application should behave as any other Spring Cloud application.

View File

@@ -1,81 +0,0 @@
[[security-configurations-inside-kubernetes]]
= Security Configurations Inside Kubernetes
[[namespace]]
== Namespace
Most of the components provided in this project need to know the namespace. For Kubernetes (1.3+), the namespace is made available to the pod as part of the service account secret and is automatically detected by the client.
For earlier versions, it needs to be specified as an environment variable to the pod. A quick way to do this is as follows:
====
[source]
----
env:
- name: "KUBERNETES_NAMESPACE"
valueFrom:
fieldRef:
fieldPath: "metadata.namespace"
----
====
[[service-account]]
== Service Account
For distributions of Kubernetes that support more fine-grained role-based access within the cluster, you need to make sure a pod that runs with `spring-cloud-kubernetes` has access to the Kubernetes API.
For any service accounts you assign to a deployment or pod, you need to make sure they have the correct roles.
Depending on the requirements, you'll need `get`, `list` and `watch` permission on the following resources:
.Kubernetes Resource Permissions
|===
|Dependency | Resources
|spring-cloud-starter-kubernetes-fabric8
|pods, services, endpoints
|spring-cloud-starter-kubernetes-fabric8-config
|configmaps, secrets
|spring-cloud-starter-kubernetes-client
|pods, services, endpoints
|spring-cloud-starter-kubernetes-client-config
|configmaps, secrets
|===
For development purposes, you can add `cluster-reader` permissions to your `default` service account. On a production system you'll likely want to provide more granular permissions.
The following Role and RoleBinding are an example for namespaced permissions for the `default` account:
====
[source,yaml]
----
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: YOUR-NAME-SPACE
name: namespace-reader
rules:
- apiGroups: [""]
resources: ["configmaps", "pods", "services", "endpoints", "secrets"]
verbs: ["get", "list", "watch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: namespace-reader-binding
namespace: YOUR-NAME-SPACE
subjects:
- kind: ServiceAccount
name: default
apiGroup: ""
roleRef:
kind: Role
name: namespace-reader
apiGroup: ""
----
====

View File

@@ -1,7 +0,0 @@
[[service-registry-implementation]]
= Service Registry Implementation
:page-section-summary-toc: 1
In Kubernetes service registration is controlled by the platform, the application itself does not control
registration as it may do in other platforms. For this reason using `spring.cloud.service-registry.auto-registration.enabled`
or setting `@EnableDiscoveryClient(autoRegister=false)` will have no effect in Spring Cloud Kubernetes.

View File

@@ -1,122 +0,0 @@
[#spring-cloud-kubernetes-configserver]
## Spring Cloud Kubernetes Config Server
The Spring Cloud Kubernetes Config Server, is based on https://spring.io/projects/spring-cloud-config[Spring Cloud Config Server] and adds an https://docs.spring.io/spring-cloud-config/docs/current/reference/html/#_environment_repository[environment repository] for Kubernetes
https://kubernetes.io/docs/concepts/configuration/configmap/[Config Maps] and https://kubernetes.io/docs/concepts/configuration/secret/[Secrets].
This is component is completely optional. However, it allows you to continue to leverage configuration
you may have stored in existing environment repositories (Git, SVN, Vault, etc) with applications that you are running on Kubernetes.
A default image is located on https://hub.docker.com/r/springcloud/spring-cloud-kubernetes-configserver[Docker Hub] which will allow you to easily get a Config Server deployed on Kubernetes without building
the code and image yourself. However, if you need to customize the config server behavior or prefer to build the image yourself you can easily build your own
image from the https://github.com/spring-cloud/spring-cloud-kubernetes/tree/main/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configserver[source code on GitHub] and use that.
### Configuration
#### Enabling The Kubernetes Environment Repository
To enable the Kubernetes environment repository the `kubernetes` profile must be included in the list of active profiles.
You may activate other profiles as well to use other environment repository implementations.
#### Config Map and Secret PropertySources
By default, only Config Map data will be fetched. To enable Secrets as well you will need to set `spring.cloud.kubernetes.secrets.enableApi=true`.
You can disable the Config Map `PropertySource` by setting `spring.cloud.kubernetes.config.enableApi=false`.
#### Fetching Config Map and Secret Data From Additional Namespaces
By default, the Kubernetes environment repository will only fetch Config Map and Secrets from the namespace in which it is deployed.
If you want to include data from other namespaces you can set `spring.cloud.kubernetes.configserver.config-map-namespaces` and/or `spring.cloud.kubernetes.configserver.secrets-namespaces` to a comma separated
list of namespace values.
NOTE: If you set `spring.cloud.kubernetes.configserver.config-map-namespaces` and/or `spring.cloud.kubernetes.configserver.secrets-namespaces`
you will need to include the namespace in which the Config Server is deployed in order to continue to fetch Config Map and Secret data from that namespace.
#### Kubernetes Access Controls
The Kubernetes Config Server uses the Kubernetes API server to fetch Config Map and Secret data. In order for it to do that
it needs ability to `get` and `list` Config Map and Secrets (depending on what you enable/disable).
### Deployment Yaml
Below is a sample deployment, service and permissions configuration you can use to deploy a basic Config Server to Kubernetes.
====
[source,yaml]
----
---
apiVersion: v1
kind: List
items:
- apiVersion: v1
kind: Service
metadata:
labels:
app: spring-cloud-kubernetes-configserver
name: spring-cloud-kubernetes-configserver
spec:
ports:
- name: http
port: 8888
targetPort: 8888
selector:
app: spring-cloud-kubernetes-configserver
type: ClusterIP
- apiVersion: v1
kind: ServiceAccount
metadata:
labels:
app: spring-cloud-kubernetes-configserver
name: spring-cloud-kubernetes-configserver
- apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
labels:
app: spring-cloud-kubernetes-configserver
name: spring-cloud-kubernetes-configserver:view
roleRef:
kind: Role
apiGroup: rbac.authorization.k8s.io
name: namespace-reader
subjects:
- kind: ServiceAccount
name: spring-cloud-kubernetes-configserver
- apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: namespace-reader
rules:
- apiGroups: ["", "extensions", "apps"]
resources: ["configmaps", "secrets"]
verbs: ["get", "list"]
- apiVersion: apps/v1
kind: Deployment
metadata:
name: spring-cloud-kubernetes-configserver-deployment
spec:
selector:
matchLabels:
app: spring-cloud-kubernetes-configserver
template:
metadata:
labels:
app: spring-cloud-kubernetes-configserver
spec:
serviceAccount: spring-cloud-kubernetes-configserver
containers:
- name: spring-cloud-kubernetes-configserver
image: springcloud/spring-cloud-kubernetes-configserver
imagePullPolicy: IfNotPresent
env:
- name: SPRING_PROFILES_INCLUDE
value: "kubernetes"
readinessProbe:
httpGet:
port: 8888
path: /actuator/health/readiness
livenessProbe:
httpGet:
port: 8888
path: /actuator/health/liveness
ports:
- containerPort: 8888
----
====

View File

@@ -1,216 +0,0 @@
[#spring-cloud-kubernetes-configuration-watcher]
## Spring Cloud Kubernetes Configuration Watcher
Kubernetes provides the ability to https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/#add-configmap-data-to-a-volume[mount a ConfigMap or Secret as a volume]
in the container of your application. When the contents of the ConfigMap or Secret changes, the https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/#mounted-configmaps-are-updated-automatically[mounted volume will be updated with those changes].
However, Spring Boot will not automatically update those changes unless you restart the application. Spring Cloud
provides the ability refresh the application context without restarting the application by either hitting the
actuator endpoint `/refresh` or via publishing a `RefreshRemoteApplicationEvent` using Spring Cloud Bus.
To achieve this configuration refresh of a Spring Cloud app running on Kubernetes, you can deploy the Spring Cloud
Kubernetes Configuration Watcher controller into your Kubernetes cluster.
The application is published as a container and is available on https://hub.docker.com/r/springcloud/spring-cloud-kubernetes-configuration-watcher[Docker Hub].
However, if you need to customize the config watcher behavior or prefer to build the image yourself you can easily build your own
image from the https://github.com/spring-cloud/spring-cloud-kubernetes/tree/main/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher[source code on GitHub] and use that.
Spring Cloud Kubernetes Configuration Watcher can send refresh notifications to applications in two ways.
1. Over HTTP in which case the application being notified must of the `/refresh` actuator endpoint exposed and accessible from within the cluster
2. Using Spring Cloud Bus, in which case you will need a message broker deployed to your custer for the application to use.
### Deployment YAML
Below is a sample deployment YAML you can use to deploy the Kubernetes Configuration Watcher to Kubernetes.
====
[source,yaml]
----
---
apiVersion: v1
kind: List
items:
- apiVersion: v1
kind: Service
metadata:
labels:
app: spring-cloud-kubernetes-configuration-watcher
name: spring-cloud-kubernetes-configuration-watcher
spec:
ports:
- name: http
port: 8888
targetPort: 8888
selector:
app: spring-cloud-kubernetes-configuration-watcher
type: ClusterIP
- apiVersion: v1
kind: ServiceAccount
metadata:
labels:
app: spring-cloud-kubernetes-configuration-watcher
name: spring-cloud-kubernetes-configuration-watcher
- apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
labels:
app: spring-cloud-kubernetes-configuration-watcher
name: spring-cloud-kubernetes-configuration-watcher:view
roleRef:
kind: Role
apiGroup: rbac.authorization.k8s.io
name: namespace-reader
subjects:
- kind: ServiceAccount
name: spring-cloud-kubernetes-configuration-watcher
- apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: namespace-reader
rules:
- apiGroups: ["", "extensions", "apps"]
resources: ["configmaps", "pods", "services", "endpoints", "secrets"]
verbs: ["get", "list", "watch"]
- apiVersion: apps/v1
kind: Deployment
metadata:
name: spring-cloud-kubernetes-configuration-watcher-deployment
spec:
selector:
matchLabels:
app: spring-cloud-kubernetes-configuration-watcher
template:
metadata:
labels:
app: spring-cloud-kubernetes-configuration-watcher
spec:
serviceAccount: spring-cloud-kubernetes-configuration-watcher
containers:
- name: spring-cloud-kubernetes-configuration-watcher
image: springcloud/spring-cloud-kubernetes-configuration-watcher:2.0.1-SNAPSHOT
imagePullPolicy: IfNotPresent
readinessProbe:
httpGet:
port: 8888
path: /actuator/health/readiness
livenessProbe:
httpGet:
port: 8888
path: /actuator/health/liveness
ports:
- containerPort: 8888
----
====
The Service Account and associated Role Binding is important for Spring Cloud Kubernetes Configuration to work properly.
The controller needs access to read data about ConfigMaps, Pods, Services, Endpoints and Secrets in the Kubernetes cluster.
### Monitoring ConfigMaps and Secrets
Spring Cloud Kubernetes Configuration Watcher will react to changes in ConfigMaps with a label of `spring.cloud.kubernetes.config` with the value `true`
or any Secret with a label of `spring.cloud.kubernetes.secret` with the value `true`. If the ConfigMap or Secret does not have either of those labels
or the values of those labels is not `true` then any changes will be ignored.
If a change is made to a ConfigMap or Secret with valid labels then Spring Cloud Kubernetes Configuration Watcher will take the name of the ConfigMap or Secret
and send a notification to the application with that name. This might not be enough for your use-case though, you could for example what to:
- bind a config-map to multiple applications, so that a change inside a single configmap triggers a refresh for many services
- have profile based sources trigger events for your application
For that reasons there is an addition annotation you could specify:
`spring.cloud.kubernetes.configmap.apps` or `spring.cloud.kubernetes.secret.apps`. It takes a String of apps separated by comma,
that specifies the names of applications that will receive a notification when changes happen in this secret/configmap.
For example:
====
[source,yaml]
----
kind: ConfigMap
apiVersion: v1
metadata:
name: example-configmap
labels:
spring.cloud.kubernetes.config: "true"
annotations:
spring.cloud.kubernetes.configmap.apps: "app-a, app-b"
----
====
### HTTP Implementation
The HTTP implementation is what is used by default. When this implementation is used Spring Cloud Kubernetes Configuration Watcher and a
change to a ConfigMap or Secret occurs then the HTTP implementation will use the Spring Cloud Kubernetes Discovery Client to fetch all
instances of the application which match the name of the ConfigMap or Secret and send an HTTP POST request to the application's actuator
`/refresh` endpoint. By default it will send the post request to `/actuator/refresh` using the port registered in the discovery client.
#### Non-Default Management Port and Actuator Path
If the application is using a non-default actuator path and/or using a different port for the management endpoints, the Kubernetes service for the application
can add an annotation called `boot.spring.io/actuator` and set its value to the path and port used by the application. For example
====
[source,yaml]
----
apiVersion: v1
kind: Service
metadata:
labels:
app: config-map-demo
name: config-map-demo
annotations:
boot.spring.io/actuator: http://:9090/myactuator/home
spec:
ports:
- name: http
port: 8080
targetPort: 8080
selector:
app: config-map-demo
----
====
Another way you can choose to configure the actuator path and/or management port is by setting
`spring.cloud.kubernetes.configuration.watcher.actuatorPath` and `spring.cloud.kubernetes.configuration.watcher.actuatorPort`.
### Messaging Implementation
The messaging implementation can be enabled by setting profile to either `bus-amqp` (RabbitMQ) or `bus-kafka` (Kafka) when the Spring Cloud Kubernetes Configuration Watcher
application is deployed to Kubernetes.
### Configuring RabbitMQ
When the `bus-amqp` profile is enabled you will need to configure Spring RabbitMQ to point it to the location of the RabbitMQ
instance you would like to use as well as any credentials necessary to authenticate. This can be done
by setting the standard Spring RabbitMQ properties, for example
====
[source,yaml]
----
spring:
rabbitmq:
username: user
password: password
host: rabbitmq
----
====
### Configuring Kafka
When the `bus-kafka` profile is enabled you will need to configure Spring Kafka to point it to the location of the Kafka Broker
instance you would like to use. This can be done by setting the standard Spring Kafka properties, for example
====
[source,yaml]
----
spring:
kafka:
producer:
bootstrap-servers: localhost:9092
----
====

View File

@@ -1,214 +0,0 @@
[#spring-cloud-kubernetes-discoveryserver]
## Spring Cloud Kubernetes Discovery Server
The Spring Cloud Kubernetes Discovery Server provides HTTP endpoints apps can use to gather information
about services available within a Kubernetes cluster. The Spring Cloud Kubernetes Discovery Server
can be used by apps using the `spring-cloud-starter-kubernetes-discoveryclient` to provide data to
the `DiscoveryClient` implementation provided by that starter.
### Permissions
The Spring Cloud Discovery server uses
the Kubernetes API server to get data about Service and Endpoint resrouces so it needs list, watch, and
get permissions to use those endpoints. See the below sample Kubernetes deployment YAML for an
examlpe of how to configure the Service Account on Kubernetes.
### Endpoints
There are three endpoints exposed by the server.
#### `/apps`
A `GET` request sent to `/apps` will return a JSON array of available services. Each item contains
the name of the Kubernetes service and service instance information. Below is a sample response.
====
[source,json]
----
[
{
"name":"spring-cloud-kubernetes-discoveryserver",
"serviceInstances":[
{
"instanceId":"836a2f25-daee-4af2-a1be-aab9ce2b938f",
"serviceId":"spring-cloud-kubernetes-discoveryserver",
"host":"10.244.1.6",
"port":8761,
"uri":"http://10.244.1.6:8761",
"secure":false,
"metadata":{
"app":"spring-cloud-kubernetes-discoveryserver",
"kubectl.kubernetes.io/last-applied-configuration":"{\"apiVersion\":\"v1\",\"kind\":\"Service\",\"metadata\":{\"annotations\":{},\"labels\":{\"app\":\"spring-cloud-kubernetes-discoveryserver\"},\"name\":\"spring-cloud-kubernetes-discoveryserver\",\"namespace\":\"default\"},\"spec\":{\"ports\":[{\"name\":\"http\",\"port\":80,\"targetPort\":8761}],\"selector\":{\"app\":\"spring-cloud-kubernetes-discoveryserver\"},\"type\":\"ClusterIP\"}}\n",
"http":"8761"
},
"namespace":"default",
"scheme":"http"
}
]
},
{
"name":"kubernetes",
"serviceInstances":[
{
"instanceId":"1234",
"serviceId":"kubernetes",
"host":"172.18.0.3",
"port":6443,
"uri":"http://172.18.0.3:6443",
"secure":false,
"metadata":{
"provider":"kubernetes",
"component":"apiserver",
"https":"6443"
},
"namespace":"default",
"scheme":"http"
}
]
}
]
----
====
#### `/apps/{name}`
A `GET` request to `/apps/{name}` can be used to get instance data for all instances of a given
service. Below is a sample response when a `GET` request is made to `/apps/kubernetes`.
====
[source,json]
----
[
{
"instanceId":"1234",
"serviceId":"kubernetes",
"host":"172.18.0.3",
"port":6443,
"uri":"http://172.18.0.3:6443",
"secure":false,
"metadata":{
"provider":"kubernetes",
"component":"apiserver",
"https":"6443"
},
"namespace":"default",
"scheme":"http"
}
]
----
====
#### `/app/{name}/{instanceid}`
A `GET` request made to `/app/{name}/{instanceid}` will return the instance data for a specific
instance of a given service. Below is a sample response when a `GET` request is made to `/app/kubernetes/1234`.
====
[source,json]
----
{
"instanceId":"1234",
"serviceId":"kubernetes",
"host":"172.18.0.3",
"port":6443,
"uri":"http://172.18.0.3:6443",
"secure":false,
"metadata":{
"provider":"kubernetes",
"component":"apiserver",
"https":"6443"
},
"namespace":"default",
"scheme":"http"
}
----
====
### Deployment YAML
An image of the Spring Cloud Discovery Server is hosted on https://hub.docker.com/r/springcloud/spring-cloud-kubernetes-discoveryserver[Docker Hub].
However, if you need to customize the discovery server behavior or prefer to build the image yourself you can easily build your own
image from the https://github.com/spring-cloud/spring-cloud-kubernetes/tree/main/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-discoveryserver[source code on GitHub] and use that.
Below is a sample deployment YAML you can use to deploy the Kubernetes Configuration Watcher to Kubernetes.
====
[source,yaml]
----
---
apiVersion: v1
kind: List
items:
- apiVersion: v1
kind: Service
metadata:
labels:
app: spring-cloud-kubernetes-discoveryserver
name: spring-cloud-kubernetes-discoveryserver
spec:
ports:
- name: http
port: 80
targetPort: 8761
selector:
app: spring-cloud-kubernetes-discoveryserver
type: ClusterIP
- apiVersion: v1
kind: ServiceAccount
metadata:
labels:
app: spring-cloud-kubernetes-discoveryserver
name: spring-cloud-kubernetes-discoveryserver
- apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
labels:
app: spring-cloud-kubernetes-discoveryserver
name: spring-cloud-kubernetes-discoveryserver:view
roleRef:
kind: Role
apiGroup: rbac.authorization.k8s.io
name: namespace-reader
subjects:
- kind: ServiceAccount
name: spring-cloud-kubernetes-discoveryserver
- apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: namespace-reader
rules:
- apiGroups: ["", "extensions", "apps"]
resources: ["services", "endpoints"]
verbs: ["get", "list", "watch"]
- apiVersion: apps/v1
kind: Deployment
metadata:
name: spring-cloud-kubernetes-discoveryserver-deployment
spec:
selector:
matchLabels:
app: spring-cloud-kubernetes-discoveryserver
template:
metadata:
labels:
app: spring-cloud-kubernetes-discoveryserver
spec:
serviceAccount: spring-cloud-kubernetes-discoveryserver
containers:
- name: spring-cloud-kubernetes-discoveryserver
image: springcloud/spring-cloud-kubernetes-discoveryserver:3.0.0-SNAPSHOT
imagePullPolicy: IfNotPresent
readinessProbe:
httpGet:
port: 8761
path: /actuator/health/readiness
livenessProbe:
httpGet:
port: 8761
path: /actuator/health/liveness
ports:
- containerPort: 8761
----
====

View File

@@ -1,59 +0,0 @@
[[spring-cloud-kubernetes]]
= Spring Cloud Kubernetes
This reference guide covers how to use Spring Cloud Kubernetes.
[[why-do-you-need-spring-cloud-kubernetes?]]
== Why do you need Spring Cloud Kubernetes?
Spring Cloud Kubernetes provides implementations of well known Spring Cloud interfaces allowing developers to build and run Spring Cloud applications on Kubernetes. While this project may be useful to you when building a cloud native application, it is also not a requirement in order to deploy a Spring Boot app on Kubernetes. If you are just getting started in your journey to running your Spring Boot app on Kubernetes you can accomplish a lot with nothing more than a basic Spring Boot app and Kubernetes itself. To learn more, you can get started by reading the https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#cloud-deployment-kubernetes[Spring Boot reference documentation for deploying to Kubernetes ] and also working through the workshop material https://hackmd.io/@ryanjbaxter/spring-on-k8s-workshop[Spring and Kubernetes].
[[configuration-properties]]
== Configuration properties
To see the list of all Kubernetes related configuration properties please check link:appendix.html[the Appendix page].
[[building]]
== Building
include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/main/docs/modules/ROOT/partials/building.adoc[]
[[building-docker-images-on-arm64]]
=== Building Docker Images On ARM64
If you run the Spring Cloud Kuberentes build on an ARM64 machine the docker images
used for the integration tests will fail to run due to using the wrong architecture.
This is because the Paketo build pack does not yet support ARM64. To work around this you
can run the build by passing `-Dspring-boot.build-image.builder=dashaun/builder:tiny` to Maven.
For example:
```
./mvnw clean install -Dspring-boot.build-image.builder=dashaun/builder:tiny
```
[[contributing]]
== Contributing
include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/main/docs/modules/ROOT/partials/contributing.adoc[]
[[aot-and-native-image-support]]
== AOT and native image support
At this point, Spring Cloud Kubernetes does not support Spring Boot AOT transformations or native images. Partial support might be added in future releases.

View File

@@ -1,78 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xmlns="https://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-docs</artifactId>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes</artifactId>
<version>3.1.0-SNAPSHOT</version>
</parent>
<packaging>jar</packaging>
<name>Spring Cloud Kubernetes Docs</name>
<description>Spring Cloud Kubernetes Docs</description>
<properties>
<docs.main>spring-cloud-kubernetes</docs.main>
<main.basedir>${basedir}/..</main.basedir>
<configprops.inclusionPattern>spring.cloud.kubernetes.*</configprops.inclusionPattern>
<upload-docs-zip.phase>deploy</upload-docs-zip.phase>
<!-- Don't upload docs jar to central / repo.spring.io -->
<maven-deploy-plugin-default.phase>none</maven-deploy-plugin-default.phase>
</properties>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>spring-cloud-starter-kubernetes-fabric8</artifactId>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>spring-cloud-starter-kubernetes-fabric8-all</artifactId>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>spring-cloud-starter-kubernetes-fabric8-config</artifactId>
</dependency>
</dependencies>
<build>
<sourceDirectory>src/main/asciidoc</sourceDirectory>
</build>
<profiles>
<profile>
<id>docs</id>
<build>
<plugins>
<plugin>
<groupId>pl.project13.maven</groupId>
<artifactId>git-commit-id-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<inherited>false</inherited>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@@ -1,13 +0,0 @@
Copyright (C) ${project.inceptionYear} ${owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

36
mvnw vendored
View File

@@ -8,7 +8,7 @@
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
# 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
@@ -19,7 +19,7 @@
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# Maven2 Start Up Batch script
# Maven Start Up Batch script
#
# Required ENV vars:
# ------------------
@@ -114,7 +114,6 @@ if $mingw ; then
M2_HOME="`(cd "$M2_HOME"; pwd)`"
[ -n "$JAVA_HOME" ] &&
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
# TODO classpath?
fi
if [ -z "$JAVA_HOME" ]; then
@@ -212,7 +211,11 @@ 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"
if [ -n "$MVNW_REPOURL" ]; then
jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
else
jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
fi
while IFS="=" read key value; do
case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
esac
@@ -221,22 +224,38 @@ else
echo "Downloading from: $jarUrl"
fi
wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
if $cygwin; then
wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
fi
if command -v wget > /dev/null; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found wget ... using wget"
fi
wget "$jarUrl" -O "$wrapperJarPath"
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
wget "$jarUrl" -O "$wrapperJarPath"
else
wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
fi
elif command -v curl > /dev/null; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found curl ... using curl"
fi
curl -o "$wrapperJarPath" "$jarUrl"
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
curl -o "$wrapperJarPath" "$jarUrl" -f
else
curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
fi
else
if [ "$MVNW_VERBOSE" = true ]; then
echo "Falling back to using Java to download"
fi
javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
# For Cygwin, switch paths to Windows format before running javac
if $cygwin; then
javaClass=`cygpath --path --windows "$javaClass"`
fi
if [ -e "$javaClass" ]; then
if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
if [ "$MVNW_VERBOSE" = true ]; then
@@ -277,6 +296,11 @@ if $cygwin; then
MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
fi
# 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
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
exec "$JAVACMD" \

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

@@ -1,161 +1,182 @@
@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 https://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%
@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 Maven 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 keystroke 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 by 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.5.6/maven-wrapper-0.5.6.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% (
if "%MVNW_VERBOSE%" == "true" (
echo Found %WRAPPER_JAR%
)
) else (
if not "%MVNW_REPOURL%" == "" (
SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
)
if "%MVNW_VERBOSE%" == "true" (
echo Couldn't find %WRAPPER_JAR%, downloading it ...
echo Downloading from: %DOWNLOAD_URL%
)
powershell -Command "&{"^
"$webclient = new-object System.Net.WebClient;"^
"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
"}"^
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
"}"
if "%MVNW_VERBOSE%" == "true" (
echo Finished downloading %WRAPPER_JAR%
)
)
@REM End of extension
@REM Provide a "standardized" way to retrieve the CLI args that will
@REM work with both Windows and non-Windows executions.
set MAVEN_CMD_LINE_ARGS=%*
%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%

386
pom.xml
View File

@@ -1,366 +1,70 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2013-2020 the original author or authors.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
~
-->
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-build</artifactId>
<version>4.1.0-SNAPSHOT</version>
<relativePath/>
</parent>
<artifactId>spring-cloud-kubernetes</artifactId>
<version>3.1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Spring Cloud Kubernetes</name>
<url>https://cloud.spring.io</url>
<inceptionYear>2017</inceptionYear>
<organization>
<name>Pivotal Software, Inc.</name>
<url>https://www.spring.io</url>
</organization>
<licenses>
<license>
<name>Apache License, Version 2.0</name>
<url>https://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-docs-build</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Spring Cloud Kubernetes Docs Build</name>
<description>Builds Spring Cloud Kubernetes Docs.</description>
<url>https://spring.io/projects/spring-cloud-kubernetes</url>
<scm>
<url>https://github.com/spring-cloud/spring-cloud-kubernetes</url>
<connection>
scm:git:git://github.com/spring-cloud/spring-cloud-kubernetes.git
<connection>scm:git:https://github.com/spring-cloud/spring-cloud-kubernetes.git
</connection>
<developerConnection>
scm:git:ssh://git@github.com/spring-cloud/spring-cloud-kubernetes.git
scm:git:git@github.com:spring-cloud/spring-cloud-kubernetes.git
</developerConnection>
<tag>HEAD</tag>
<url>https://github.com/spring-cloud/spring-cloud-kubernetes</url>
</scm>
<issueManagement>
<url>https://github.com/spring-cloud/spring-cloud-kubernetes/issues</url>
</issueManagement>
<properties>
<!-- Dependency Versions -->
<mockito-inline.version>4.8.1</mockito-inline.version>
<spring-cloud-commons.version>4.1.0-SNAPSHOT</spring-cloud-commons.version>
<spring-cloud-config.version>4.1.0-SNAPSHOT</spring-cloud-config.version>
<spring-cloud-bus.version>4.1.0-SNAPSHOT</spring-cloud-bus.version>
<spring-cloud-contract.version>4.1.0-SNAPSHOT</spring-cloud-contract.version>
<maven-surefire-plugin.version>3.1.0</maven-surefire-plugin.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>
<io.spring.maven.antora-version>0.0.3</io.spring.maven.antora-version>
</properties>
<modules>
<module>spring-cloud-kubernetes-dependencies</module>
<module>spring-cloud-kubernetes-commons</module>
<module>spring-cloud-kubernetes-test-support</module>
<module>spring-cloud-kubernetes-client-autoconfig</module>
<module>spring-cloud-kubernetes-client-config</module>
<module>spring-cloud-kubernetes-client-loadbalancer</module>
<module>spring-cloud-kubernetes-fabric8-autoconfig</module>
<module>spring-cloud-kubernetes-fabric8-config</module>
<module>spring-cloud-kubernetes-fabric8-discovery</module>
<module>spring-cloud-kubernetes-client-discovery</module>
<module>spring-cloud-starter-kubernetes-fabric8</module>
<module>spring-cloud-starter-kubernetes-fabric8-config</module>
<module>spring-cloud-starter-kubernetes-fabric8-all</module>
<module>spring-cloud-starter-kubernetes-client</module>
<module>spring-cloud-starter-kubernetes-client-config</module>
<module>spring-cloud-starter-kubernetes-client-loadbalancer</module>
<module>spring-cloud-starter-kubernetes-client-all</module>
<module>spring-cloud-kubernetes-examples</module>
<module>spring-cloud-kubernetes-fabric8-leader</module>
<module>spring-cloud-kubernetes-fabric8-istio</module>
<module>spring-cloud-kubernetes-controllers</module>
<module>spring-cloud-kubernetes-integration-tests</module>
<module>docs</module>
<module>spring-cloud-kubernetes-fabric8-loadbalancer</module>
<module>spring-cloud-starter-kubernetes-fabric8-loadbalancer</module>
<module>spring-cloud-kubernetes-discovery</module>
<module>spring-cloud-starter-kubernetes-discoveryclient</module>
</modules>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-dependencies</artifactId>
<version>${project.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons-dependencies</artifactId>
<version>${spring-cloud-commons.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-dependencies</artifactId>
<version>${spring-cloud-config.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-bus-dependencies</artifactId>
<version>${spring-cloud-bus.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-dependencies</artifactId>
<version>${spring-cloud-contract.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--Spring Boot 3.1 uses Mockito 5.x. Mockito 5.x has removed support for mockito-inline.-->
<!--https://github.com/mockito/mockito/issues/2877-->
<!--We need to support both Boot 3.0.x and Boot 3.1.x with Spring Cloud K8s 3.0.x-->
<!--To maintain compatibility we manage the version ourselves-->
<!-- TODO Remove this and all references to mockito-inline in the next minor -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-bom</artifactId>
<version>${mockito-inline.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>${groovy.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-test-support</artifactId>
<version>${spring-cloud-commons.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>flatten-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<inherited>true</inherited>
<groupId>io.spring.maven.antora</groupId>
<artifactId>antora-maven-plugin</artifactId>
<version>${io.spring.maven.antora-version}</version>
<extensions>true</extensions>
<configuration>
<source>17</source>
<target>17</target>
<options>
<option>--to-dir=target/antora/site</option>
<option>--stacktrace</option>
<option>--fetch</option>
</options>
<environment>
<ALGOLIA_API_KEY>9d489079e5ec46dbb238909fee5c9c29</ALGOLIA_API_KEY>
<ALGOLIA_APP_ID>WB1FQYI187</ALGOLIA_APP_ID>
<ALGOLIA_INDEX_NAME>springcloudkubernetes</ALGOLIA_INDEX_NAME>
</environment>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<inherited>true</inherited>
<configuration>
<parallel>all</parallel>
<reuseForks>false</reuseForks>
<includes>
<include>${testsToRun}</include>
</includes>
</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>
<repositories>
<repository>
<id>spring-snapshot</id>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
<releases>
<enabled>false</enabled>
</releases>
</repository>
<repository>
<id>spring-milestone</id>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
<profiles>
<profile>
<id>spring</id>
<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
<releases>
<enabled>false</enabled>
</releases>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-releases</id>
<name>Spring Releases</name>
<url>https://repo.spring.io/release</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
<releases>
<enabled>false</enabled>
</releases>
</pluginRepository>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>spring-releases</id>
<name>Spring Releases</name>
<url>https://repo.spring.io/release</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</profile>
<profile>
<id>release</id>
<build>
<plugins>
<!-- TODO: move t s-c-build -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>1.3.1</version>
<executions>
<execution>
<id>enforce-no-snapshots</id>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<requireReleaseDeps>
<message>No Snapshots Allowed!</message>
</requireReleaseDeps>
</rules>
<fail>false</fail>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>sonar</id>
<build>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<executions>
<execution>
<id>pre-unit-test</id>
<goals>
<goal>prepare-agent</goal>
</goals>
<configuration>
<propertyName>surefireArgLine</propertyName>
<destFile>${project.build.directory}/jacoco.exec
</destFile>
</configuration>
</execution>
<execution>
<id>post-unit-test</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
<configuration>
<!-- Sets the path to the file which contains the execution data. -->
<dataFile>${project.build.directory}/jacoco.exec
</dataFile>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<parallel>all</parallel>
<reuseForks>false</reuseForks>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@@ -1 +0,0 @@
./mvnw -s .settings.xml clean install -Dservice.occurence=${_SERVICE_OCCURENCE} #org.jacoco:jacoco-maven-plugin:prepare-agent install -U -P sonar

View File

@@ -1,7 +0,0 @@
#!/usr/bin/env bash
set -e
./mvnw deploy -DskipTests -B -Pfast,deploy ${@}
./mvnw dockerfile:push -pl :spring-cloud-kubernetes-configuration-watcher -Pdockerpush ${@}
./mvnw dockerfile:push -pl :spring-cloud-kubernetes-discoveryserver -Pdockerpush ${@}
./mvnw dockerfile:push -pl :spring-cloud-kubernetes-configserver -Pdockerpush ${@}

View File

@@ -1,5 +0,0 @@
#!/usr/bin/env bash
set -e
./mvnw clean install -B -Pdocs ${@}

View File

@@ -0,0 +1,118 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes</artifactId>
<version>3.0.4-SNAPSHOT</version>
</parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-client-autoconfig</artifactId>
<version>3.0.4-SNAPSHOT</version>
<description>Spring Cloud parent pom, managing plugins and dependencies for Spring
Cloud projects</description>
<url>https://cloud.spring.io/spring-cloud-kubernetes-client-autoconfig</url>
<inceptionYear>2017</inceptionYear>
<organization>
<name>Pivotal Software, Inc.</name>
<url>https://www.spring.io</url>
</organization>
<licenses>
<license>
<name>Apache License, Version 2.0</name>
<url>https://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
<developers>
<developer>
<id>dsyer</id>
<name>Dave Syer</name>
<email>dsyer at pivotal.io</email>
<organization>Pivotal Software, Inc.</organization>
<organizationUrl>https://www.spring.io</organizationUrl>
<roles>
<role>lead</role>
</roles>
</developer>
<developer>
<id>sgibb</id>
<name>Spencer Gibb</name>
<email>sgibb at pivotal.io</email>
<organization>Pivotal Software, Inc.</organization>
<organizationUrl>https://www.spring.io</organizationUrl>
<roles>
<role>lead</role>
</roles>
</developer>
<developer>
<id>mgrzejszczak</id>
<name>Marcin Grzejszczak</name>
<email>mgrzejszczak at pivotal.io</email>
<organization>Pivotal Software, Inc.</organization>
<organizationUrl>https://www.spring.io</organizationUrl>
<roles>
<role>developer</role>
</roles>
</developer>
<developer>
<id>rbaxter</id>
<name>Ryan Baxter</name>
<email>rbaxter at pivotal.io</email>
<organization>Pivotal Software, Inc.</organization>
<organizationUrl>https://www.spring.io</organizationUrl>
<roles>
<role>developer</role>
</roles>
</developer>
<developer>
<id>omaciaszeksharma</id>
<name>Olga Maciaszek-Sharma</name>
<email>omaciaszeksharma at pivotal.io</email>
<organization>Pivotal Software, Inc.</organization>
<organizationUrl>https://www.spring.io</organizationUrl>
<roles>
<role>developer</role>
</roles>
</developer>
</developers>
<scm>
<connection>scm:git:git://github.com/spring-cloud/spring-cloud-kubernetes.git/spring-cloud-kubernetes-client-autoconfig</connection>
<developerConnection>scm:git:ssh://git@github.com/spring-cloud/spring-cloud-kubernetes.git/spring-cloud-kubernetes-client-autoconfig</developerConnection>
<url>https://github.com/spring-cloud/spring-cloud-kubernetes/spring-cloud-kubernetes-client-autoconfig</url>
</scm>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-commons</artifactId>
<version>3.0.4-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.kubernetes</groupId>
<artifactId>client-java</artifactId>
<version>17.0.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.kubernetes</groupId>
<artifactId>client-java-extended</artifactId>
<version>17.0.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator-autoconfigure</artifactId>
<version>3.0.7</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>3.0.7</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View File

@@ -1,64 +0,0 @@
<?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"
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>
<groupId>org.springframework.cloud</groupId>
<version>3.1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-kubernetes-client-autoconfig</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-commons</artifactId>
</dependency>
<dependency>
<groupId>io.kubernetes</groupId>
<artifactId>client-java</artifactId>
</dependency>
<dependency>
<groupId>io.kubernetes</groupId>
<artifactId>client-java-extended</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator-autoconfigure</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-test-support</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -1,51 +0,0 @@
/*
* Copyright 2013-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.client;
import io.kubernetes.client.openapi.models.V1Pod;
import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator;
import org.springframework.boot.actuate.autoconfigure.info.ConditionalOnEnabledInfoContributor;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnCloudPlatform;
import org.springframework.boot.cloud.CloudPlatform;
import org.springframework.cloud.kubernetes.commons.PodUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author wind57
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(HealthIndicator.class)
@ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES)
public class KubernetesClientActuatorConfiguration {
@Bean
@ConditionalOnEnabledHealthIndicator("kubernetes")
public KubernetesClientHealthIndicator kubernetesHealthIndicator(PodUtils<V1Pod> podUtils) {
return new KubernetesClientHealthIndicator(podUtils);
}
@Bean
@ConditionalOnEnabledInfoContributor("kubernetes")
public KubernetesClientInfoContributor kubernetesInfoContributor(PodUtils<V1Pod> podUtils) {
return new KubernetesClientInfoContributor(podUtils);
}
}

View File

@@ -1,79 +0,0 @@
/*
* Copyright 2013-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.client;
import io.kubernetes.client.openapi.ApiClient;
import io.kubernetes.client.openapi.apis.CoreV1Api;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnCloudPlatform;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.cloud.CloudPlatform;
import org.springframework.cloud.kubernetes.commons.KubernetesClientProperties;
import org.springframework.cloud.kubernetes.commons.KubernetesCommonsAutoConfiguration;
import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import static org.springframework.cloud.kubernetes.client.KubernetesClientUtils.kubernetesApiClient;
/**
* @author Ryan Baxter
*/
@Configuration
@ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES)
@AutoConfigureAfter(KubernetesCommonsAutoConfiguration.class)
public class KubernetesClientAutoConfiguration {
/**
* this bean will be based on
* {@link org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties}
* in the next major release.
*/
@Deprecated(forRemoval = true)
@Bean
@ConditionalOnMissingBean
public ApiClient apiClient(Environment environment) {
ApiClient apiClient = kubernetesApiClient();
// it's too early to inject KubernetesClientProperties here, all its properties
// are missing. For the time being work-around with reading from the environment.
apiClient.setUserAgent(environment.getProperty("spring.cloud.kubernetes.client.user-agent",
KubernetesClientProperties.DEFAULT_USER_AGENT));
return apiClient;
}
@Bean
@ConditionalOnMissingBean
public CoreV1Api coreApi(ApiClient apiClient) {
return new CoreV1Api(apiClient);
}
@Bean
@ConditionalOnMissingBean
public KubernetesNamespaceProvider kubernetesNamespaceProvider(Environment environment) {
return new KubernetesNamespaceProvider(environment);
}
@Bean
@ConditionalOnMissingBean
public KubernetesClientPodUtils kubernetesPodUtils(CoreV1Api client,
KubernetesNamespaceProvider kubernetesNamespaceProvider) {
return new KubernetesClientPodUtils(client, kubernetesNamespaceProvider.getNamespace());
}
}

View File

@@ -1,69 +0,0 @@
/*
* Copyright 2013-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.client;
import java.util.Collections;
import java.util.Map;
import io.kubernetes.client.openapi.models.V1ObjectMeta;
import io.kubernetes.client.openapi.models.V1Pod;
import io.kubernetes.client.openapi.models.V1PodSpec;
import io.kubernetes.client.openapi.models.V1PodStatus;
import org.springframework.cloud.kubernetes.commons.AbstractKubernetesHealthIndicator;
import org.springframework.cloud.kubernetes.commons.PodUtils;
import org.springframework.util.CollectionUtils;
/**
* @author Ryan Baxter
*/
public class KubernetesClientHealthIndicator extends AbstractKubernetesHealthIndicator {
private final PodUtils<V1Pod> utils;
public KubernetesClientHealthIndicator(PodUtils<V1Pod> utils) {
this.utils = utils;
}
@Override
protected Map<String, Object> getDetails() {
V1Pod current = this.utils.currentPod().get();
if (current != null) {
Map<String, Object> details = CollectionUtils.newHashMap(8);
details.put(INSIDE, true);
V1ObjectMeta meta = current.getMetadata();
details.put(NAMESPACE, meta.getNamespace());
details.put(POD_NAME, meta.getName());
details.put(LABELS, meta.getLabels());
V1PodStatus status = current.getStatus();
details.put(POD_IP, status.getPodIP());
details.put(HOST_IP, status.getHostIP());
V1PodSpec spec = current.getSpec();
details.put(SERVICE_ACCOUNT, spec.getServiceAccountName());
details.put(NODE_NAME, spec.getNodeName());
return details;
}
else {
return Collections.singletonMap(INSIDE, false);
}
}
}

View File

@@ -1,66 +0,0 @@
/*
* Copyright 2013-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.client;
import java.util.Collections;
import java.util.Map;
import io.kubernetes.client.openapi.models.V1ObjectMeta;
import io.kubernetes.client.openapi.models.V1Pod;
import io.kubernetes.client.openapi.models.V1PodSpec;
import io.kubernetes.client.openapi.models.V1PodStatus;
import org.springframework.cloud.kubernetes.commons.AbstractKubernetesInfoContributor;
import org.springframework.cloud.kubernetes.commons.PodUtils;
import org.springframework.util.CollectionUtils;
/**
* @author Ryan Baxter
*/
public class KubernetesClientInfoContributor extends AbstractKubernetesInfoContributor {
private final PodUtils<V1Pod> utils;
public KubernetesClientInfoContributor(PodUtils<V1Pod> utils) {
this.utils = utils;
}
@Override
public Map<String, Object> getDetails() {
V1Pod current = this.utils.currentPod().get();
if (current != null) {
Map<String, Object> details = CollectionUtils.newHashMap(7);
details.put(INSIDE, true);
V1ObjectMeta meta = current.getMetadata();
details.put(NAMESPACE, meta.getNamespace());
details.put(POD_NAME, meta.getName());
V1PodSpec spec = current.getSpec();
details.put(SERVICE_ACCOUNT, spec.getServiceAccountName());
details.put(NODE_NAME, spec.getNodeName());
V1PodStatus status = current.getStatus();
details.put(POD_IP, status.getPodIP());
details.put(HOST_IP, status.getHostIP());
return details;
}
return Collections.singletonMap(INSIDE, false);
}
}

View File

@@ -1,118 +0,0 @@
/*
* Copyright 2013-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.client;
import java.nio.file.Paths;
import java.util.function.Supplier;
import io.kubernetes.client.openapi.ApiException;
import io.kubernetes.client.openapi.apis.CoreV1Api;
import io.kubernetes.client.openapi.models.V1Pod;
import io.kubernetes.client.util.Config;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.kubernetes.commons.EnvReader;
import org.springframework.cloud.kubernetes.commons.LazilyInstantiate;
import org.springframework.cloud.kubernetes.commons.PodUtils;
import org.springframework.util.StringUtils;
/**
* @author Ryan Baxter
*/
public class KubernetesClientPodUtils implements PodUtils<V1Pod> {
/**
* Hostname environment variable name.
*/
public static final String HOSTNAME = "HOSTNAME";
/**
* KUBERNETES_SERVICE_HOST environment variable name.
*/
public static final String KUBERNETES_SERVICE_HOST = "KUBERNETES_SERVICE_HOST";
private static final Log LOG = LogFactory.getLog(KubernetesClientPodUtils.class);
private final CoreV1Api client;
private final String hostName;
private final Supplier<V1Pod> current;
private final String namespace;
private final String serviceHost;
public KubernetesClientPodUtils(CoreV1Api client, String namespace) {
if (client == null) {
throw new IllegalArgumentException("Must provide an instance of KubernetesClient");
}
this.client = client;
this.hostName = EnvReader.getEnv(HOSTNAME);
this.serviceHost = EnvReader.getEnv(KUBERNETES_SERVICE_HOST);
this.current = LazilyInstantiate.using(this::internalGetPod);
this.namespace = namespace;
}
@Override
public Supplier<V1Pod> currentPod() {
return current;
}
@Override
public boolean isInsideKubernetes() {
return currentPod().get() != null;
}
private V1Pod internalGetPod() {
try {
if (isServiceHostEnvVarPresent() && isHostNameEnvVarPresent() && isServiceAccountFound()) {
LOG.debug("reading pod in namespace : " + namespace);
return client.readNamespacedPod(hostName, namespace, null);
}
}
catch (Throwable t) {
if (t instanceof ApiException apiException) {
LOG.warn("error reading pod, with error : " + apiException.getResponseBody());
}
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?", t);
}
return null;
}
private boolean isServiceHostEnvVarPresent() {
return StringUtils.hasLength(serviceHost);
}
private boolean isHostNameEnvVarPresent() {
return StringUtils.hasLength(hostName);
}
private boolean isServiceAccountFound() {
boolean serviceAccountPathPresent = Paths.get(Config.SERVICEACCOUNT_TOKEN_PATH).toFile().exists();
if (!serviceAccountPathPresent) {
// https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/
LOG.warn("serviceaccount path not present, did you disable it via 'automountServiceAccountToken : false'?"
+ " Major functionalities will not work without that property being set");
}
return serviceAccountPathPresent && Paths.get(Config.SERVICEACCOUNT_CA_PATH).toFile().exists();
}
}

View File

@@ -1,106 +0,0 @@
/*
* Copyright 2013-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.client;
import io.kubernetes.client.openapi.ApiClient;
import io.kubernetes.client.util.ClientBuilder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider;
import org.springframework.cloud.kubernetes.commons.config.NamespaceResolutionFailedException;
import org.springframework.util.StringUtils;
/**
* @author Ryan Baxter
*/
public final class KubernetesClientUtils {
private static final Log LOG = LogFactory.getLog(KubernetesClientUtils.class);
private KubernetesClientUtils() {
}
public static ApiClient createApiClientForInformerClient() {
ApiClient apiClient = kubernetesApiClient();
apiClient.setReadTimeout(0);
return apiClient;
}
public static ApiClient kubernetesApiClient() {
try {
// Assume we are running in a cluster
ApiClient apiClient = ClientBuilder.cluster().build();
LOG.info("Created API client in the cluster.");
return apiClient;
}
catch (Exception e) {
LOG.info("Could not create the Kubernetes ApiClient in a cluster environment, because : ", e);
LOG.info("Trying to use a \"standard\" configuration to create the Kubernetes ApiClient");
try {
ApiClient apiClient = ClientBuilder.defaultClient();
LOG.info("Created standard API client. Unless $KUBECONFIG or $HOME/.kube/config is defined, "
+ "this client will try to connect to localhost:8080");
return apiClient;
}
catch (Exception e1) {
LOG.warn("Could not create a Kubernetes ApiClient from either a cluster or standard environment. "
+ "Will return one that always connects to localhost:8080", e1);
return new ClientBuilder().build();
}
}
}
/**
* this method does the namespace resolution for both config map and secrets
* implementations. It tries these places to find the namespace:
*
* <pre>
* 1. from a normalized source (which can be null)
* 2. from a property 'spring.cloud.kubernetes.client.namespace', if such is present
* 3. from a String residing in a file denoted by `spring.cloud.kubernetes.client.serviceAccountNamespacePath`
* property, if such is present
* 4. from a String residing in `/var/run/secrets/kubernetes.io/serviceaccount/namespace` file,
* if such is present (kubernetes default path)
* </pre>
*
* If any of the above fail, we throw a NamespaceResolutionFailedException.
* @param namespace normalized namespace
* @param configurationTarget Config Map/Secret
* @param provider the provider which computes the namespace
* @return application namespace
* @throws NamespaceResolutionFailedException when namespace could not be resolved
*/
public static String getApplicationNamespace(String namespace, String configurationTarget,
KubernetesNamespaceProvider provider) {
if (StringUtils.hasText(namespace)) {
LOG.debug(configurationTarget + " namespace : " + namespace);
return namespace;
}
if (provider != null) {
String providerNamespace = provider.getNamespace();
if (StringUtils.hasText(providerNamespace)) {
LOG.debug(configurationTarget + " namespace from provider : " + namespace);
return providerNamespace;
}
}
throw new NamespaceResolutionFailedException("unresolved namespace");
}
}

View File

@@ -1,40 +0,0 @@
/*
* Copyright 2013-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.client.profile;
import io.kubernetes.client.openapi.apis.CoreV1Api;
import org.springframework.cloud.kubernetes.client.KubernetesClientPodUtils;
import org.springframework.cloud.kubernetes.commons.profile.AbstractKubernetesProfileEnvironmentPostProcessor;
import org.springframework.core.env.Environment;
import static io.kubernetes.client.util.Config.ENV_SERVICE_HOST;
/**
* @author Ryan Baxter
* @author Thomas Vitale
*/
public class KubernetesClientProfileEnvironmentPostProcessor extends AbstractKubernetesProfileEnvironmentPostProcessor {
@Override
protected boolean isInsideKubernetes(Environment environment) {
CoreV1Api api = new CoreV1Api();
KubernetesClientPodUtils utils = new KubernetesClientPodUtils(api, environment.getProperty(NAMESPACE_PROPERTY));
return environment.containsProperty(ENV_SERVICE_HOST) || utils.isInsideKubernetes();
}
}

View File

@@ -1,2 +0,0 @@
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.cloud.kubernetes.client.profile.KubernetesClientProfileEnvironmentPostProcessor

View File

@@ -1,2 +0,0 @@
org.springframework.cloud.kubernetes.client.KubernetesClientAutoConfiguration
org.springframework.cloud.kubernetes.client.KubernetesClientActuatorConfiguration

View File

@@ -1,58 +0,0 @@
/*
* Copyright 2013-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.client;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.health.ReactiveHealthContributorRegistry;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalManagementPort;
import org.springframework.cloud.kubernetes.client.example.App;
import org.springframework.http.MediaType;
import org.springframework.test.web.reactive.server.WebTestClient;
/**
* @author wind57
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = App.class,
properties = { "management.health.kubernetes.enabled=false", "management.endpoint.health.show-details=always",
"management.endpoint.health.show-components=always", "spring.main.cloud-platform=KUBERNETES",
"management.endpoints.web.exposure.include=health" })
class ActuatorDisabledHealthTest {
@Autowired
private ReactiveHealthContributorRegistry registry;
@Autowired
private WebTestClient webClient;
@LocalManagementPort
private int port;
@Test
void healthEndpointShouldNotContainKubernetes() {
this.webClient.get().uri("http://localhost:{port}/actuator/health", this.port)
.accept(MediaType.APPLICATION_JSON).exchange().expectStatus().isOk().expectBody()
.jsonPath("components.kubernetes").doesNotExist();
Assertions.assertNull(registry.getContributor("kubernetes"),
"reactive kubernetes contributor must NOT be present when 'management.health.kubernetes.enabled=false'");
}
}

View File

@@ -1,55 +0,0 @@
/*
* Copyright 2013-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.client;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.health.ReactiveHealthContributorRegistry;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalManagementPort;
import org.springframework.cloud.kubernetes.client.example.App;
import org.springframework.http.MediaType;
import org.springframework.test.web.reactive.server.WebTestClient;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = App.class,
properties = { "management.health.kubernetes.enabled=true", "management.endpoint.health.show-details=always",
"management.endpoint.health.show-components=always", "management.endpoints.web.exposure.include=health",
"spring.main.cloud-platform=KUBERNETES" })
class ActuatorEnabledHealthTest {
@Autowired
private WebTestClient webClient;
@Autowired
private ReactiveHealthContributorRegistry registry;
@LocalManagementPort
private int port;
@Test
void healthEndpointShouldContainKubernetes() {
this.webClient.get().uri("http://localhost:{port}/actuator/health", this.port)
.accept(MediaType.APPLICATION_JSON).exchange().expectStatus().isOk().expectBody()
.jsonPath("components.kubernetes").exists();
Assertions.assertNotNull(registry.getContributor("kubernetes"),
"reactive kubernetes contributor must be present when 'management.health.kubernetes.enabled=true'");
}
}

View File

@@ -1,46 +0,0 @@
/*
* Copyright 2013-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.client;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.client.example.App;
import org.springframework.context.ApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author wind57
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = App.class,
properties = "spring.main.cloud-platform=KUBERNETES")
public class KubernetesClientHealthIndicatorInsideTests {
@Autowired
private ApplicationContext context;
// test that the bean responsible for health is present.
// the actual fields it must provide are tested in
// KubernetesClientHealthIndicatorTests.
@Test
public void test() {
assertThat(context.getBean(KubernetesClientHealthIndicator.class)).isNotNull();
}
}

View File

@@ -1,46 +0,0 @@
/*
* Copyright 2013-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.client;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.client.example.App;
import org.springframework.context.ApplicationContext;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
/**
* @author wind57
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = App.class,
properties = { "management.health.kubernetes.enabled=false" })
public class KubernetesClientHealthIndicatorNotInsideTests {
@Autowired
private ApplicationContext context;
// test that the bean responsible for info contribution is NOT present.
@Test
public void test() {
assertThatThrownBy(() -> context.getBean(KubernetesClientHealthIndicator.class))
.isInstanceOf(NoSuchBeanDefinitionException.class);
}
}

View File

@@ -1,85 +0,0 @@
/*
* Copyright 2013-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.client;
import java.util.Map;
import io.kubernetes.client.openapi.models.V1Pod;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.cloud.kubernetes.commons.PodUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
import static org.springframework.cloud.kubernetes.client.KubernetesClientHealthIndicator.HOST_IP;
import static org.springframework.cloud.kubernetes.client.KubernetesClientHealthIndicator.INSIDE;
import static org.springframework.cloud.kubernetes.client.KubernetesClientHealthIndicator.LABELS;
import static org.springframework.cloud.kubernetes.client.KubernetesClientHealthIndicator.NAMESPACE;
import static org.springframework.cloud.kubernetes.client.KubernetesClientHealthIndicator.NODE_NAME;
import static org.springframework.cloud.kubernetes.client.KubernetesClientHealthIndicator.POD_IP;
import static org.springframework.cloud.kubernetes.client.KubernetesClientHealthIndicator.POD_NAME;
import static org.springframework.cloud.kubernetes.client.KubernetesClientHealthIndicator.SERVICE_ACCOUNT;
import static org.springframework.cloud.kubernetes.client.StubProvider.STUB_HOST_IP;
import static org.springframework.cloud.kubernetes.client.StubProvider.STUB_LABELS;
import static org.springframework.cloud.kubernetes.client.StubProvider.STUB_NAMESPACE;
import static org.springframework.cloud.kubernetes.client.StubProvider.STUB_NODE_NAME;
import static org.springframework.cloud.kubernetes.client.StubProvider.STUB_POD_IP;
import static org.springframework.cloud.kubernetes.client.StubProvider.STUB_POD_NAME;
import static org.springframework.cloud.kubernetes.client.StubProvider.STUB_SERVICE_ACCOUNT;
/**
* @author Ryan Baxter
*/
@ExtendWith(MockitoExtension.class)
class KubernetesClientHealthIndicatorTests {
@Mock
private PodUtils<V1Pod> utils;
@Test
void getDetailsNotInsideTest() {
when(utils.currentPod()).thenReturn(() -> null);
KubernetesClientHealthIndicator healthIndicator = new KubernetesClientHealthIndicator(utils);
Map<String, Object> details = healthIndicator.getDetails();
assertThat(details.size()).isEqualTo(1);
assertThat(details.get(INSIDE)).isEqualTo(false);
}
@Test
void getDetailsInsideTest() {
when(utils.currentPod()).thenReturn(StubProvider::stubPod);
KubernetesClientHealthIndicator healthIndicator = new KubernetesClientHealthIndicator(utils);
Map<String, Object> details = healthIndicator.getDetails();
assertThat(details.size()).isEqualTo(8);
assertThat(details.get(INSIDE)).isEqualTo(true);
assertThat(details.get(HOST_IP)).isEqualTo(STUB_HOST_IP);
assertThat(details.get(POD_IP)).isEqualTo(STUB_POD_IP);
assertThat(details.get(NODE_NAME)).isEqualTo(STUB_NODE_NAME);
assertThat(details.get(SERVICE_ACCOUNT)).isEqualTo(STUB_SERVICE_ACCOUNT);
assertThat(details.get(POD_NAME)).isEqualTo(STUB_POD_NAME);
assertThat(details.get(NAMESPACE)).isEqualTo(STUB_NAMESPACE);
assertThat(details.get(LABELS)).isEqualTo(STUB_LABELS);
}
}

View File

@@ -1,46 +0,0 @@
/*
* Copyright 2013-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.client;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.client.example.App;
import org.springframework.context.ApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author wind57
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = App.class,
properties = "spring.main.cloud-platform=KUBERNETES")
public class KubernetesClientInfoContributorInsideTests {
@Autowired
private ApplicationContext context;
// test that the bean responsible for info contribution is present.
// the actual fields it must provide are tested in
// KubernetesClientInfoContributorTests.
@Test
public void test() {
assertThat(context.getBean(KubernetesClientInfoContributor.class)).isNotNull();
}
}

View File

@@ -1,46 +0,0 @@
/*
* Copyright 2013-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.client;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.client.example.App;
import org.springframework.context.ApplicationContext;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
/**
* @author wind57
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = App.class,
properties = { "management.info.kubernetes.enabled=false" })
public class KubernetesClientInfoContributorNotInsideTests {
@Autowired
private ApplicationContext context;
// test that the bean responsible for info contribution is NOT present.
@Test
public void test() {
assertThatThrownBy(() -> context.getBean(KubernetesClientInfoContributor.class))
.isInstanceOf(NoSuchBeanDefinitionException.class);
}
}

View File

@@ -1,82 +0,0 @@
/*
* Copyright 2013-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.client;
import java.util.Map;
import io.kubernetes.client.openapi.models.V1Pod;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.cloud.kubernetes.commons.PodUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
import static org.springframework.cloud.kubernetes.client.KubernetesClientHealthIndicator.HOST_IP;
import static org.springframework.cloud.kubernetes.client.KubernetesClientHealthIndicator.INSIDE;
import static org.springframework.cloud.kubernetes.client.KubernetesClientHealthIndicator.NAMESPACE;
import static org.springframework.cloud.kubernetes.client.KubernetesClientHealthIndicator.NODE_NAME;
import static org.springframework.cloud.kubernetes.client.KubernetesClientHealthIndicator.POD_IP;
import static org.springframework.cloud.kubernetes.client.KubernetesClientHealthIndicator.POD_NAME;
import static org.springframework.cloud.kubernetes.client.KubernetesClientHealthIndicator.SERVICE_ACCOUNT;
import static org.springframework.cloud.kubernetes.client.StubProvider.STUB_HOST_IP;
import static org.springframework.cloud.kubernetes.client.StubProvider.STUB_NAMESPACE;
import static org.springframework.cloud.kubernetes.client.StubProvider.STUB_NODE_NAME;
import static org.springframework.cloud.kubernetes.client.StubProvider.STUB_POD_IP;
import static org.springframework.cloud.kubernetes.client.StubProvider.STUB_POD_NAME;
import static org.springframework.cloud.kubernetes.client.StubProvider.STUB_SERVICE_ACCOUNT;
/**
* @author Ryan Baxter
*/
@ExtendWith(MockitoExtension.class)
class KubernetesClientInfoContributorTests {
@Mock
private PodUtils<V1Pod> utils;
@Test
void getDetailsIsNotInside() {
when(utils.currentPod()).thenReturn(() -> null);
KubernetesClientInfoContributor infoContributor = new KubernetesClientInfoContributor(utils);
Map<String, Object> details = infoContributor.getDetails();
assertThat(details.size()).isEqualTo(1);
assertThat(details.get(INSIDE)).isEqualTo(false);
}
@Test
void getDetailsInside() {
when(utils.currentPod()).thenReturn(StubProvider::stubPod);
KubernetesClientInfoContributor infoContributor = new KubernetesClientInfoContributor(utils);
Map<String, Object> details = infoContributor.getDetails();
assertThat(details.size()).isEqualTo(7);
assertThat(details.get(INSIDE)).isEqualTo(true);
assertThat(details.get(HOST_IP)).isEqualTo(STUB_HOST_IP);
assertThat(details.get(POD_IP)).isEqualTo(STUB_POD_IP);
assertThat(details.get(NODE_NAME)).isEqualTo(STUB_NODE_NAME);
assertThat(details.get(SERVICE_ACCOUNT)).isEqualTo(STUB_SERVICE_ACCOUNT);
assertThat(details.get(POD_NAME)).isEqualTo(STUB_POD_NAME);
assertThat(details.get(NAMESPACE)).isEqualTo(STUB_NAMESPACE);
}
}

View File

@@ -1,174 +0,0 @@
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.client;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.function.Supplier;
import io.kubernetes.client.openapi.ApiException;
import io.kubernetes.client.openapi.apis.CoreV1Api;
import io.kubernetes.client.openapi.models.V1Pod;
import io.kubernetes.client.util.Config;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.springframework.cloud.kubernetes.commons.EnvReader;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
/**
* @author wind57
*/
public class KubernetesClientPodUtilsTests {
private static final String KUBERNETES_SERVICE_HOST = KubernetesClientPodUtils.KUBERNETES_SERVICE_HOST;
private static final String HOSTNAME = KubernetesClientPodUtils.HOSTNAME;
private static final String SERVICE_ACCOUNT_TOKEN_PATH = Config.SERVICEACCOUNT_TOKEN_PATH;
private static final String SERVICE_ACCOUNT_CERT_PATH = Config.SERVICEACCOUNT_CA_PATH;
private static final String POD_HOSTNAME = "pod-hostname";
private static final String HOST = "10.1.1.1";
private static final V1Pod POD = new V1Pod();
private final CoreV1Api client = Mockito.mock(CoreV1Api.class);
private final Path tokenPath = Mockito.mock(Path.class);
private final File tokenFile = Mockito.mock(File.class);
private final Path certPath = Mockito.mock(Path.class);
private final File certFile = Mockito.mock(File.class);
private MockedStatic<EnvReader> envReader;
private MockedStatic<Paths> paths;
@BeforeEach
public void before() {
envReader = Mockito.mockStatic(EnvReader.class);
paths = Mockito.mockStatic(Paths.class);
}
@AfterEach
public void after() {
envReader.close();
paths.close();
}
@Test
public void constructorThrowsIllegalArgumentExceptionWhenKubeClientIsNull() {
assertThatThrownBy(() -> new KubernetesClientPodUtils(null, "namespace"))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Must provide an instance of KubernetesClient");
}
@Test
public void serviceHostNotPresent() {
mockHost(null);
KubernetesClientPodUtils util = new KubernetesClientPodUtils(client, "namespace");
Supplier<V1Pod> sup = util.currentPod();
assertSupplierAndClient(sup, util);
}
@Test
public void hostNameNotPresent() {
mockHost(HOST);
mockHostname(null);
KubernetesClientPodUtils util = new KubernetesClientPodUtils(client, "namespace");
Supplier<V1Pod> sup = util.currentPod();
assertSupplierAndClient(sup, util);
}
@Test
public void serviceAccountPathNotPresent() {
mockTokenPath(false);
mockHostname(HOST);
KubernetesClientPodUtils util = new KubernetesClientPodUtils(client, "namespace");
Supplier<V1Pod> sup = util.currentPod();
assertSupplierAndClient(sup, util);
}
@Test
public void serviceAccountCertPathNotPresent() {
mockTokenPath(true);
mockCertPath(false);
mockHostname(HOST);
KubernetesClientPodUtils util = new KubernetesClientPodUtils(client, "namespace");
Supplier<V1Pod> sup = util.currentPod();
assertSupplierAndClient(sup, util);
}
@Test
public void allPresent() throws ApiException {
mockTokenPath(true);
mockCertPath(true);
mockHost(HOST);
mockHostname(POD_HOSTNAME);
mockPodResult();
KubernetesClientPodUtils util = new KubernetesClientPodUtils(client, "namespace");
Supplier<V1Pod> sup = util.currentPod();
assertThat(sup.get()).isNotNull();
assertThat(util.isInsideKubernetes()).isTrue();
}
private void assertSupplierAndClient(Supplier<V1Pod> sup, KubernetesClientPodUtils util) {
assertThat(sup.get()).isNull();
assertThat(util.isInsideKubernetes()).isFalse();
}
private void mockHost(String host) {
envReader.when(() -> EnvReader.getEnv(KUBERNETES_SERVICE_HOST)).thenReturn(host);
}
private void mockHostname(String name) {
envReader.when(() -> EnvReader.getEnv(HOSTNAME)).thenReturn(name);
}
private void mockTokenPath(boolean result) {
Mockito.when(tokenPath.toFile()).thenReturn(tokenFile);
Mockito.when(tokenFile.exists()).thenReturn(result);
paths.when(() -> Paths.get(SERVICE_ACCOUNT_TOKEN_PATH)).thenReturn(tokenPath);
}
private void mockCertPath(boolean result) {
Mockito.when(certPath.toFile()).thenReturn(certFile);
Mockito.when(certFile.exists()).thenReturn(result);
paths.when(() -> Paths.get(SERVICE_ACCOUNT_CERT_PATH)).thenReturn(certPath);
}
private void mockPodResult() throws ApiException {
Mockito.when(client.readNamespacedPod(POD_HOSTNAME, "namespace", null)).thenReturn(POD);
}
}

View File

@@ -1,54 +0,0 @@
/*
* Copyright 2013-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.client;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider;
import org.springframework.cloud.kubernetes.commons.config.NamespaceResolutionFailedException;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
/**
* @author wind57
*/
class KubernetesClientUtilsTests {
private final KubernetesNamespaceProvider provider = Mockito.mock(KubernetesNamespaceProvider.class);
@Test
void testNamespaceFromNormalizedSource() {
String result = KubernetesClientUtils.getApplicationNamespace("abc", "target", null);
assertThat(result).isEqualTo("abc");
}
@Test
void testNamespaceFromProvider() {
Mockito.when(provider.getNamespace()).thenReturn("def");
String result = KubernetesClientUtils.getApplicationNamespace("", "target", provider);
assertThat(result).isEqualTo("def");
}
@Test
void testNamespaceResolutionFailed() {
assertThatThrownBy(() -> KubernetesClientUtils.getApplicationNamespace("", "target", null))
.isInstanceOf(NamespaceResolutionFailedException.class);
}
}

View File

@@ -1,58 +0,0 @@
/*
* Copyright 2013-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.client;
import java.util.Collections;
import java.util.Map;
import io.kubernetes.client.openapi.models.V1ObjectMeta;
import io.kubernetes.client.openapi.models.V1Pod;
import io.kubernetes.client.openapi.models.V1PodSpec;
import io.kubernetes.client.openapi.models.V1PodStatus;
/**
* @author wind57
*/
final class StubProvider {
static final String STUB_POD_IP = "127.0.0.1";
static final String STUB_HOST_IP = "123.456.789.1";
static final String STUB_NODE_NAME = "nodeName";
static final String STUB_SERVICE_ACCOUNT = "serviceAccount";
static final String STUB_POD_NAME = "mypod";
static final String STUB_NAMESPACE = "default";
static final Map<String, String> STUB_LABELS = Collections.singletonMap("spring", "cloud");
private StubProvider() {
}
static V1Pod stubPod() {
V1ObjectMeta metaData = new V1ObjectMeta().labels(STUB_LABELS).name(STUB_POD_NAME).namespace(STUB_NAMESPACE);
V1PodStatus status = new V1PodStatus().podIP(STUB_POD_IP).hostIP(STUB_HOST_IP);
V1PodSpec spec = new V1PodSpec().nodeName(STUB_NODE_NAME).serviceAccountName(STUB_SERVICE_ACCOUNT);
return new V1Pod().metadata(metaData).status(status).spec(spec);
}
}

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