Add configuration to stop and start a GCP host BOSH Lite in the CircleCI pipeline
Template credentials required by BOSH from Circle CI environment variables Script to create CircleCI environments from file in LastPass Ensure only a single job runs at a time so that there is not a deadlock over BOSH lite environment (in future multiple envs will be enabled) Update Docker file to include bbl and bosh cli Ensure space and org created in CF before running test Run Unit Tests and ATs as seperate steps Reduce use of .block() in test set up check environment variables exist before creating in circleci use springcloudservices docker repo Use different property prefix for ATs to avoid clashing Fix deleteServiceInstance so it checks if SI present first to avoid error Ensure logging is configured to not leak secrets in CircleCI output Add script to wait for BOSH to release all it's locks before continuing [closes #45]
This commit is contained in:
committed by
Oliver Hughes
parent
3c5417be81
commit
9ba46e1578
@@ -1,58 +0,0 @@
|
||||
# Java Gradle CircleCI 2.0 configuration file
|
||||
#
|
||||
# Check https://circleci.com/docs/2.0/language-java/ for more details
|
||||
#
|
||||
version: 2
|
||||
jobs:
|
||||
build:
|
||||
docker:
|
||||
- image: circleci/openjdk:8-jdk
|
||||
|
||||
working_directory: ~/repo
|
||||
|
||||
environment:
|
||||
JAVA_TOOL_OPTIONS: "-Xms512m -Xmx2048m"
|
||||
GRADLE_OPTS: '-Dorg.gradle.jvmargs="-Xmx512m -Xmx2048m" -Dorg.gradle.daemon=false'
|
||||
TERM: dumb
|
||||
|
||||
steps:
|
||||
- checkout
|
||||
|
||||
- restore_cache:
|
||||
key: sc-app-broker-{{ .Branch }}
|
||||
|
||||
- run:
|
||||
name: "Download dependencies"
|
||||
command: ./gradlew assemble
|
||||
|
||||
- save_cache:
|
||||
paths:
|
||||
- ~/.gradle
|
||||
key: sc-app-broker-{{ .Branch }}
|
||||
|
||||
- run:
|
||||
name: "Disable Gradle daemon"
|
||||
command: touch ~/.gradle/gradle.properties && echo "org.gradle.daemon=false" >> ~/.gradle/gradle.properties
|
||||
|
||||
- run:
|
||||
name: "Run tests"
|
||||
command: ./gradlew clean codeCoverageReport --stacktrace --info --continue
|
||||
|
||||
- run:
|
||||
name: "Aggregate test results"
|
||||
when: always
|
||||
command: |
|
||||
mkdir -p ~/junit/
|
||||
find . -type f -regex ".*/build/test-results/.*" -exec cp {} ~/junit/ \;
|
||||
|
||||
- run:
|
||||
name: "Upload tests coverage"
|
||||
command: bash <(curl -s https://codecov.io/bash)
|
||||
|
||||
- store_artifacts:
|
||||
path: ~/junit/
|
||||
destination: artifacts
|
||||
|
||||
- store_test_results:
|
||||
path: ~/junit/
|
||||
destination: testartifacts
|
||||
1
.circleci/config.yml
Symbolic link
1
.circleci/config.yml
Symbolic link
@@ -0,0 +1 @@
|
||||
../ci/config.yml
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -12,3 +12,9 @@ target
|
||||
bin
|
||||
.idea
|
||||
out
|
||||
.envrc
|
||||
bbl-state.json
|
||||
director-vars-store.yml
|
||||
jumpbox-vars-store.yml
|
||||
director-vars-file.yml
|
||||
bbl-env.sh
|
||||
@@ -306,11 +306,11 @@ configure(rootProject) {
|
||||
}
|
||||
|
||||
pmd {
|
||||
ruleSetFiles = files("${project.rootDir}/config/pmdRuleSet.xml")
|
||||
ruleSetFiles = files("${project.rootDir}/ci/config/pmdRuleSet.xml")
|
||||
}
|
||||
|
||||
pmdTest {
|
||||
ruleSetFiles = files("${project.rootDir}/config/pmdTestRuleSet.xml")
|
||||
ruleSetFiles = files("${project.rootDir}/ci/config/pmdTestRuleSet.xml")
|
||||
}
|
||||
|
||||
task codeCoverageReport(type: JacocoReport) {
|
||||
|
||||
13
ci/Dockerfile
Normal file
13
ci/Dockerfile
Normal file
@@ -0,0 +1,13 @@
|
||||
FROM circleci/openjdk:8-jdk
|
||||
|
||||
RUN BOSH_URL="https://s3.amazonaws.com/bosh-cli-artifacts/bosh-cli-5.0.1-linux-amd64"&& \
|
||||
sudo curl --silent --show-error --location --fail --retry 3 --output /usr/bin/bosh $BOSH_URL && \
|
||||
sudo chmod +x /usr/bin/bosh && \
|
||||
bosh -v
|
||||
|
||||
RUN BBL_URL="https://github.com/cloudfoundry/bosh-bootloader/releases/download/v6.8.4/bbl-v6.8.4_linux_x86-64" && \
|
||||
sudo curl --silent --show-error --location --fail --retry 3 --output /usr/bin/bbl $BBL_URL && \
|
||||
sudo chmod +x /usr/bin/bbl && \
|
||||
bbl -v
|
||||
|
||||
RUN sudo apt-get install python3
|
||||
50
ci/README.md
Normal file
50
ci/README.md
Normal file
@@ -0,0 +1,50 @@
|
||||
#App Broker CI Pipeline
|
||||
|
||||
### Managing BOSH Lite on GCP
|
||||
|
||||
Currently CircleCI is used to run the acceptance tests. The system under test is a BOSH Lite, provisioned using [BOSH Bootloader](https://github.com/cloudfoundry/bosh-bootloader)
|
||||
|
||||
The BOSH Lite director can be targeted using following steps;
|
||||
|
||||
* Install bosh-cli and bbl
|
||||
```
|
||||
$ brew tap cloudfoundry/tap
|
||||
$ brew install bosh-cli
|
||||
$ brew install bbl
|
||||
```
|
||||
* Setup BBL environment variables
|
||||
```
|
||||
lpass show --notes 7083089362665788436 > ci/.envrc
|
||||
source ci/.envrc
|
||||
./scripts/populate-secrets-from-environment.sh
|
||||
./scripts/fix-bbl-state-json-line-breaks.py
|
||||
cd bbl-bosh-lite-environment/bbl-state
|
||||
bbl print-env > bbl-env.sh && source bbl-env.sh
|
||||
bosh deployments
|
||||
```
|
||||
|
||||
### Running CI pipeline locally
|
||||
CircleCI allows pipelines to be run locally, this is very useful development purposes
|
||||
|
||||
* Install circle command line tool to run local builds:
|
||||
```$bash
|
||||
curl -o /usr/local/bin/circleci https://circle-downloads.s3.amazonaws.com/releases/build_agent_wrapper/circleci && \
|
||||
chmod +x /usr/local/bin/circleci
|
||||
```
|
||||
* Run script to execute pipeline
|
||||
`ci/scripts/run-local-ci-job.sh`
|
||||
|
||||
### Credentials
|
||||
CircleCI uses environment variables to store credentials (these are stored inside
|
||||
a Vault instance within the CircleCI server). Environment variables need to
|
||||
be uploaded to circle so they can be used by the build.
|
||||
|
||||
#### Adding a new variable or credential
|
||||
* Edit the note in LastPass, adding the new variable:
|
||||
`lpass edit --notes 7083089362665788436 --sync=now`
|
||||
* Source all the variables in your local environment
|
||||
```
|
||||
lpass show --notes 7083089362665788436 > ci/.envrc
|
||||
source ci/.envrc
|
||||
```
|
||||
* Upload the variables to CircleCi: `ci/scripts/export-envvars-to-cirle.sh`
|
||||
42
ci/bbl-bosh-lite-environment/bbl-state/bbl-state.json.tmpl
Normal file
42
ci/bbl-bosh-lite-environment/bbl-state/bbl-state.json.tmpl
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"version": 14,
|
||||
"bblVersion": "6.8.4",
|
||||
"iaas": "gcp",
|
||||
"id": "<%= bblEnvId %>",
|
||||
"envID": "appbroker-env",
|
||||
"noDirector": false,
|
||||
"aws": {},
|
||||
"azure": {},
|
||||
"gcp": {
|
||||
"zone": "europe-west2-a",
|
||||
"region": "europe-west2",
|
||||
"zones": [
|
||||
"europe-west2-a",
|
||||
"europe-west2-b",
|
||||
"europe-west2-c"
|
||||
]
|
||||
},
|
||||
"vsphere": {},
|
||||
"openstack": {},
|
||||
"jumpbox": {
|
||||
"url": "<%= jumpboxUrl %>"
|
||||
},
|
||||
"bosh": {
|
||||
"directorName": "bosh-appbroker-env",
|
||||
"directorUsername": "<%= directorUsername %>",
|
||||
"directorPassword": "<%= directorPassword %>",
|
||||
"directorAddress": "https://<%= directorInternalIp %>:<%= directorPort %>",
|
||||
"directorSSLCA": <%= directorSSLCA %>,
|
||||
"directorSSLCertificate": <%= directorSSLCertificate %>,
|
||||
"directorSSLPrivateKey": <%= directorSSLPrivateKey %>
|
||||
},
|
||||
"tfState": "",
|
||||
"lb": {
|
||||
"type": "cf",
|
||||
"cert": <%= lbCert %>,
|
||||
"key": <%= lbKey %>,
|
||||
"chain": "",
|
||||
"domain": "<%= lbDomain %>"
|
||||
},
|
||||
"latestTFOutput": ""
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
internal_ip: <%= directorInternalIp %>
|
||||
@@ -0,0 +1,13 @@
|
||||
admin_password: <%= directorAdminPassword %>
|
||||
credhub_admin_client_secret: <%= credhubAdminClientSecret %>
|
||||
credhub_ca:
|
||||
certificate: |
|
||||
<%= credhubCA %>
|
||||
|
||||
uaa_ssl:
|
||||
certificate: |
|
||||
<%= uaaCert %>
|
||||
|
||||
jumpbox_ssh:
|
||||
private_key: |
|
||||
<%= directorJumpboxSshPrivateKey %>
|
||||
@@ -0,0 +1,3 @@
|
||||
jumpbox_ssh:
|
||||
private_key: |
|
||||
<%= jumpboxPrivateKey %>
|
||||
108
ci/config.yml
Normal file
108
ci/config.yml
Normal file
@@ -0,0 +1,108 @@
|
||||
# Java Gradle CircleCI 2.0 configuration file
|
||||
#
|
||||
# Check https://circleci.com/docs/2.0/language-java/ for more details
|
||||
#
|
||||
version: 2
|
||||
jobs:
|
||||
build:
|
||||
docker:
|
||||
- image: springcloudservices/spring-cloud-app-broker-ci:v1
|
||||
|
||||
working_directory: ~/repo
|
||||
|
||||
environment:
|
||||
JAVA_TOOL_OPTIONS: "-Xms512m -Xmx2048m"
|
||||
GRADLE_OPTS: '-Dorg.gradle.jvmargs="-Xmx512m -Xmx2048m" -Dorg.gradle.daemon=false'
|
||||
TERM: dumb
|
||||
|
||||
steps:
|
||||
- checkout
|
||||
|
||||
- restore_cache:
|
||||
key: sc-app-broker-{{ .Branch }}
|
||||
|
||||
- run:
|
||||
name: "Queue until all other builds finished"
|
||||
command: |
|
||||
cd ~/repo/ci/scripts
|
||||
./wait-for-other-builds-to-finish.sh 60
|
||||
|
||||
- run:
|
||||
name: "Download dependencies"
|
||||
command: ./gradlew assemble
|
||||
|
||||
- save_cache:
|
||||
paths:
|
||||
- ~/.gradle
|
||||
key: sc-app-broker-{{ .Branch }}
|
||||
|
||||
- run:
|
||||
name: "Disable Gradle daemon"
|
||||
command: touch ~/.gradle/gradle.properties && echo "org.gradle.daemon=false" >> ~/.gradle/gradle.properties
|
||||
|
||||
- run:
|
||||
name: "Run unit tests"
|
||||
command: ./gradlew clean codeCoverageReport --stacktrace
|
||||
|
||||
- run:
|
||||
name: "Aggregate test results"
|
||||
when: always
|
||||
command: |
|
||||
mkdir -p ~/junit/
|
||||
find . -type f -regex ".*/build/test-results/.*" -exec cp {} ~/junit/ \;
|
||||
|
||||
- run:
|
||||
name: "Upload tests coverage"
|
||||
command: bash <(curl -s https://codecov.io/bash)
|
||||
|
||||
- store_artifacts:
|
||||
path: ~/junit/
|
||||
destination: artifacts
|
||||
|
||||
- store_test_results:
|
||||
path: ~/junit/
|
||||
destination: testartifacts
|
||||
|
||||
- run:
|
||||
name: "Populate Secrets from Environment"
|
||||
command: |
|
||||
if [[ ${CIRCLE_BUILD_NUM} = "" ]]; then
|
||||
echo "populating from .envrc"
|
||||
[[ -f "ci/.envrc" ]] || (printf "Download .envrc from LastPass using \"lpass show --notes 7083089362665788436\" in directory ${CI_DIR} before running script"; exit 1)
|
||||
cd ~/repo/ci/
|
||||
cat .envrc >> $BASH_ENV
|
||||
./scripts/populate-secrets-from-environment.sh
|
||||
./scripts/fix-bbl-state-json-line-breaks.py
|
||||
cd bbl-bosh-lite-environment/bbl-state
|
||||
bbl print-env >> $BASH_ENV
|
||||
else
|
||||
echo "populating from bbl env"
|
||||
cd ~/repo/ci/scripts
|
||||
./populate-secrets-from-environment.sh
|
||||
cd ~/repo/ci/bbl-bosh-lite-environment/bbl-state
|
||||
bbl print-env >> $BASH_ENV
|
||||
fi
|
||||
|
||||
- run:
|
||||
name: "Start BOSH Lite"
|
||||
command: |
|
||||
cd ~/repo/ci
|
||||
./scripts/wait-for-bosh-locks-to-be-released.sh 30
|
||||
bosh -n -d cf start
|
||||
|
||||
- run:
|
||||
name: "Run acceptance tests"
|
||||
command: |
|
||||
./gradlew -b spring-cloud-app-broker-sample/build.gradle assemble
|
||||
./gradlew clean assemble check -PacceptanceTests --stacktrace --continue -b spring-cloud-app-broker-acceptance-tests/build.gradle
|
||||
|
||||
- store_test_results:
|
||||
path: ~/repo/spring-cloud-app-broker-acceptance-tests/build/test-results
|
||||
destination: acceptancetestartifacts
|
||||
|
||||
- run:
|
||||
name: "Stop BOSH Lite"
|
||||
command: |
|
||||
cd ~/repo/ci/bbl-bosh-lite-environment/bbl-state
|
||||
bosh -n -d cf stop
|
||||
when: always
|
||||
44
ci/scripts/delete-all-envvars-in-circle.sh
Executable file
44
ci/scripts/delete-all-envvars-in-circle.sh
Executable file
@@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env bash
|
||||
#TODO: List all envvars in Circle then delete to avoid needing to explicitly name all vars
|
||||
set -euo pipefail
|
||||
readonly CIRCLE_API_BASE=https://circleci.com/api/v1.1/project/github/spring-cloud-incubator/spring-cloud-app-broker
|
||||
|
||||
delete_envvars(){
|
||||
|
||||
delete_envvar_from_circle DIRECTOR_SSLCA
|
||||
delete_envvar_from_circle JUMPBOX_PRIVATE_KEY
|
||||
delete_envvar_from_circle UAA_CERT
|
||||
delete_envvar_from_circle CREDHUB_CA
|
||||
delete_envvar_from_circle LB_KEY
|
||||
delete_envvar_from_circle LB_CERT
|
||||
delete_envvar_from_circle DIRECTOR_SSLPRIVATE_KEY
|
||||
delete_envvar_from_circle DIRECTOR_SSLCERTIFICATE
|
||||
delete_envvar_from_circle DIRECTOR_JUMPBOX_SSH_PRIVATE_KEY
|
||||
delete_envvar_from_circle DIRECTOR_USERNAME
|
||||
delete_envvar_from_circle DIRECTOR_PASSWORD
|
||||
delete_envvar_from_circle DIRECTOR_INTERNAL_IP
|
||||
delete_envvar_from_circle DIRECTOR_PORT
|
||||
delete_envvar_from_circle JUMPBOX_URL
|
||||
delete_envvar_from_circle LB_DOMAIN
|
||||
delete_envvar_from_circle DIRECTOR_ADMIN_PASSWORD
|
||||
delete_envvar_from_circle CREDHUB_ADMIN_CLIENT_SECRET
|
||||
delete_envvar_from_circle BBL_ENV_ID
|
||||
delete_envvar_from_circle CIRCLECI_API_KEY
|
||||
delete_envvar_from_circle SPRING_CLOUD_APPBROKER_ACCEPTANCETEST_CLOUDFOUNDRY_API_HOST
|
||||
delete_envvar_from_circle SPRING_CLOUD_APPBROKER_ACCEPTANCETEST_CLOUDFOUNDRY_API_PORT
|
||||
delete_envvar_from_circle SPRING_CLOUD_APPBROKER_ACCEPTANCETEST_CLOUDFOUNDRY_USERNAME
|
||||
delete_envvar_from_circle SPRING_CLOUD_APPBROKER_ACCEPTANCETEST_CLOUDFOUNDRY_PASSWORD
|
||||
delete_envvar_from_circle SPRING_CLOUD_APPBROKER_ACCEPTANCETEST_CLOUDFOUNDRY_DEFAULT_ORG
|
||||
delete_envvar_from_circle SPRING_CLOUD_APPBROKER_ACCEPTANCETEST_CLOUDFOUNDRY_DEFAULT_SPACE
|
||||
delete_envvar_from_circle SPRING_CLOUD_APPBROKER_ACCEPTANCETEST_CLOUDFOUNDRY_SKIP_SSL_VALIDATION
|
||||
delete_envvar_from_circle TESTS_SAMPLEBROKERAPPPATH
|
||||
|
||||
}
|
||||
|
||||
delete_envvar_from_circle() {
|
||||
local envvar_name=$1
|
||||
curl -XDELETE "${CIRCLE_API_BASE}/envvar/${envvar_name}?circle-token=${CIRCLECI_API_KEY}"
|
||||
|
||||
}
|
||||
|
||||
delete_envvars
|
||||
77
ci/scripts/export-envvars-to-circle-ci.sh
Executable file
77
ci/scripts/export-envvars-to-circle-ci.sh
Executable file
@@ -0,0 +1,77 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
readonly CIRCLE_API_BASE=https://circleci.com/api/v1.1/project/github/spring-cloud-incubator/spring-cloud-app-broker
|
||||
readonly PROG_DIR=$(readlink -m $(dirname $0))
|
||||
readonly CI_DIR=$(dirname "${PROG_DIR}")
|
||||
|
||||
export_variables_to_circle() {
|
||||
[[ ! -f "${CI_DIR}/.envrc" ]] && printf "Download .envrc from LastPass using \"lpass show --notes 7083089362665788436\" in directory ${SCRIPTS_DIR} before running script" && exit 1
|
||||
|
||||
: ${DIRECTOR_SSLCA:?"Required Env Variable not found!"}
|
||||
: ${JUMPBOX_PRIVATE_KEY:?"Required Env Variable not found!"}
|
||||
: ${UAA_CERT:?"Required Env Variable not found!"}
|
||||
: ${CREDHUB_CA:?"Required Env Variable not found!"}
|
||||
: ${LB_KEY:?"Required Env Variable not found!"}
|
||||
: ${LB_CERT:?"Required Env Variable not found!"}
|
||||
: ${DIRECTOR_SSLPRIVATE_KEY:?"Required Env Variable not found!"}
|
||||
: ${DIRECTOR_SSLCERTIFICATE:?"Required Env Variable not found!"}
|
||||
: ${DIRECTOR_SSLCERTIFICATE:?"Required Env Variable not found!"}
|
||||
: ${DIRECTOR_JUMPBOX_SSH_PRIVATE_KEY:?"Required Env Variable not found!"}
|
||||
: ${DIRECTOR_USERNAME:?"Required Env Variable not found!"}
|
||||
: ${DIRECTOR_PASSWORD:?"Required Env Variable not found!"}
|
||||
: ${DIRECTOR_INTERNAL_IP:?"Required Env Variable not found!"}
|
||||
: ${DIRECTOR_PORT:?"Required Env Variable not found!"}
|
||||
: ${JUMPBOX_URL:?"Required Env Variable not found!"}
|
||||
: ${LB_DOMAIN:?"Required Env Variable not found!"}
|
||||
: ${DIRECTOR_ADMIN_PASSWORD:?"Required Env Variable not found!"}
|
||||
: ${CREDHUB_ADMIN_CLIENT_SECRET:?"Required Env Variable not found!"}
|
||||
: ${BBL_ENV_ID:?"Required Env Variable not found!"}
|
||||
: ${CIRCLECI_API_KEY:?"Required Env Variable not found!"}
|
||||
: ${SPRING_CLOUD_APPBROKER_ACCEPTANCETEST_CLOUDFOUNDRY_API_HOST:?"Required Env Variable not found!"}
|
||||
: ${SPRING_CLOUD_APPBROKER_ACCEPTANCETEST_CLOUDFOUNDRY_API_PORT:?"Required Env Variable not found!"}
|
||||
: ${SPRING_CLOUD_APPBROKER_ACCEPTANCETEST_CLOUDFOUNDRY_USERNAME:?"Required Env Variable not found!"}
|
||||
: ${SPRING_CLOUD_APPBROKER_ACCEPTANCETEST_CLOUDFOUNDRY_PASSWORD:?"Required Env Variable not found!"}
|
||||
: ${SPRING_CLOUD_APPBROKER_ACCEPTANCETEST_CLOUDFOUNDRY_DEFAULT_ORG:?"Required Env Variable not found!"}
|
||||
: ${SPRING_CLOUD_APPBROKER_ACCEPTANCETEST_CLOUDFOUNDRY_DEFAULT_SPACE:?"Required Env Variable not found!"}
|
||||
: ${SPRING_CLOUD_APPBROKER_ACCEPTANCETEST_CLOUDFOUNDRY_SKIP_SSL_VALIDATION:?"Required Env Variable not found!"}
|
||||
: ${TESTS_SAMPLEBROKERAPPPATH:?"Required Env Variable not found!"}
|
||||
|
||||
create_envvar_in_circle DIRECTOR_SSLCA "${DIRECTOR_SSLCA}"
|
||||
create_envvar_in_circle JUMPBOX_PRIVATE_KEY "${JUMPBOX_PRIVATE_KEY}"
|
||||
create_envvar_in_circle UAA_CERT "${UAA_CERT}"
|
||||
create_envvar_in_circle CREDHUB_CA "${CREDHUB_CA}"
|
||||
create_envvar_in_circle LB_KEY "${LB_KEY}"
|
||||
create_envvar_in_circle LB_CERT "${LB_CERT}"
|
||||
create_envvar_in_circle DIRECTOR_SSLPRIVATE_KEY "${DIRECTOR_SSLPRIVATE_KEY}"
|
||||
create_envvar_in_circle DIRECTOR_SSLCERTIFICATE "${DIRECTOR_SSLCERTIFICATE}"
|
||||
create_envvar_in_circle DIRECTOR_JUMPBOX_SSH_PRIVATE_KEY "${DIRECTOR_JUMPBOX_SSH_PRIVATE_KEY}"
|
||||
create_envvar_in_circle DIRECTOR_USERNAME "${DIRECTOR_USERNAME}"
|
||||
create_envvar_in_circle DIRECTOR_PASSWORD "${DIRECTOR_PASSWORD}"
|
||||
create_envvar_in_circle DIRECTOR_INTERNAL_IP "${DIRECTOR_INTERNAL_IP}"
|
||||
create_envvar_in_circle DIRECTOR_PORT "${DIRECTOR_PORT}"
|
||||
create_envvar_in_circle JUMPBOX_URL "${JUMPBOX_URL}"
|
||||
create_envvar_in_circle LB_DOMAIN "${LB_DOMAIN}"
|
||||
create_envvar_in_circle DIRECTOR_ADMIN_PASSWORD "${DIRECTOR_ADMIN_PASSWORD}"
|
||||
create_envvar_in_circle CREDHUB_ADMIN_CLIENT_SECRET "${CREDHUB_ADMIN_CLIENT_SECRET}"
|
||||
create_envvar_in_circle BBL_ENV_ID "${BBL_ENV_ID}"
|
||||
create_envvar_in_circle CIRCLECI_API_KEY "${CIRCLECI_API_KEY}"
|
||||
create_envvar_in_circle SPRING_CLOUD_APPBROKER_ACCEPTANCETEST_CLOUDFOUNDRY_API_HOST "${SPRING_CLOUD_APPBROKER_ACCEPTANCETEST_CLOUDFOUNDRY_API_HOST}"
|
||||
create_envvar_in_circle SPRING_CLOUD_APPBROKER_ACCEPTANCETEST_CLOUDFOUNDRY_API_PORT "${SPRING_CLOUD_APPBROKER_ACCEPTANCETEST_CLOUDFOUNDRY_API_PORT}"
|
||||
create_envvar_in_circle SPRING_CLOUD_APPBROKER_ACCEPTANCETEST_CLOUDFOUNDRY_USERNAME "${SPRING_CLOUD_APPBROKER_ACCEPTANCETEST_CLOUDFOUNDRY_USERNAME}"
|
||||
create_envvar_in_circle SPRING_CLOUD_APPBROKER_ACCEPTANCETEST_CLOUDFOUNDRY_PASSWORD "${SPRING_CLOUD_APPBROKER_ACCEPTANCETEST_CLOUDFOUNDRY_PASSWORD}"
|
||||
create_envvar_in_circle SPRING_CLOUD_APPBROKER_ACCEPTANCETEST_CLOUDFOUNDRY_DEFAULT_ORG "${SPRING_CLOUD_APPBROKER_ACCEPTANCETEST_CLOUDFOUNDRY_DEFAULT_ORG}"
|
||||
create_envvar_in_circle SPRING_CLOUD_APPBROKER_ACCEPTANCETEST_CLOUDFOUNDRY_DEFAULT_SPACE "${SPRING_CLOUD_APPBROKER_ACCEPTANCETEST_CLOUDFOUNDRY_DEFAULT_SPACE}"
|
||||
create_envvar_in_circle SPRING_CLOUD_APPBROKER_ACCEPTANCETEST_CLOUDFOUNDRY_SKIP_SSL_VALIDATION "${SPRING_CLOUD_APPBROKER_ACCEPTANCETEST_CLOUDFOUNDRY_SKIP_SSL_VALIDATION}"
|
||||
create_envvar_in_circle TESTS_SAMPLEBROKERAPPPATH "${TESTS_SAMPLEBROKERAPPPATH}"
|
||||
|
||||
}
|
||||
|
||||
create_envvar_in_circle() {
|
||||
local envvar_name=$1
|
||||
local envvar_value=$2
|
||||
|
||||
curl -XPOST "${CIRCLE_API_BASE}/envvar?circle-token=${CIRCLECI_API_KEY}" \
|
||||
-H "Content-Type: application/json" -d '{"name": "'"${envvar_name}"'", "value": "'"${envvar_value}"'"}'
|
||||
}
|
||||
|
||||
export_variables_to_circle
|
||||
12
ci/scripts/fix-bbl-state-json-line-breaks.py
Executable file
12
ci/scripts/fix-bbl-state-json-line-breaks.py
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env python3
|
||||
# I couldn't get this working with sed so python it is!
|
||||
import fileinput
|
||||
import os
|
||||
import sys
|
||||
|
||||
script_path = os.path.dirname(os.path.realpath(sys.argv[0]))
|
||||
ci_dir = os.path.dirname(script_path)
|
||||
|
||||
with fileinput.FileInput(os.path.join(ci_dir, 'bbl-bosh-lite-environment/bbl-state/bbl-state.json'), inplace=True) as file:
|
||||
for line in file:
|
||||
print(line.replace('\\\\n', '\\n'), end='')
|
||||
48
ci/scripts/populate-secrets-from-environment.sh
Executable file
48
ci/scripts/populate-secrets-from-environment.sh
Executable file
@@ -0,0 +1,48 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
readonly PROG_DIR=$(readlink -m $(dirname $0))
|
||||
readonly SCRIPTS_DIR=$(dirname "${PROG_DIR}")
|
||||
readonly CI_DIR=$(dirname "${SCRIPTS_DIR}")
|
||||
cd "${SCRIPTS_DIR}/bbl-bosh-lite-environment/bbl-state/"
|
||||
|
||||
substitute_bbl_state_file(){
|
||||
|
||||
perl -pe "s|<%= bblEnvId %>|${BBL_ENV_ID}|;" \
|
||||
-pe "s|<%= jumpboxUrl %>|${JUMPBOX_URL}|;" \
|
||||
-pe "s|<%= directorUsername %>|${DIRECTOR_USERNAME}|;" \
|
||||
-pe "s|<%= directorPassword %>|${DIRECTOR_PASSWORD}|;" \
|
||||
-pe "s|<%= directorInternalIp %>|${DIRECTOR_INTERNAL_IP}|;" \
|
||||
-pe "s|<%= directorPort %>|${DIRECTOR_PORT}|;" \
|
||||
-pe "s|<%= directorSSLCA %>|$(echo ${DIRECTOR_SSLCA} | jq -aR .)|;" \
|
||||
-pe "s|<%= directorSSLCertificate %>|$(echo ${DIRECTOR_SSLCERTIFICATE} | jq -aR .)|;" \
|
||||
-pe "s|<%= directorSSLPrivateKey %>|$(echo ${DIRECTOR_SSLPRIVATE_KEY} | jq -aR .)|;" \
|
||||
-pe "s|<%= lbCert %>|$(echo ${LB_CERT} | jq -aR .)|;" \
|
||||
-pe "s|<%= lbKey %>|$(echo ${LB_KEY} | jq -aR .)|;" \
|
||||
-pe "s|<%= lbDomain %>|${LB_DOMAIN}|;" \
|
||||
bbl-state.json.tmpl > bbl-state.json
|
||||
}
|
||||
|
||||
substitute_director_vars_store(){
|
||||
perl -pe "s|<%= directorAdminPassword %>|${DIRECTOR_ADMIN_PASSWORD}|;" \
|
||||
-pe "s|<%= credhubAdminClientSecret %>|${CREDHUB_ADMIN_CLIENT_SECRET}|;" \
|
||||
-pe "s|<%= credhubCA %>|${CREDHUB_CA}|;" \
|
||||
-pe "s|<%= uaaCert %>|${UAA_CERT}|;" \
|
||||
-pe "s|<%= directorJumpboxSshPrivateKey %>|${DIRECTOR_JUMPBOX_SSH_PRIVATE_KEY}|;" \
|
||||
director-vars-store.yml.tmpl > vars/director-vars-store.yml
|
||||
}
|
||||
|
||||
substitute_director_vars_file(){
|
||||
perl -pe "s|<%= directorInternalIp %>|${DIRECTOR_INTERNAL_IP}|;" \
|
||||
director-vars-file.yml.tmpl > vars/director-vars-file.yml
|
||||
}
|
||||
|
||||
substitute_jumpbox_vars_store(){
|
||||
perl -pe "s|<%= jumpboxPrivateKey %>|${JUMPBOX_PRIVATE_KEY}|;" \
|
||||
jumpbox-vars-store.yml.tmpl > vars/jumpbox-vars-store.yml
|
||||
}
|
||||
|
||||
substitute_bbl_state_file
|
||||
substitute_director_vars_store
|
||||
substitute_director_vars_file
|
||||
substitute_jumpbox_vars_store
|
||||
27
ci/scripts/run-local-ci-job.sh
Executable file
27
ci/scripts/run-local-ci-job.sh
Executable file
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
readonly PROG_DIR=$(readlink -m $(dirname $0))
|
||||
readonly CI_DIR=$(dirname "${PROG_DIR}")
|
||||
readonly PROJECT_ROOT=$(dirname "${CI_DIR}")
|
||||
|
||||
validate(){
|
||||
[[ $(type circleci) ]] || (echo "circleci tool not installed" >&2 ; circle_usage ; exit 2)
|
||||
[[ -f "${CI_DIR}/.envrc" ]] || (printf "Download .envrc from LastPass using \"lpass show --notes 7083089362665788436\" in directory ${CI_DIR} before running script"; exit 1)
|
||||
}
|
||||
|
||||
circle_usage() {
|
||||
cat <<- EOF
|
||||
Install circle command line tool to run local builds:
|
||||
"curl -o /usr/local/bin/circleci https://circle-downloads.s3.amazonaws.com/releases/build_agent_wrapper/circleci && \
|
||||
chmod +x /usr/local/bin/circleci"
|
||||
EOF
|
||||
}
|
||||
|
||||
run_circle_command(){
|
||||
cd "${PROJECT_ROOT}"
|
||||
circleci build
|
||||
}
|
||||
|
||||
validate
|
||||
run_circle_command
|
||||
33
ci/scripts/wait-for-bosh-locks-to-be-released.sh
Executable file
33
ci/scripts/wait-for-bosh-locks-to-be-released.sh
Executable file
@@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env bash
|
||||
# The BOSH director might be in use by someone else. Check `bosh locks` command before running ATs
|
||||
set -euo pipefail
|
||||
|
||||
[[ -z "${1:-}" ]] && printf "Must provide Max Queue Time in *minutes* as script argument\n" && exit 1
|
||||
|
||||
readonly MAX_TIME=${1}
|
||||
|
||||
wait_for_bosh_locks_to_be_released(){
|
||||
local max_time_seconds=$((MAX_TIME * 60))
|
||||
|
||||
until [[ $(bosh locks --json | jq '.Tables[].Rows | length') -eq 0 ]]; do
|
||||
|
||||
sleep 10
|
||||
printf "."
|
||||
if [[ "${SECONDS}" -gt "${max_time_seconds}" ]]; then
|
||||
# After cancelling the build, wait some time to ensure the job has been cancelled otherwise
|
||||
# the script will exit and the job will be incorrectly marked success or failed
|
||||
printf "Max time exceeded waiting for bosh locks to be released, cancelling build\n"
|
||||
cancel_current_build
|
||||
sleep 20
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
printf "No BOSH locks found, continuing build\n"
|
||||
}
|
||||
|
||||
cancel_current_build() {
|
||||
curl -s -X POST \
|
||||
"https://circleci.com/api/v1.1/project/github/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}/${CIRCLE_BUILD_NUM}/cancel?circle-token=${CIRCLECI_API_KEY}" > /dev/null
|
||||
}
|
||||
|
||||
wait_for_bosh_locks_to_be_released
|
||||
40
ci/scripts/wait-for-other-builds-to-finish.sh
Executable file
40
ci/scripts/wait-for-other-builds-to-finish.sh
Executable file
@@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
[[ -z "${1:-}" ]] && printf "Must provide Max Queue Time in *minutes* as script argument\n" && exit 1
|
||||
[[ -z "${CIRCLE_BUILD_NUM:-}" ]] && printf "skipping, not running on remote circleci environment\n" && exit 0
|
||||
: ${CIRCLECI_API_KEY:?"Required Env Variable not found!"}
|
||||
: ${CIRCLE_PROJECT_USERNAME:?"Required Env Variable not found!"}
|
||||
: ${CIRCLE_PROJECT_REPONAME:?"Required Env Variable not found!"}
|
||||
: ${CIRCLE_REPOSITORY_URL:?"Required Env Variable not found!"}
|
||||
|
||||
readonly MAX_TIME=${1}
|
||||
|
||||
wait_for_previous_builds_to_complete(){
|
||||
local max_time_seconds=$((MAX_TIME * 60))
|
||||
|
||||
until [[ $(curl -s "https://circleci.com/api/v1.1/project/github/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}?circle-token=${CIRCLECI_API_KEY}&filter=running" \
|
||||
| jq 'min_by(.build_num) | .build_num') \
|
||||
-eq ${CIRCLE_BUILD_NUM} ]]; do
|
||||
|
||||
sleep 10
|
||||
printf "."
|
||||
if [[ "${SECONDS}" -gt "${max_time_seconds}" ]]; then
|
||||
# After cancelling the build, wait some time to ensure the job has been cancelled otherwise
|
||||
# the script will exit and the job will be incorrectly marked success or failed
|
||||
printf "Max time exceeded waiting to reach front of queue, cancelling build\n"
|
||||
cancel_current_build
|
||||
sleep 20
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
printf "Build is front of queue\n"
|
||||
|
||||
}
|
||||
|
||||
cancel_current_build() {
|
||||
curl -s -X POST \
|
||||
"https://circleci.com/api/v1.1/project/github/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}/${CIRCLE_BUILD_NUM}/cancel?circle-token=${CIRCLECI_API_KEY}" > /dev/null
|
||||
}
|
||||
|
||||
wait_for_previous_builds_to_complete
|
||||
@@ -12,14 +12,14 @@
|
||||
|
||||
The tests require the following properties to be set:
|
||||
|
||||
* `tests.sampleAppPath` - The path to the sample broker jar (eg. /spring-cloud-app-broker-sample/build/libs/spring-cloud-app-broker-sample.jar).
|
||||
* `spring.cloud.appbroker.deployer.cloudfoundry.api-host` - The CF API host where the tests are going to run.
|
||||
* `spring.cloud.appbroker.deployer.cloudfoundry.api-port` - The CF API port where the tests are going to run.
|
||||
* `spring.cloud.appbroker.deployer.cloudfoundry.username` - The CF API username where the tests are going to run.
|
||||
* `spring.cloud.appbroker.deployer.cloudfoundry.password` - The CF API password where the tests are going to run.
|
||||
* `spring.cloud.appbroker.deployer.cloudfoundry.default-org` - The CF organization where the tests are going to run.
|
||||
* `spring.cloud.appbroker.deployer.cloudfoundry.default-space` - The CF space where the tests are going to run.
|
||||
* `spring.cloud.appbroker.deployer.cloudfoundry.skip-ssl-validation` - If SSL validation should be skipped.
|
||||
* `tests.sampleBrokerAppPath` - The path to the sample broker jar (eg. /spring-cloud-app-broker-sample/build/libs/spring-cloud-app-broker-sample.jar).
|
||||
* `spring.cloud.appbroker.acceptance-test.cloudfoundry.api-host` - The CF API host where the tests are going to run.
|
||||
* `spring.cloud.appbroker.acceptance-test.cloudfoundry.api-port` - The CF API port where the tests are going to run.
|
||||
* `spring.cloud.appbroker.acceptance-test.cloudfoundry.username` - The CF API username where the tests are going to run.
|
||||
* `spring.cloud.appbroker.acceptance-test.cloudfoundry.password` - The CF API password where the tests are going to run.
|
||||
* `spring.cloud.appbroker.acceptance-test.cloudfoundry.default-org` - The CF organization where the tests are going to run.
|
||||
* `spring.cloud.appbroker.acceptance-test.cloudfoundry.default-space` - The CF space where the tests are going to run.
|
||||
* `spring.cloud.appbroker.acceptance-test.cloudfoundry.skip-ssl-validation` - If SSL validation should be skipped.
|
||||
|
||||
These properties can be set with `-D` system properties on the gradle command line.
|
||||
|
||||
@@ -28,11 +28,11 @@ Also, the flag acceptanceTests must be provided as `-PacceptanceTests`.
|
||||
[source,bash]
|
||||
----
|
||||
$ ./gradlew test -PacceptanceTests \
|
||||
-Dtests.sampleAppPath=/path/to/broker.jar \
|
||||
-Dspring.cloud.appbroker.deployer.cloudfoundry.api-host=api.cf.my.cf.io \
|
||||
-Dspring.cloud.appbroker.deployer.cloudfoundry.api-port=443 \
|
||||
-Dspring.cloud.appbroker.deployer.cloudfoundry.username=admin \
|
||||
-Dspring.cloud.appbroker.deployer.cloudfoundry.password=password \
|
||||
-Dspring.cloud.appbroker.deployer.cloudfoundry.default-org=test \
|
||||
-Dspring.cloud.appbroker.deployer.cloudfoundry.default-space=development \
|
||||
-Dspring.cloud.appbroker.deployer.cloudfoundry.skip-ssl-validation=true
|
||||
-Dtests.sampleBrokerAppPath=/path/to/broker.jar \
|
||||
-Dspring.cloud.appbroker.acceptance-test.cloudfoundry.api-host=api.cf.my.cf.io \
|
||||
-Dspring.cloud.appbroker.acceptance-test.cloudfoundry.api-port=443 \
|
||||
-Dspring.cloud.appbroker.acceptance-test.cloudfoundry.username=admin \
|
||||
-Dspring.cloud.appbroker.acceptance-test.cloudfoundry.password=password \
|
||||
-Dspring.cloud.appbroker.acceptance-test.cloudfoundry.default-org=test \
|
||||
-Dspring.cloud.appbroker.acceptance-test.cloudfoundry.default-space=development \
|
||||
-Dspring.cloud.appbroker.acceptance-test.cloudfoundry.skip-ssl-validation=true
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2016-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.appbroker.acceptance;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
@ConfigurationProperties(value = "tests")
|
||||
public class AcceptanceTestProperties {
|
||||
|
||||
private String sampleBrokerAppPath;
|
||||
|
||||
public String getSampleBrokerAppPath() {
|
||||
return sampleBrokerAppPath;
|
||||
}
|
||||
|
||||
public void setSampleBrokerAppPath(String sampleBrokerAppPath) {
|
||||
this.sampleBrokerAppPath = sampleBrokerAppPath;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,15 +18,19 @@ package org.springframework.cloud.appbroker.acceptance;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
import org.cloudfoundry.operations.applications.ApplicationEnvironments;
|
||||
import org.cloudfoundry.operations.applications.ApplicationSummary;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.appbroker.acceptance.fixtures.cf.CloudFoundryClientConfiguration;
|
||||
import org.springframework.cloud.appbroker.acceptance.fixtures.cf.CloudFoundryService;
|
||||
@@ -35,6 +39,7 @@ import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
@SpringBootTest(classes = {CloudFoundryClientConfiguration.class, CloudFoundryService.class})
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@ExtendWith(BrokerPropertiesParameterResolver.class)
|
||||
@EnableConfigurationProperties(AcceptanceTestProperties.class)
|
||||
class CloudFoundryAcceptanceTest {
|
||||
|
||||
@BeforeEach
|
||||
@@ -43,7 +48,7 @@ class CloudFoundryAcceptanceTest {
|
||||
}
|
||||
|
||||
private static final String SAMPLE_BROKER_APP_NAME = "sample-broker";
|
||||
private static final String BROKER_NAME = "sample-broker-name";
|
||||
private static final String SERVICE_BROKER_NAME = "sample-broker-name";
|
||||
private static final String SERVICE_NAME = "example";
|
||||
private static final String PLAN_NAME = "standard";
|
||||
private static final String SERVICE_INSTANCE_NAME = "my-service";
|
||||
@@ -51,53 +56,68 @@ class CloudFoundryAcceptanceTest {
|
||||
@Autowired
|
||||
private CloudFoundryService cloudFoundryService;
|
||||
|
||||
@Value("${tests.sampleAppPath}")
|
||||
private String sampleAppPath;
|
||||
@Autowired
|
||||
private AcceptanceTestProperties acceptanceTestProperties;
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
cleanup();
|
||||
blockingSubscribe(cleanup());
|
||||
}
|
||||
|
||||
void initializeBroker(String[] properties) {
|
||||
cleanup();
|
||||
void initializeBroker(String[] backingAppProperties) {
|
||||
|
||||
cloudFoundryService.pushAppNoStart(SAMPLE_BROKER_APP_NAME, getSampleAppPath());
|
||||
cloudFoundryService.setBrokerAppEnvironment(properties);
|
||||
cloudFoundryService.startApplication(SAMPLE_BROKER_APP_NAME);
|
||||
|
||||
String backingAppURL = cloudFoundryService.getApplicationRoute(SAMPLE_BROKER_APP_NAME);
|
||||
cloudFoundryService.createServiceBroker(BROKER_NAME, backingAppURL);
|
||||
cloudFoundryService.enableServiceBrokerAccess(SERVICE_NAME);
|
||||
blockingSubscribe(cloudFoundryService
|
||||
.getOrCreateDefaultOrganization()
|
||||
.then(cloudFoundryService.getOrCreateDefaultSpace())
|
||||
.then(cloudFoundryService.pushAppBroker(SAMPLE_BROKER_APP_NAME, getSampleBrokerAppPath(), backingAppProperties))
|
||||
.then(cloudFoundryService.createServiceBroker(SERVICE_BROKER_NAME, SAMPLE_BROKER_APP_NAME))
|
||||
.then(cloudFoundryService.enableServiceBrokerAccess(SERVICE_NAME)));
|
||||
}
|
||||
|
||||
private void cleanup() {
|
||||
cloudFoundryService.deleteServiceInstance(SERVICE_INSTANCE_NAME);
|
||||
cloudFoundryService.deleteServiceBroker(BROKER_NAME);
|
||||
cloudFoundryService.deleteBackingApp(SAMPLE_BROKER_APP_NAME);
|
||||
private Mono<Void> cleanup() {
|
||||
return cloudFoundryService
|
||||
.deleteServiceInstance(SERVICE_INSTANCE_NAME)
|
||||
.then(cloudFoundryService.deleteServiceBroker(SERVICE_BROKER_NAME))
|
||||
.then(cloudFoundryService.deleteBackingApp(SAMPLE_BROKER_APP_NAME));
|
||||
}
|
||||
|
||||
void createServiceInstance() {
|
||||
cloudFoundryService.createServiceInstance(PLAN_NAME, SERVICE_NAME, SERVICE_INSTANCE_NAME);
|
||||
blockingSubscribe(cloudFoundryService.createServiceInstance(PLAN_NAME, SERVICE_NAME, SERVICE_INSTANCE_NAME));
|
||||
}
|
||||
|
||||
void deleteServiceInstance() {
|
||||
cloudFoundryService.deleteServiceInstance(SERVICE_INSTANCE_NAME);
|
||||
blockingSubscribe(cloudFoundryService.deleteServiceInstance(SERVICE_INSTANCE_NAME));
|
||||
}
|
||||
|
||||
Optional<ApplicationSummary> getApplicationSummaryByName(String appName) {
|
||||
List<ApplicationSummary> applicationsAfterDeletion = cloudFoundryService.getApplications();
|
||||
|
||||
return applicationsAfterDeletion.stream()
|
||||
return cloudFoundryService
|
||||
.getApplications()
|
||||
.flatMapMany(Flux::fromIterable)
|
||||
.filter(applicationSummary -> appName.equals(applicationSummary.getName()))
|
||||
.findFirst();
|
||||
.next()
|
||||
.blockOptional();
|
||||
}
|
||||
|
||||
ApplicationEnvironments getApplicationEnvironmentByName(String appName) {
|
||||
return cloudFoundryService.getApplicationEnvironmentByAppName(appName);
|
||||
return cloudFoundryService.getApplicationEnvironmentByAppName(appName).block();
|
||||
}
|
||||
|
||||
private Path getSampleAppPath() {
|
||||
return Paths.get(sampleAppPath, "");
|
||||
private Path getSampleBrokerAppPath() {
|
||||
return Paths.get(acceptanceTestProperties.getSampleBrokerAppPath(), "");
|
||||
}
|
||||
|
||||
private <T> void blockingSubscribe(Mono<? super T> publisher){
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
publisher.subscribe(System.out::println, t -> {
|
||||
t.printStackTrace();
|
||||
latch.countDown();
|
||||
}, latch::countDown);
|
||||
try {
|
||||
latch.await();
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,16 +17,17 @@
|
||||
package org.springframework.cloud.appbroker.acceptance.fixtures.cf;
|
||||
|
||||
import java.net.URI;
|
||||
import org.cloudfoundry.reactor.ProxyConfiguration;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import org.cloudfoundry.reactor.ProxyConfiguration;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import static org.springframework.cloud.appbroker.acceptance.fixtures.cf.CloudFoundryProperties.PROPERTY_PREFIX;
|
||||
|
||||
@ConfigurationProperties(PROPERTY_PREFIX)
|
||||
public class CloudFoundryProperties {
|
||||
|
||||
static final String PROPERTY_PREFIX = "spring.cloud.appbroker.deployer.cloudfoundry";
|
||||
static final String PROPERTY_PREFIX = "spring.cloud.appbroker.acceptancetest.cloudfoundry";
|
||||
|
||||
private String apiHost;
|
||||
private Integer apiPort;
|
||||
|
||||
@@ -18,194 +18,263 @@ package org.springframework.cloud.appbroker.acceptance.fixtures.cf;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.Map;
|
||||
|
||||
import org.cloudfoundry.client.CloudFoundryClient;
|
||||
import org.cloudfoundry.operations.CloudFoundryOperations;
|
||||
import org.cloudfoundry.operations.applications.ApplicationDetail;
|
||||
import org.cloudfoundry.operations.applications.ApplicationEnvironments;
|
||||
import org.cloudfoundry.operations.applications.ApplicationManifest;
|
||||
import org.cloudfoundry.operations.applications.ApplicationSummary;
|
||||
import org.cloudfoundry.operations.applications.DeleteApplicationRequest;
|
||||
import org.cloudfoundry.operations.applications.GetApplicationEnvironmentsRequest;
|
||||
import org.cloudfoundry.operations.applications.GetApplicationRequest;
|
||||
import org.cloudfoundry.operations.applications.PushApplicationRequest;
|
||||
import org.cloudfoundry.operations.applications.SetEnvironmentVariableApplicationRequest;
|
||||
import org.cloudfoundry.operations.applications.StartApplicationRequest;
|
||||
import org.cloudfoundry.operations.applications.PushApplicationManifestRequest;
|
||||
import org.cloudfoundry.operations.organizations.CreateOrganizationRequest;
|
||||
import org.cloudfoundry.operations.organizations.DefaultOrganizations;
|
||||
import org.cloudfoundry.operations.organizations.OrganizationSummary;
|
||||
import org.cloudfoundry.operations.serviceadmin.CreateServiceBrokerRequest;
|
||||
import org.cloudfoundry.operations.serviceadmin.DeleteServiceBrokerRequest;
|
||||
import org.cloudfoundry.operations.serviceadmin.EnableServiceAccessRequest;
|
||||
import org.cloudfoundry.operations.services.CreateServiceInstanceRequest;
|
||||
import org.cloudfoundry.operations.services.DeleteServiceInstanceRequest;
|
||||
import org.cloudfoundry.operations.spaces.CreateSpaceRequest;
|
||||
import org.cloudfoundry.operations.spaces.DefaultSpaces;
|
||||
import org.cloudfoundry.operations.spaces.SpaceSummary;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cloud.appbroker.acceptance.AcceptanceTestProperties;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
@Service
|
||||
public class CloudFoundryService {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(CloudFoundryService.class);
|
||||
|
||||
@Autowired
|
||||
private CloudFoundryOperations cloudFoundryOperations;
|
||||
private CloudFoundryProperties cloudFoundryProperties;
|
||||
private CloudFoundryClient cloudFoundryClient;
|
||||
private AcceptanceTestProperties acceptanceTestProperties;
|
||||
|
||||
@Autowired
|
||||
private CloudFoundryProperties cloudFoundryProperties;
|
||||
|
||||
public void enableServiceBrokerAccess(String serviceName) {
|
||||
cloudFoundryOperations
|
||||
.serviceAdmin()
|
||||
.enableServiceAccess(EnableServiceAccessRequest.builder().serviceName(serviceName).build())
|
||||
.block();
|
||||
public CloudFoundryService(CloudFoundryOperations cloudFoundryOperations, CloudFoundryProperties cloudFoundryProperties,
|
||||
CloudFoundryClient cloudFoundryClient, AcceptanceTestProperties acceptanceTestProperties) {
|
||||
this.cloudFoundryOperations = cloudFoundryOperations;
|
||||
this.cloudFoundryProperties = cloudFoundryProperties;
|
||||
this.cloudFoundryClient = cloudFoundryClient;
|
||||
this.acceptanceTestProperties = acceptanceTestProperties;
|
||||
}
|
||||
|
||||
public void createServiceBroker(String brokerName, String backingAppURL) {
|
||||
cloudFoundryOperations
|
||||
.serviceAdmin()
|
||||
.create(CreateServiceBrokerRequest.builder()
|
||||
.name(brokerName)
|
||||
.username("user")
|
||||
.password("password")
|
||||
.url(backingAppURL)
|
||||
.build())
|
||||
.block();
|
||||
}
|
||||
|
||||
public String getApplicationRoute(String appName) {
|
||||
return "https://" + cloudFoundryOperations
|
||||
.applications()
|
||||
.get(GetApplicationRequest.builder().name(appName).build())
|
||||
.block().getUrls().get(0);
|
||||
}
|
||||
|
||||
public void startApplication(String appName) {
|
||||
cloudFoundryOperations
|
||||
.applications()
|
||||
.start(StartApplicationRequest.builder().name(appName).build())
|
||||
.block();
|
||||
}
|
||||
|
||||
public void pushAppNoStart(String appName, Path appPath) {
|
||||
cloudFoundryOperations
|
||||
.applications()
|
||||
.push(PushApplicationRequest
|
||||
.builder()
|
||||
.noStart(true)
|
||||
.path(appPath)
|
||||
.name(appName)
|
||||
.build())
|
||||
.block();
|
||||
}
|
||||
|
||||
public void deleteBackingApp(String appName) {
|
||||
try {
|
||||
cloudFoundryOperations
|
||||
.applications()
|
||||
.delete(DeleteApplicationRequest.builder().name(appName).build())
|
||||
.block();
|
||||
} catch (Exception e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteServiceBroker(String brokerName) {
|
||||
try {
|
||||
public Mono<Void> enableServiceBrokerAccess(String serviceName) {
|
||||
return loggingMono(
|
||||
cloudFoundryOperations
|
||||
.serviceAdmin()
|
||||
.delete(DeleteServiceBrokerRequest.builder().name(brokerName).build())
|
||||
.block();
|
||||
} catch (Exception e) {
|
||||
// Ignore
|
||||
}
|
||||
.enableServiceAccess(EnableServiceAccessRequest.builder().serviceName(serviceName).build()));
|
||||
}
|
||||
|
||||
public void deleteServiceInstance(String serviceInstanceName) {
|
||||
try {
|
||||
public Mono<Void> createServiceBroker(String brokerName, String sampleBrokerAppName) {
|
||||
return loggingMono(
|
||||
getApplicationRoute(sampleBrokerAppName)
|
||||
.flatMap(url -> cloudFoundryOperations
|
||||
.serviceAdmin()
|
||||
.create(CreateServiceBrokerRequest
|
||||
.builder()
|
||||
.name(brokerName)
|
||||
.username("user")
|
||||
.password("password")
|
||||
.url(url)
|
||||
.build())));
|
||||
}
|
||||
|
||||
public Mono<String> getApplicationRoute(String appName) {
|
||||
return loggingMono(
|
||||
cloudFoundryOperations
|
||||
.applications()
|
||||
.get(GetApplicationRequest.builder().name(appName).build())
|
||||
.map(ApplicationDetail::getUrls)
|
||||
.flatMapMany(Flux::fromIterable)
|
||||
.next()
|
||||
.map(url -> "https://" + url));
|
||||
}
|
||||
|
||||
public Mono<Void> pushAppBroker(String appName, Path appPath, String[] backingAppProperties) {
|
||||
return loggingMono(
|
||||
cloudFoundryOperations
|
||||
.applications()
|
||||
.pushManifest(PushApplicationManifestRequest
|
||||
.builder()
|
||||
.manifest(ApplicationManifest
|
||||
.builder()
|
||||
.putAllEnvironmentVariables(appBrokerDeployerEnvironmentVariables())
|
||||
.putAllEnvironmentVariables(appBrokerCatalogEnvironmentVariables())
|
||||
.putAllEnvironmentVariables(backingAppEnvironmentVariables(backingAppProperties))
|
||||
.name(appName)
|
||||
.path(appPath)
|
||||
.memory(1024)
|
||||
.build())
|
||||
.build()));
|
||||
}
|
||||
|
||||
public Mono<Void> deleteBackingApp(String appName) {
|
||||
return loggingMono(
|
||||
cloudFoundryOperations
|
||||
.applications()
|
||||
.delete(DeleteApplicationRequest.builder().name(appName).build()));
|
||||
}
|
||||
|
||||
public Mono<Void> deleteServiceBroker(String brokerName) {
|
||||
return loggingMono(
|
||||
cloudFoundryOperations
|
||||
.serviceAdmin()
|
||||
.delete(DeleteServiceBrokerRequest.builder().name(brokerName).build()));
|
||||
}
|
||||
|
||||
public Mono<Void> deleteServiceInstance(String serviceInstanceName) {
|
||||
return loggingMono(
|
||||
cloudFoundryOperations
|
||||
.services()
|
||||
.deleteInstance(DeleteServiceInstanceRequest.builder().name(serviceInstanceName).build())
|
||||
.block();
|
||||
} catch (Exception e) {
|
||||
// Ignore
|
||||
.listInstances()
|
||||
.filter(si -> si.getName().equals(serviceInstanceName))
|
||||
.next()
|
||||
.flatMap(si ->
|
||||
cloudFoundryOperations
|
||||
.services()
|
||||
.deleteInstance(DeleteServiceInstanceRequest.builder().name(si.getName()).build())));
|
||||
}
|
||||
|
||||
public Mono<Void> createServiceInstance(String planName, String serviceName, String serviceInstanceName) {
|
||||
return loggingMono(
|
||||
cloudFoundryOperations
|
||||
.services()
|
||||
.createInstance(CreateServiceInstanceRequest
|
||||
.builder()
|
||||
.planName(planName)
|
||||
.serviceName(serviceName)
|
||||
.serviceInstanceName(serviceInstanceName)
|
||||
.build()));
|
||||
}
|
||||
|
||||
public Mono<List<ApplicationSummary>> getApplications() {
|
||||
return loggingMono(
|
||||
cloudFoundryOperations.applications().list().collectList());
|
||||
}
|
||||
|
||||
public Mono<ApplicationEnvironments> getApplicationEnvironmentByAppName(String appName) {
|
||||
return loggingMono(
|
||||
cloudFoundryOperations
|
||||
.applications()
|
||||
.getEnvironments(GetApplicationEnvironmentsRequest.builder().name(appName).build()));
|
||||
}
|
||||
|
||||
|
||||
public Mono<SpaceSummary> getOrCreateDefaultSpace() {
|
||||
final String defaultOrg = cloudFoundryProperties.getDefaultOrg();
|
||||
|
||||
DefaultSpaces spaceOperations = new DefaultSpaces(
|
||||
Mono.just(cloudFoundryClient),
|
||||
getOrCreateDefaultOrganization().map(OrganizationSummary::getId),
|
||||
Mono.just(cloudFoundryProperties.getUsername()));
|
||||
|
||||
final String defaultSpace = cloudFoundryProperties.getDefaultSpace();
|
||||
return loggingMono(
|
||||
getDefaultSpace(spaceOperations)
|
||||
.switchIfEmpty(spaceOperations
|
||||
.create(CreateSpaceRequest
|
||||
.builder()
|
||||
.name(defaultSpace)
|
||||
.organization(defaultOrg)
|
||||
.build())
|
||||
.then(getDefaultSpace(spaceOperations))));
|
||||
}
|
||||
|
||||
public Mono<OrganizationSummary> getOrCreateDefaultOrganization() {
|
||||
DefaultOrganizations organizationOperations = new DefaultOrganizations(
|
||||
Mono.just(cloudFoundryClient),
|
||||
Mono.just(cloudFoundryProperties.getUsername()));
|
||||
|
||||
final String defaultOrg = cloudFoundryProperties.getDefaultOrg();
|
||||
return loggingMono(
|
||||
getDefaultOrg(organizationOperations)
|
||||
.switchIfEmpty(organizationOperations
|
||||
.create(CreateOrganizationRequest
|
||||
.builder()
|
||||
.organizationName(defaultOrg)
|
||||
.build())
|
||||
.then(getDefaultOrg(organizationOperations))));
|
||||
}
|
||||
|
||||
private Mono<OrganizationSummary> getDefaultOrg(DefaultOrganizations orgOperations) {
|
||||
return orgOperations
|
||||
.list()
|
||||
.filter(r -> r
|
||||
.getName()
|
||||
.equals(cloudFoundryProperties.getDefaultOrg()))
|
||||
.next();
|
||||
}
|
||||
|
||||
private Mono<SpaceSummary> getDefaultSpace(DefaultSpaces spaceOperations) {
|
||||
return spaceOperations
|
||||
.list()
|
||||
.filter(r -> r
|
||||
.getName()
|
||||
.equals(cloudFoundryProperties.getDefaultSpace()))
|
||||
.next();
|
||||
}
|
||||
|
||||
private Map<String, String> appBrokerDeployerEnvironmentVariables() {
|
||||
|
||||
Map<String, String> deployerVariables = new HashMap<>();
|
||||
deployerVariables.put("spring.cloud.appbroker.deployer.cloudfoundry.api-host", cloudFoundryProperties.getApiHost());
|
||||
deployerVariables.put("spring.cloud.appbroker.deployer.cloudfoundry.api-port", String.valueOf(cloudFoundryProperties.getApiPort()));
|
||||
deployerVariables.put("spring.cloud.appbroker.deployer.cloudfoundry.username", cloudFoundryProperties.getUsername());
|
||||
deployerVariables.put("spring.cloud.appbroker.deployer.cloudfoundry.password", cloudFoundryProperties.getPassword());
|
||||
deployerVariables.put("spring.cloud.appbroker.deployer.cloudfoundry.default-org", cloudFoundryProperties.getDefaultOrg());
|
||||
deployerVariables.put("spring.cloud.appbroker.deployer.cloudfoundry.default-space", cloudFoundryProperties.getDefaultSpace());
|
||||
deployerVariables.put("spring.cloud.appbroker.deployer.cloudfoundry.skip-ssl-validation", String.valueOf(cloudFoundryProperties.isSkipSslValidation()));
|
||||
deployerVariables.put("spring.cloud.appbroker.apps[0].cloudfoundry.skip-ssl-validation", String.valueOf(cloudFoundryProperties.isSkipSslValidation()));
|
||||
return deployerVariables;
|
||||
}
|
||||
|
||||
private Map<String, String> appBrokerCatalogEnvironmentVariables() {
|
||||
Map<String, String> catalogVariables = new HashMap<>();
|
||||
catalogVariables.put("spring.cloud.openservicebroker.catalog.services[0].id", "example-service");
|
||||
catalogVariables.put("spring.cloud.openservicebroker.catalog.services[0].name", "example");
|
||||
catalogVariables.put("spring.cloud.openservicebroker.catalog.services[0].description", "A simple example");
|
||||
catalogVariables.put("spring.cloud.openservicebroker.catalog.services[0].bindable", "true");
|
||||
catalogVariables.put("spring.cloud.openservicebroker.catalog.services[0].tags[0]", "example");
|
||||
catalogVariables.put("spring.cloud.openservicebroker.catalog.services[0].plans[0].id", "simple-plan");
|
||||
catalogVariables.put("spring.cloud.openservicebroker.catalog.services[0].plans[0].bindable", "true");
|
||||
catalogVariables.put("spring.cloud.openservicebroker.catalog.services[0].plans[0].name", "standard");
|
||||
catalogVariables.put("spring.cloud.openservicebroker.catalog.services[0].plans[0].description", "A simple plan");
|
||||
catalogVariables.put("spring.cloud.openservicebroker.catalog.services[0].plans[0].free", "true");
|
||||
return catalogVariables;
|
||||
}
|
||||
|
||||
private Map<String, String> backingAppEnvironmentVariables(String[] backingAppProperties) {
|
||||
Map<String, String> backingAppVariables = new HashMap<>();
|
||||
for (String appProperty : backingAppProperties) {
|
||||
final String[] appPropertyKeyValue = appProperty.split("=");
|
||||
if (appPropertyKeyValue.length != 2) {
|
||||
throw new RuntimeException(format("Backing app property '%s' is incorrectly formatted", Arrays.toString(appPropertyKeyValue)));
|
||||
}
|
||||
else backingAppVariables.put(appPropertyKeyValue[0], appPropertyKeyValue[1]);
|
||||
}
|
||||
return backingAppVariables;
|
||||
}
|
||||
|
||||
public void createServiceInstance(String planName, String serviceName, String serviceInstanceName) {
|
||||
cloudFoundryOperations
|
||||
.services()
|
||||
.createInstance(CreateServiceInstanceRequest
|
||||
.builder()
|
||||
.planName(planName)
|
||||
.serviceName(serviceName)
|
||||
.serviceInstanceName(serviceInstanceName)
|
||||
.build())
|
||||
.block();
|
||||
}
|
||||
|
||||
private static SetEnvironmentVariableApplicationRequest createEnvRequest(String appName, String key, String value) {
|
||||
return SetEnvironmentVariableApplicationRequest
|
||||
.builder()
|
||||
.name(appName)
|
||||
.variableName(key)
|
||||
.variableValue(value)
|
||||
.build();
|
||||
}
|
||||
|
||||
public List<ApplicationSummary> getApplications() {
|
||||
return cloudFoundryOperations.applications().list().collectList().block();
|
||||
}
|
||||
|
||||
public ApplicationEnvironments getApplicationEnvironmentByAppName(String appName) {
|
||||
return cloudFoundryOperations
|
||||
.applications()
|
||||
.getEnvironments(GetApplicationEnvironmentsRequest.builder().name(appName).build())
|
||||
.block();
|
||||
}
|
||||
|
||||
public void setBrokerAppEnvironment(String[] properties) {
|
||||
Flux<Void> catalogPublishers = getCatalogPublishers();
|
||||
Flux<Void> appBrokerCFPublishers = getAppBrokerCFPublishers();
|
||||
Flux<Void> appBrokerApplicationPublishers = Flux.concat(Arrays.stream(properties)
|
||||
.filter(property -> property.contains("="))
|
||||
.map(property -> property.split("="))
|
||||
.map(property -> setEnvRequest(property[0], property[1]))
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
Flux.concat(catalogPublishers, appBrokerCFPublishers, appBrokerApplicationPublishers).blockLast();
|
||||
}
|
||||
|
||||
private Flux<Void> getAppBrokerCFPublishers() {
|
||||
return Flux.concat(
|
||||
setEnvRequest("spring.cloud.appbroker.deployer.cloudfoundry.api-host", cloudFoundryProperties.getApiHost()),
|
||||
setEnvRequest("spring.cloud.appbroker.deployer.cloudfoundry.api-port", String.valueOf(cloudFoundryProperties.getApiPort())),
|
||||
setEnvRequest("spring.cloud.appbroker.deployer.cloudfoundry.username", cloudFoundryProperties.getUsername()),
|
||||
setEnvRequest("spring.cloud.appbroker.deployer.cloudfoundry.password", cloudFoundryProperties.getPassword()),
|
||||
setEnvRequest("spring.cloud.appbroker.deployer.cloudfoundry.default-org", cloudFoundryProperties.getDefaultOrg()),
|
||||
setEnvRequest("spring.cloud.appbroker.deployer.cloudfoundry.default-space", cloudFoundryProperties.getDefaultSpace()),
|
||||
setEnvRequest("spring.cloud.appbroker.deployer.cloudfoundry.skip-ssl-validation", String.valueOf(cloudFoundryProperties.isSkipSslValidation()))
|
||||
);
|
||||
}
|
||||
|
||||
private Flux<Void> getCatalogPublishers() {
|
||||
return Flux.concat(
|
||||
setEnvRequest("spring.cloud.openservicebroker.catalog.services[0].id", "example-service"),
|
||||
setEnvRequest("spring.cloud.openservicebroker.catalog.services[0].name", "example"),
|
||||
setEnvRequest("spring.cloud.openservicebroker.catalog.services[0].description", "A simple example"),
|
||||
setEnvRequest("spring.cloud.openservicebroker.catalog.services[0].bindable", "true"),
|
||||
setEnvRequest("spring.cloud.openservicebroker.catalog.services[0].tags[0]", "example"),
|
||||
setEnvRequest("spring.cloud.openservicebroker.catalog.services[0].plans[0].id", "simple-plan"),
|
||||
setEnvRequest("spring.cloud.openservicebroker.catalog.services[0].plans[0].bindable", "true"),
|
||||
setEnvRequest("spring.cloud.openservicebroker.catalog.services[0].plans[0].name", "standard"),
|
||||
setEnvRequest("spring.cloud.openservicebroker.catalog.services[0].plans[0].description", "A simple plan"),
|
||||
setEnvRequest("spring.cloud.openservicebroker.catalog.services[0].plans[0].free", "true")
|
||||
);
|
||||
}
|
||||
|
||||
private Mono<Void> setEnvRequest(String key, String value) {
|
||||
return cloudFoundryOperations
|
||||
.applications()
|
||||
.setEnvironmentVariable(createEnvRequest("sample-broker", key, value))
|
||||
.doOnSuccess(v -> LOGGER.info("Environment with key {} set", key));
|
||||
private <T> Mono<T> loggingMono(Mono<T> publisher) {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
return publisher.log();
|
||||
}
|
||||
else return publisher;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
logging:
|
||||
level:
|
||||
cloudfoundry-client: DEBUG
|
||||
cloudfoundry-client: ERROR
|
||||
org.springframework.cloud.appbroker: DEBUG
|
||||
Reference in New Issue
Block a user