---
# System prepended metadata

title: Deploy avail-api-app service

---

# Deploy avail-api-app service
[bamboo@localhost solar-travel-availability]$ gradle :avail-api:deployContainer -PimageVersion=0.0.1-DEMO -PocProjectName=fet-nfv-poc
~~~
> Configure project :avail-api
Working directory: /home/bamboo/projects/solar-travel-availability

> Task :avail-api:createNewProject
Executing command: oc new-project fet-nfv-poc
Executing command: oc project fet-nfv-poc
Already on project "fet-nfv-poc" on server "https://c100-e.eu-de.containers.cloud.ibm.com:32553".


> Task :avail-api:createImageSecret
Skip creating Image Secret. Repository is public on quay.io, private is not free.

> Task :avail-api:deleteApp
Executing command: oc delete all -l app=avail-api-app
No resources found


> Task :avail-api:installRedisDatabase
Executing command: oc new-app --docker-image redis --name redis-solar-travel-availability
Executing command: oc new-app --docker-image redis --name redis-solar-travel-pubsub

> Task :avail-api:createNewApp
Executing command: oc project fet-nfv-poc
Already on project "fet-nfv-poc" on server "https://c100-e.eu-de.containers.cloud.ibm.com:32553".

Executing command: oc new-app --docker-image quay.io/solar-travel/avail-api-app:0.0.1-DEMO -e DEVELOPMENT_ENV=true -e REDIS_HOST=redis-solar-travel-availability -e IBM_S3_LOCATION=null -e IBM_S3_API_KEY=null -e IBM_S3_IAM_SERVICE_ID=null -e IBM_S3_PUBLIC_ENDPOINT=null -e RES_DATA_BUCKET_NAME=null -e RES_DATA_OBJECT_KEY=null -e REDIS_CONNECTION_STRING=redis://redis-solar-travel-availability -e REDIS_PUBSUB_CONNECTION_STRING=redis://redis-solar-travel-pubsub -e REDIS_API_KEY=null -e REDIS_PASSWORD='' -e REDIS_DEPLOYMENT_ID=''
--> Found Docker image e426479 (9 days old) from quay.io for "quay.io/solar-travel/avail-api-app:0.0.1-DEMO"

    * An image stream tag will be created as "avail-api-app:0.0.1-DEMO" that will track this image
    * This image will be deployed in deployment config "avail-api-app"
    * Ports 9080/tcp, 9443/tcp will be load balanced by service "avail-api-app"
      * Other containers can access this service through the hostname "avail-api-app"

--> Creating resources ...
    imagestream.image.openshift.io "avail-api-app" created
    deploymentconfig.apps.openshift.io "avail-api-app" created
    service "avail-api-app" created
--> Success
    Application is not exposed. You can expose services to the outside world by executing one or more of the commands below:
     'oc expose svc/avail-api-app'
    Run 'oc status' to view your app.


> Task :avail-api:checkPodStatus
Executing command: oc get dc avail-api-app | grep avail-api-app | cut -d' ' -f1,4,14,23
avail-api-app 1 1 0

Retry pods status checking: 0
Executing command: oc get dc avail-api-app | grep avail-api-app | cut -d' ' -f1,4,14,23
avail-api-app 1 1 1

Executing command: oc get dc avail-api-app | grep avail-api-app | cut -d' ' -f1,4,14,23
avail-api-app 1 1 1


> Task :avail-api:exposeService
Executing command: oc expose service avail-api-app
route.route.openshift.io/avail-api-app exposed

Executing command: oc patch route/avail-api-app --patch '{"spec":{"tls":{"termination":"edge"}}}'
route.route.openshift.io/avail-api-app patched


> Task :avail-api:testDeployedService
Executing command: oc get route | grep avail-api-app | grep -P 'avail-api-app-fet-nfv-poc.*\.(com|cloud)' -o
avail-api-app-fet-nfv-poc.fetnet-1579251008816-f72ef11f3ab089a8c677044eb28292cd-0001.eu-de.containers.appdomain.cloud

Executing command: curl -sX GET https://avail-api-app-fet-nfv-poc.fetnet-1579251008816-f72ef11f3ab089a8c677044eb28292cd-0001.eu-de.containers.appdomain.cloud/hello

Executing command: curl -sX GET https://avail-api-app-fet-nfv-poc.fetnet-1579251008816-f72ef11f3ab089a8c677044eb28292cd-0001.eu-de.containers.appdomain.cloud/hello
Hello, world

Test success
----------------------------------------------------------------------------------------

> Task :avail-api:deployContainer
The container has been successfully deployed.
~~~


# Deploy avail-mgnt-app service
[bamboo@localhost solar-travel-availability]$ gradle :avail-mgnt:deployContainer -PimageVersion=0.0.1-DEMO -PocProjectName=fet-nfv-poc
~~~
> Configure project :avail-api
Working directory: /home/bamboo/projects/solar-travel-availability

> Task :avail-mgnt:createNewProject
Executing command: oc new-project fet-nfv-poc

> Task :avail-mgnt:createImageSecret
Skip creating Image Secret. Repository is public on quay.io, private is not free.

> Task :avail-mgnt:deleteApp
Executing command: oc delete all -l app=avail-mgnt-app
No resources found


> Task :avail-mgnt:installRedisDatabase
Executing command: oc new-app --docker-image redis --name redis-solar-travel-availability
Executing command: oc new-app --docker-image redis --name redis-solar-travel-pubsub

> Task :avail-mgnt:createNewApp
Executing command: oc project fet-nfv-poc
Already on project "fet-nfv-poc" on server "https://c100-e.eu-de.containers.cloud.ibm.com:32553".

Executing command: oc new-app --docker-image quay.io/solar-travel/avail-mgnt-app:0.0.1-DEMO -e DEVELOPMENT_ENV=true -e REDIS_HOST=redis-solar-travel-availability -e IBM_S3_LOCATION=null -e IBM_S3_API_KEY=null -e IBM_S3_IAM_SERVICE_ID=null -e IBM_S3_PUBLIC_ENDPOINT=null -e RES_DATA_BUCKET_NAME=null -e RES_DATA_OBJECT_KEY=null -e REDIS_CONNECTION_STRING=redis://redis-solar-travel-availability -e REDIS_PUBSUB_CONNECTION_STRING=redis://redis-solar-travel-pubsub -e REDIS_API_KEY=null -e REDIS_PASSWORD='' -e REDIS_DEPLOYMENT_ID=''
--> Found Docker image fde1105 (8 days old) from quay.io for "quay.io/solar-travel/avail-mgnt-app:0.0.1-DEMO"

    * An image stream tag will be created as "avail-mgnt-app:0.0.1-DEMO" that will track this image
    * This image will be deployed in deployment config "avail-mgnt-app"
    * Ports 9080/tcp, 9443/tcp will be load balanced by service "avail-mgnt-app"
      * Other containers can access this service through the hostname "avail-mgnt-app"

--> Creating resources ...
    imagestream.image.openshift.io "avail-mgnt-app" created
    deploymentconfig.apps.openshift.io "avail-mgnt-app" created
    service "avail-mgnt-app" created
--> Success
    Application is not exposed. You can expose services to the outside world by executing one or more of the commands below:
     'oc expose svc/avail-mgnt-app'
    Run 'oc status' to view your app.


> Task :avail-mgnt:checkPodStatus
Executing command: oc get dc avail-mgnt-app | grep avail-mgnt-app | cut -d' ' -f1,4,14,23
avail-mgnt-app 1 1 0

Retry pods status checking: 0
Executing command: oc get dc avail-mgnt-app | grep avail-mgnt-app | cut -d' ' -f1,4,14,23
avail-mgnt-app 1 1 1

Executing command: oc get dc avail-mgnt-app | grep avail-mgnt-app | cut -d' ' -f1,4,14,23
avail-mgnt-app 1 1 1


> Task :avail-mgnt:exposeService
Executing command: oc expose service avail-mgnt-app
route.route.openshift.io/avail-mgnt-app exposed

Executing command: oc patch route/avail-mgnt-app --patch '{"spec":{"tls":{"termination":"edge"}}}'
route.route.openshift.io/avail-mgnt-app patched


> Task :avail-mgnt:testDeployedService
Executing command: oc get route | grep avail-mgnt-app | grep -P 'avail-mgnt-app-fet-nfv-poc.*\.(com|cloud)' -o
avail-mgnt-app-fet-nfv-poc.fetnet-1579251008816-f72ef11f3ab089a8c677044eb28292cd-0001.eu-de.containers.appdomain.cloud

Executing command: curl -sX GET https://avail-mgnt-app-fet-nfv-poc.fetnet-1579251008816-f72ef11f3ab089a8c677044eb28292cd-0001.eu-de.containers.appdomain.cloud/hello
Hello, world

Test success
----------------------------------------------------------------------------------------

> Task :avail-mgnt:deployContainer
The container has been successfully deployed.

BUILD SUCCESSFUL in 1m 1s
9 actionable tasks: 9 executed
~~~


# Running CLI command to reload data into in-memory database
[bamboo@localhost demo-scripts]$ ./reload.sh
~~~
1d5ac0d8-0cbc-4c06-bca0-c498348d497e
~~~
[bamboo@localhost demo-scripts]$ ./wait.sh 1d5ac0d8-0cbc-4c06-bca0-c498348d497e
~~~
wait response:
id=1d5ac0d8-0cbc-4c06-bca0-c498348d497e
receivedTime=2020-02-17T04:19:42.597+0000
status=COMPLETE
~~~

# Calling api /trains/{trainDate}/{trainTime}/{origin}/{dest}/ai
[bamboo@localhost demo-scripts]$ curl -sX GET 'https://avail-api-app-fet-nfv-poc.fetnet-1579251008816-f72ef11f3ab089a8c677044eb28292cd-0001.eu-de.containers.appdomain.cloud/trains/20190131/2200/SUN/EAR/ai'
~~~
[{
  "destinationStation": {
    "locationCode": "EAR"
  },
  "originStation": {
    "locationCode": "SUN"
  },
  "trainDate": "20190131",
  "trainTime": "2205",
  "train": {
    "trainNumber": "0295"
  },
  "classCodes": [{
    "code": "Y",
    "quantity": 0
  }, {
    "code": "YF",
    "quantity": 0
  }, {
    "code": "YV",
    "quantity": 0
  }, {
    "code": "YW;",
    "quantity": 0
  }]
}, {
  "destinationStation": {
    "locationCode": "EAR"
  },
  "originStation": {
    "locationCode": "SUN"
  },
  "trainDate": "20190131",
  "trainTime": "2215",
  "train": {
    "trainNumber": "0565"
  },
  "classCodes": [{
    "code": "Y",
    "quantity": 0
  }, {
    "code": "YF",
    "quantity": 0
  }, {
    "code": "YV",
    "quantity": 0
  }, {
    "code": "YW;",
    "quantity": 0
  }]
}, {
  "destinationStation": {
    "locationCode": "EAR"
  },
  "originStation": {
    "locationCode": "SUN"
  },
  "trainDate": "20190131",
  "trainTime": "2250",
  "train": {
    "trainNumber": "0567"
  },
  "classCodes": [{
    "code": "Y",
    "quantity": 0
  }, {
    "code": "YF",
    "quantity": 0
  }, {
    "code": "YV",
    "quantity": 0
  }, {
    "code": "YW;",
    "quantity": 0
  }]
}]
~~~
# Deploy api-gateway-app
[bamboo@localhost solar-travel-integration]$ gradle :api-gateway:deployContainer -PimageVersion=0.0.4-DEMO -PocProjectName=fet-nfv-poc
~~~
> Task :api-gateway:createNewProject
Executing command: oc new-project fet-nfv-poc

> Task :api-gateway:createImageSecret
Skip creating Image Secret. Repository is public on quay.io, private is not free.

> Task :api-gateway:deleteApp
Executing command: oc delete all -l app=api-gateway-app
pod "api-gateway-app-67bf5787f-kjh9z" deleted
service "api-gateway-app" deleted
replicaset.apps "api-gateway-app-67bf5787f" deleted
route.route.openshift.io "api-gateway-app" deleted

Executing command: oc delete serviceaccounts api-gateway-app
serviceaccount "api-gateway-app" deleted

Executing command: oc delete deployment api-gateway-app
deployment.extensions "api-gateway-app" deleted

Executing command: oc delete clusterrolebindings api-gateway-cluster-reader
clusterrolebinding.authorization.openshift.io "api-gateway-cluster-reader" deleted


> Task :api-gateway:createNewApp
Executing command: cat api-gateway/create-api-gateway-template.yaml|sed "s/{{ ocProjectName }}/fet-nfv-poc/g;s/{{ imageVersion }}/0.0.4-DEMO/g" > api-gateway/create-api-gateway.yaml

Executing command: oc create -f api-gateway/create-api-gateway.yaml
serviceaccount/api-gateway-app created
clusterrolebinding.authorization.openshift.io/api-gateway-cluster-reader created
route.route.openshift.io/api-gateway-app created
service/api-gateway-app created
deployment.apps/api-gateway-app created


> Task :api-gateway:checkPodStatus
Executing command: oc get deployment api-gateway-app | grep api-gateway-app | cut -d' ' -f1,4,14,23
api-gateway-app 1


> Task :api-gateway:exposeService
Skip exposing service, already did in the deployment yaml file

> Task :api-gateway:testDeployedService
Executing command: oc get route | grep api-gateway-app | grep -P 'api-gateway-app-fet-nfv-poc.*\.(com|cloud)' -o
api-gateway-app-fet-nfv-poc.fetnet-1579251008816-f72ef11f3ab089a8c677044eb28292cd-0001.eu-de.containers.appdomain.cloud

Executing command: (masked): oc serviceaccounts get-token api-gateway-app
******
Executing command: (masked): curl -H "Authorization: bearer <token>" -sX Get https://api-gateway-app-fet-nfv-poc.fetnet-1579251008816-f72ef11f3ab089a8c677044eb28292cd-0001.eu-de.containers.appdomain.cloud/hello
******
Executing command: (masked): curl -H "Authorization: bearer <token>" -sX Get https://api-gateway-app-fet-nfv-poc.fetnet-1579251008816-f72ef11f3ab089a8c677044eb28292cd-0001.eu-de.containers.appdomain.cloud/hello
******
Test success
----------------------------------------------------------------------------------------

> Task :api-gateway:deployContainer
The container has been successfully deployed.

BUILD SUCCESSFUL in 55s
8 actionable tasks: 8 executed
~~~

# Access API gateway url /hello via a web browser
https://api-gateway-app-fet-nfv-poc.fetnet-1579251008816-f72ef11f3ab089a8c677044eb28292cd-0001.eu-de.containers.appdomain.cloud/hello

# Access API gateway url /management/trains/reload using Authorization Header
## Getting the token
[bamboo@localhost solar-travel-integration]$ TOKEN=$(oc serviceaccounts get-token api-gateway-app)
## Use the token
[bamboo@localhost solar-travel-integration]$ curl -H "Authorization: bearer $TOKEN" -sX Get https://api-gateway-app-fet-nfv-poc.fetnet-1579251008816-f72ef11f3ab089a8c677044eb28292cd-0001.eu-de.containers.appdomain.cloud/management/trains/reload
~~~
{"id":"76afc0bf-1308-41af-9489-39b12cd3a1fe","receivedTime":"2020-02-17T04:42:41.049+0000","status":"ACCEPTED"}
~~~

# Lookup Train schedule via API gateway
[bamboo@localhost solar-travel-integration]$ curl -H "Authorization: bearer $TOKEN" -sX Get 'https://api-gateway-app-fet-nfv-poc.fetnet-1579251008816-f72ef11f3ab089a8c677044eb28292cd-0001.eu-de.containers.appdomain.cloud/customers/trains?trainDate=20190131&trainTime=2200&origin=EAR&dest=SUN'
~~~
[{"destinationStation":{"locationCode":"SUN"},"originStation":{"locationCode":"EAR"},"trainDate":"20190131","trainTime":"2202","train":{"trainNumber":"0684"},"classCodes":[{"code":"Y","quantity":0},{"code":"YF","quantity":0},{"code":"YV","quantity":0},{"code":"YW;","quantity":0}]},{"destinationStation":{"locationCode":"SUN"},"originStation":{"locationCode":"EAR"},"trainDate":"20190131","trainTime":"2232","train":{"trainNumber":"0160"},"classCodes":[{"code":"Y","quantity":0},{"code":"YF","quantity":0},{"code":"YV","quantity":0},{"code":"YW;","quantity":0}]},{"destinationStation":{"locationCode":"SUN"},"originStation":{"locationCode":"EAR"},"trainDate":"20190131","trainTime":"2242","train":{"trainNumber":"0858"},"classCodes":[{"code":"YA","quantity":0},{"code":"Y","quantity":0},{"code":"YB","quantity":0},{"code":"YF","quantity":0},{"code":"YV","quantity":0},{"code":"YW;","quantity":0}]},{"destinationStation":{"locationCode":"SUN"},"originStation":{"locationCode":"EAR"},"trainDate":"20190131","trainTime":"2307","train":{"trainNumber":"0690"},"classCodes":[{"code":"Y","quantity":0},{"code":"YF","quantity":0},{"code":"YV","quantity":0},{"code":"YW;","quantity":0}]},{"destinationStation":{"locationCode":"SUN"},"originStation":{"locationCode":"EAR"},"trainDate":"20190131","trainTime":"2347","train":{"trainNumber":"0862"},"classCodes":[{"code":"YA","quantity":0},{"code":"Y","quantity":0},{"code":"YB","quantity":0},{"code":"YF","quantity":0},{"code":"YV","quantity":0},{"code":"YW;","quantity":0}]},{"destinationStation":{"locationCode":"SUN"},"originStation":{"locationCode":"EAR"},"trainDate":"20190131","trainTime":"2352","train":{"trainNumber":"0294"},"classCodes":[{"code":"YA","quantity":0},{"code":"Y","quantity":0},{"code":"YB","quantity":0},{"code":"YF","quantity":0},{"code":"YV","quantity":0},{"code":"YW;","quantity":0}]}]
~~~

# oauth-proxy sidecar deployment
[bamboo@localhost solar-travel-integration]$ oc get deployments api-gateway-app -o yaml
~~~
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  annotations:
    deployment.kubernetes.io/revision: "1"
  creationTimestamp: 2020-02-17T04:32:41Z
  generation: 1
  name: api-gateway-app
  namespace: fet-nfv-poc
  resourceVersion: "10787232"
  selfLink: /apis/extensions/v1beta1/namespaces/fet-nfv-poc/deployments/api-gateway-app
  uid: 890ea3a3-513e-11ea-8fd4-8a83bb8e45db
spec:
  progressDeadlineSeconds: 600
  replicas: 1
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      app: api-gateway-app
  strategy:
    rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 25%
    type: RollingUpdate
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: api-gateway-app
    spec:
      containers:
      - args:
        - --https-address=:8443
        - --provider=openshift
        - --openshift-service-account=api-gateway-app
        - --upstream=http://localhost:9080
        - --tls-cert=/etc/tls/private/tls.crt
        - --tls-key=/etc/tls/private/tls.key
        - --cookie-secret=SECRET
        - --openshift-delegate-urls={"/":{"resource":"services","verb":"get","project":"fet-nfv-poc","name":"api-gateway-app"}}
        image: openshift/oauth-proxy:latest
        imagePullPolicy: IfNotPresent
        name: oauth-proxy
        ports:
        - containerPort: 8443
          name: public
          protocol: TCP
        resources: {}
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
        volumeMounts:
        - mountPath: /etc/tls/private
          name: api-gateway-proxy-tls
      - env:
        - name: AVAILABILITY_SERVICE_NAME
          value: avail-api-app:9080
        - name: AVAILABILITY_MGNT_SERVICE_NAME
          value: avail-mgnt-app:9080
        - name: DEVELOPMENT_ENV
          value: "true"
        - name: IBM_S3_API_KEY
          value: "null"
        - name: IBM_S3_IAM_SERVICE_ID
          value: "null"
        - name: IBM_S3_LOCATION
          value: "null"
        - name: IBM_S3_PUBLIC_ENDPOINT
          value: "null"
        - name: RES_DATA_BUCKET_NAME
          value: "null"
        - name: RES_DATA_OBJECT_KEY
          value: "null"
        image: quay.io/solar-travel/api-gateway-app:0.0.4-DEMO
        imagePullPolicy: Always
        name: api-gateway-app-upstream
        resources: {}
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
      dnsPolicy: ClusterFirst
      restartPolicy: Always
      schedulerName: default-scheduler
      securityContext: {}
      serviceAccount: api-gateway-app
      serviceAccountName: api-gateway-app
      terminationGracePeriodSeconds: 30
      volumes:
      - name: api-gateway-proxy-tls
        secret:
          defaultMode: 420
          secretName: api-gateway-proxy-tls
status:
  availableReplicas: 1
  conditions:
  - lastTransitionTime: 2020-02-17T04:32:46Z
    lastUpdateTime: 2020-02-17T04:32:46Z
    message: Deployment has minimum availability.
    reason: MinimumReplicasAvailable
    status: "True"
    type: Available
  - lastTransitionTime: 2020-02-17T04:32:41Z
    lastUpdateTime: 2020-02-17T04:32:46Z
    message: ReplicaSet "api-gateway-app-69fdbcf798" has successfully progressed.
    reason: NewReplicaSetAvailable
    status: "True"
    type: Progressing
  observedGeneration: 1
  readyReplicas: 1
  replicas: 1
  updatedReplicas: 1
~~~
# Integration Testing with Gradle docker-compose-plugin
[bamboo@localhost solar-travel-integration]$ gradle api-gateway:tasks --all
~~~
> Task :api-gateway:tasks

------------------------------------------------------------
Tasks runnable from project :api-gateway
------------------------------------------------------------

Application tasks
-----------------
bootRun - Runs this project as a Spring Boot application.

Build tasks
-----------
assemble - Assembles the outputs of this project.
bootJar - Assembles an executable jar archive containing the main classes and their dependencies.
build - Assembles and tests this project.
buildDependents - Assembles and tests this project and all projects that depend on it.
buildNeeded - Assembles and tests this project and all projects it depends on.
classes - Assembles main classes.
clean - Deletes the build directory.
jar - Assembles a jar archive containing the main classes.
testClasses - Assembles test classes.

Docker tasks
------------
composeBuild - Builds images for services of docker-compose project
composeDown - Stops and removes containers of docker-compose project (only if stopContainers is set to true)
composeDownForced - Stops and removes containers of docker-compose project
composeLogs - Stores log output from services in containers of docker-compose project
composePull - Builds and pulls images of docker-compose project
composePush - Pushes images for services of docker-compose project
composeUp - Builds and starts containers of docker-compose project

Documentation tasks
-------------------
javadoc - Generates Javadoc API documentation for the main source code.

Help tasks
----------
buildEnvironment - Displays all buildscript dependencies declared in project ':api-gateway'.
components - Displays the components produced by project ':api-gateway'. [incubating]
dependencies - Displays all dependencies declared in project ':api-gateway'.
dependencyInsight - Displays the insight into a specific dependency in project ':api-gateway'.
dependencyManagement - Displays the dependency management declared in project ':api-gateway'.
dependentComponents - Displays the dependent components of components in project ':api-gateway'. [incubating]
help - Displays a help message.
model - Displays the configuration model of project ':api-gateway'. [incubating]
projects - Displays the sub-projects of project ':api-gateway'.
properties - Displays the properties of project ':api-gateway'.
tasks - Displays the tasks runnable from project ':api-gateway'.

Verification tasks
------------------
check - Runs all checks.
test - Runs the unit tests.

Other tasks
-----------
buildDockerImage - Build the docker image of Websphere Liberty Developer version that run the springboot app.
checkPodStatus - Check the deployed pods status.
compileJava - Compiles main Java source.
compileTestJava - Compiles test Java source.
createImageSecret - Create a secret for pulling image from the image registry.
createNewApp
createNewProject - Create a new Openshift project using the optional value of -PocProjectName if provided, otherwise, it is default to the parent project name.
deleteApp - Delete the openshift application.
deployContainer
enableSpringApp - Let Liberty recognizes the Springboot app.
exposeService - Make the app accessible outside the cluster by creating a service route.
initConfigParameters
installIBMCloudPlugin - Install IBM Cloud cr and cs plugins.
integrationTest - Perform integration test with other containers.
loginCloud - Login to IBM Cloud.
loginOpenshift - Loging to openshift with parameter <openshiftUrl>.
processResources - Processes main resources.
processTestResources - Processes test resources.
pushDockerImage - Push the spring boot application docker image to a Container Registry.
removeDockerContainer - Remove any running api-gateway container.
removeDockerImage - Remove any api-gateway-app image.
testDeployedService - Test the deployed service.
testDockerImage - Test the docker image created by the buildDockerImage task.
validateContainer

Rules
-----
Pattern: clean<TaskName>: Cleans the output files of a task.
Pattern: build<ConfigurationName>: Assembles the artifacts of a configuration.
Pattern: upload<ConfigurationName>: Assembles and uploads the artifacts belonging to a configuration.

BUILD SUCCESSFUL in 1s
1 actionable task: 1 executed
<-------------> 0% WAITING
> IDLE
~~~

## docker-compose-plugins settings
~~~
dockerCompose {
     useComposeFiles = ['api-gateway-integration.yaml']
     removeImages = "Local"
     environment.put 'AVAIL_API_VERSION', '0.0.1-SNAPSHOT'
     environment.put 'AVAIL_MGNT_VERSION', '0.0.1-SNAPSHOT'
}

dockerCompose.isRequiredBy(integrationTest)
~~~
[bamboo@localhost solar-travel-integration]$ ls api-gateway/api-gateway-integration.yaml -ls
~~~
4 -rw-rw-r--. 1 bamboo bamboo 1384 Feb  7 12:20 api-gateway/api-gateway-integration.yaml
~~~
[bamboo@localhost solar-travel-integration]$ vi api-gateway/api-gateway-integration.yaml  -R
~~~
1 version: "3.3"
2
3 services:
4   api-gateway:
5     build: .
6     links:
7       - avail-api-app
8       - avail-mgnt-app
9     ports:
10       - "39083:9080"
11     environment:
12       - AVAILABILITY_SERVICE_NAME=avail-api-app:9080
13       - AVAILABILITY_MGNT_SERVICE_NAME=avail-mgnt-app:9080
14
15   avail-api-app:
16     image: "quay.io/solar-travel/avail-api-app:${AVAIL_API_VERSION}"
17     links:
18       - redis-solar-travel-availability
19     ports:
20       - "39081:9080"
21     environment:
22       - REDIS_CONNECTION_STRING=redis://redis-solar-travel-availability
23       - IBM_S3_LOCATION=""
24       - IBM_S3_API_KEY=""
25       - IBM_S3_IAM_SERVICE_ID=""
26       - IBM_S3_PUBLIC_ENDPOINT=""
27       - RES_DATA_BUCKET_NAME=""
28       - RES_DATA_OBJECT_KEY=""
29
30   avail-mgnt-app:
31     image: "quay.io/solar-travel/avail-mgnt-app:${AVAIL_MGNT_VERSION}"
32     links:
33       - redis-solar-travel-availability
34       - redis-solar-travel-pubsub
35     ports:
36       - "39082:9080"
37     environment:
38       - REDIS_CONNECTION_STRING=redis://redis-solar-travel-availability
39       - REDIS_PUBSUB_CONNECTION_STRING=redis://redis-solar-travel-pubsub
40       - IBM_S3_LOCATION=""
41       - IBM_S3_API_KEY=""
42       - IBM_S3_IAM_SERVICE_ID=""
43       - IBM_S3_PUBLIC_ENDPOINT=""
44       - RES_DATA_BUCKET_NAME=""
45       - RES_DATA_OBJECT_KEY=""
46
47   redis-solar-travel-availability:
48     image: redis
49
50   redis-solar-travel-pubsub:
51     image: redis
~~~
## Grdle integrationTest task implementation
~~~
669 task integrationTest(dependsOn: build) {
670     description = "Perform integration test with other containers."
671
672     doLast {
673         String[] ports = ["39081", "39082", "39083"]
674         for (String port : ports) {
675             sendRetry("http://localhost:$port/hello")
676             println "Connected to endpoint http://localhost:$port/hello"
677             println "----------------------------------------------------------------------------------------"
678         }
679         String result = executeCmd("curl -sX GET 'http://localhost:39083/management/trains/reload'")
680         def jsonSlurper = new groovy.json.JsonSlurper()
681         def reloadResult = jsonSlurper.parseText(result)
682
683         executeCmd("curl -sX GET 'http://localhost:39083/management/trains/waitfor/${reloadResult.id}'")
684         result = executeCmd("curl -sX GET 'http://localhost:39083/customers/trains?trainDate=20190131&trainTime=2200&origin=EAR&dest=SUN'")
685         def trains = jsonSlurper.parseText(result)
686         assert 6 == trains.size()
687         println "Total number of trains return: ${trains.size()}"
688     }
689 }
~~~
## Running the integration Test
[bamboo@rhel-7-server solar-travel-integration]$ gradle :api-gateway:integrationTest
~~~
Starting a Gradle Daemon (subsequent builds will be faster)

> Task :api-gateway:composeUp
redis-solar-travel-availability uses an image, skipping
avail-api-app uses an image, skipping
redis-solar-travel-pubsub uses an image, skipping
avail-mgnt-app uses an image, skipping
Building api-gateway
aa06e167b60ece271c3dc97464aed27fapigateway_redis-solar-travel-availability_1 is up-to-date
aa06e167b60ece271c3dc97464aed27fapigateway_redis-solar-travel-pubsub_1 is up-to-date
aa06e167b60ece271c3dc97464aed27fapigateway_avail-api-app_1 is up-to-date
aa06e167b60ece271c3dc97464aed27fapigateway_avail-mgnt-app_1 is up-to-date
aa06e167b60ece271c3dc97464aed27fapigateway_api-gateway_1 is up-to-date
Will use 172.20.0.1 (network aa06e167b60ece271c3dc97464aed27fapigateway_default) as host of redis-solar-travel-availability
Will use 172.20.0.1 (network aa06e167b60ece271c3dc97464aed27fapigateway_default) as host of avail-api-app
Will use 172.20.0.1 (network aa06e167b60ece271c3dc97464aed27fapigateway_default) as host of redis-solar-travel-pubsub
Will use 172.20.0.1 (network aa06e167b60ece271c3dc97464aed27fapigateway_default) as host of avail-mgnt-app
Will use 172.20.0.1 (network aa06e167b60ece271c3dc97464aed27fapigateway_default) as host of api-gateway
Probing TCP socket on 172.20.0.1:39081 of service 'avail-api-app_1'
TCP socket on 172.20.0.1:39081 of service 'avail-api-app_1' is ready
Probing TCP socket on 172.20.0.1:39082 of service 'avail-mgnt-app_1'
TCP socket on 172.20.0.1:39082 of service 'avail-mgnt-app_1' is ready
Probing TCP socket on 172.20.0.1:39083 of service 'api-gateway_1'
TCP socket on 172.20.0.1:39083 of service 'api-gateway_1' is ready

> Task :api-gateway:integrationTest
Executing command: curl -sX GET http://localhost:39081/hello
Hello, world

Connected to endpoint http://localhost:39081/hello
----------------------------------------------------------------------------------------
Executing command: curl -sX GET http://localhost:39082/hello
Hello, world

Connected to endpoint http://localhost:39082/hello
----------------------------------------------------------------------------------------
Executing command: curl -sX GET http://localhost:39083/hello
Hello, world

Connected to endpoint http://localhost:39083/hello
----------------------------------------------------------------------------------------
Executing command: curl -sX GET 'http://localhost:39083/management/trains/reload'
{"id":"9f8b3775-1db9-4b05-9f89-7903d85f853b","receivedTime":"2020-02-17T08:18:12.400+0000","status":"ACCEPTED"}

Executing command: curl -sX GET 'http://localhost:39083/management/trains/waitfor/9f8b3775-1db9-4b05-9f89-7903d85f853b'
{"id":"9f8b3775-1db9-4b05-9f89-7903d85f853b","receivedTime":"2020-02-17T08:18:42.660+0000","status":"COMPLETE"}

Executing command: curl -sX GET 'http://localhost:39083/customers/trains?trainDate=20190131&trainTime=2200&origin=EAR&dest=SUN'
[{"destinationStation":{"locationCode":"SUN"},"originStation":{"locationCode":"EAR"},"trainDate":"20190131","trainTime":"2202","train":{"trainNumber":"0684"},"classCodes":[{"code":"Y","quantity":0},{"code":"YF","quantity":0},{"code":"YV","quantity":0},{"code":"YW;","quantity":0}]},{"destinationStation":{"locationCode":"SUN"},"originStation":{"locationCode":"EAR"},"trainDate":"20190131","trainTime":"2232","train":{"trainNumber":"0160"},"classCodes":[{"code":"Y","quantity":0},{"code":"YF","quantity":0},{"code":"YV","quantity":0},{"code":"YW;","quantity":0}]},{"destinationStation":{"locationCode":"SUN"},"originStation":{"locationCode":"EAR"},"trainDate":"20190131","trainTime":"2242","train":{"trainNumber":"0858"},"classCodes":[{"code":"YA","quantity":0},{"code":"Y","quantity":0},{"code":"YB","quantity":0},{"code":"YF","quantity":0},{"code":"YV","quantity":0},{"code":"YW;","quantity":0}]},{"destinationStation":{"locationCode":"SUN"},"originStation":{"locationCode":"EAR"},"trainDate":"20190131","trainTime":"2307","train":{"trainNumber":"0690"},"classCodes":[{"code":"Y","quantity":0},{"code":"YF","quantity":0},{"code":"YV","quantity":0},{"code":"YW;","quantity":0}]},{"destinationStation":{"locationCode":"SUN"},"originStation":{"locationCode":"EAR"},"trainDate":"20190131","trainTime":"2347","train":{"trainNumber":"0862"},"classCodes":[{"code":"YA","quantity":0},{"code":"Y","quantity":0},{"code":"YB","quantity":0},{"code":"YF","quantity":0},{"code":"YV","quantity":0},{"code":"YW;","quantity":0}]},{"destinationStation":{"locationCode":"SUN"},"originStation":{"locationCode":"EAR"},"trainDate":"20190131","trainTime":"2352","train":{"trainNumber":"0294"},"classCodes":[{"code":"YA","quantity":0},{"code":"Y","quantity":0},{"code":"YB","quantity":0},{"code":"YF","quantity":0},{"code":"YV","quantity":0},{"code":"YW;","quantity":0}]}]

Total number of trains return: 6

> Task :api-gateway:composeDown
Stopping aa06e167b60ece271c3dc97464aed27fapigateway_api-gateway_1                     ...
Stopping aa06e167b60ece271c3dc97464aed27fapigateway_avail-mgnt-app_1                  ...
Stopping aa06e167b60ece271c3dc97464aed27fapigateway_avail-api-app_1                   ...
Stopping aa06e167b60ece271c3dc97464aed27fapigateway_redis-solar-travel-pubsub_1       ...
Stopping aa06e167b60ece271c3dc97464aed27fapigateway_redis-solar-travel-availability_1 ...
Stopping aa06e167b60ece271c3dc97464aed27fapigateway_api-gateway_1                     ... done
Stopping aa06e167b60ece271c3dc97464aed27fapigateway_avail-api-app_1                   ... done
Stopping aa06e167b60ece271c3dc97464aed27fapigateway_avail-mgnt-app_1                  ... done
Stopping aa06e167b60ece271c3dc97464aed27fapigateway_redis-solar-travel-pubsub_1       ... done
Stopping aa06e167b60ece271c3dc97464aed27fapigateway_redis-solar-travel-availability_1 ... done
Removing aa06e167b60ece271c3dc97464aed27fapigateway_api-gateway_1                     ...
Removing aa06e167b60ece271c3dc97464aed27fapigateway_avail-mgnt-app_1                  ...
Removing aa06e167b60ece271c3dc97464aed27fapigateway_avail-api-app_1                   ...
Removing aa06e167b60ece271c3dc97464aed27fapigateway_redis-solar-travel-pubsub_1       ...
Removing aa06e167b60ece271c3dc97464aed27fapigateway_redis-solar-travel-availability_1 ...
Removing aa06e167b60ece271c3dc97464aed27fapigateway_redis-solar-travel-availability_1 ... done
Removing aa06e167b60ece271c3dc97464aed27fapigateway_avail-api-app_1                   ... done
Removing aa06e167b60ece271c3dc97464aed27fapigateway_api-gateway_1                     ... done
Removing aa06e167b60ece271c3dc97464aed27fapigateway_redis-solar-travel-pubsub_1       ... done
Removing aa06e167b60ece271c3dc97464aed27fapigateway_avail-mgnt-app_1                  ... done
Removing network aa06e167b60ece271c3dc97464aed27fapigateway_default
Removing image aa06e167b60ece271c3dc97464aed27fapigateway_api-gateway

BUILD SUCCESSFUL in 1m 20s
6 actionable tasks: 3 executed, 3 up-to-date
~~~
# Jenkins Pipeline
~~~
def executeCmd(def cmd) {
    def output = new StringBuffer();
    println "Executing command: " + cmd
    def process = ['bash', '-c', cmd].execute()
    process.waitForProcessOutput(output, output)
    if (process.exitValue() != 0) throw new Exception(output.toString())
    println output.toString()
    return output.toString()
}

"${BUILD_NUMBER}"

node {
    stage('Clone sources') {
      executeCmd "rm -r -f ${workspace}/${serviceName}"
      executeCmd "git config --global user.name 'George Tsang'"
      executeCmd "git config --global user.emai 'tsangcc@tw.ibm.com'"
      withCredentials([string(credentialsId: 'jenkins-ibm-git-repo-key', variable: 'IBM_GIT_REPO_KEY')]) {
        executeCmd "git clone --single-branch --branch $branch https://${IBM_GIT_REPO_KEY}@github.ibm.com/tsangcc/solar-travel-integration.git ${workspace}/${serviceName}"
      }
      //executeCmd "sudo chown -R jenkins:jenkins ${workspace}/avail-api-app"
    }

	stage('Compile and Test service application') {
	  dir("${WORKSPACE}/${serviceName}") {
        def statusCode = sh label: '', returnStatus: true, script: './gradlew clean && ./gradlew ${serviceName}:build'
        if (statusCode != 0) throw new Exception("Failed to build container image.")
      }
	}

    stage('Build Container Image') {
      dir("${WORKSPACE}/${serviceName}") {
        def statusCode = sh label: '', returnStatus: true, script: './gradlew -PimageVersion=${imageVersion}.${BUILD_NUMBER}-${imageTagName} :${serviceName}:buildDockerImage'
        if (statusCode != 0) throw new Exception("Failed to build container image.")
      }
    }

	stage('Test Container Image') {
	  dir("${WORKSPACE}/${serviceName}") {
        def statusCode = sh label: '', returnStatus: true, script: './gradlew -PimageVersion=${imageVersion}.${BUILD_NUMBER}-${imageTagName} :${serviceName}:testDockerImage'
        if (statusCode != 0) throw new Exception("Failed to test container image.")
      }
	}

    stage('Validate and Push Container Image') {
      dir("${WORKSPACE}/${serviceName}") {
        withCredentials([string(credentialsId: 'jenkins-docker-password', variable: 'dockerPassword')]) {
            def statusCode = sh label: '', returnStatus: true, script: './gradlew -PimageVersion=${imageVersion}.${BUILD_NUMBER}-${imageTagName} -PdockerUser=gtsang -PdockerPassword=$dockerPassword -PdockerImageRegistryUrl=$DOCKER_IMAGE_REGISTRY_URL -PdockerImageNamespace=$DOCKER_IMAGE_NAMESPACE :${serviceName}:validateContainer'
            if (statusCode != 0) throw new Exception("Failed to validate container image.")
        }
      }
    }

    stage('Deploy Container to OpenShift') {
      dir("${WORKSPACE}/${serviceName}") {
        withCredentials([string(credentialsId: 'jenkins-openshift-login-token', variable: 'token')]) {
            executeCmd "oc login $ocHost --token=$token --insecure-skip-tls-verify"
        }
        def statusCode = sh label: '', returnStatus: true, script: './gradlew -PimageVersion=${imageVersion}.${BUILD_NUMBER}-${imageTagName} -Ptls-termination=edge -PocProjectName=$ocProjectName -PdockerImageRegistryUrl=$DOCKER_IMAGE_REGISTRY_URL -PdockerImageNamespace=$DOCKER_IMAGE_NAMESPACE :${serviceName}:deployContainer'
        executeCmd "oc logout"
        if (statusCode != 0) throw new Exception("Failed to deploy container")
      }
    }
}
~~~
## Jenkins Build with Parameters setting example
![](jenkins1.png)

## Jenkins Build result
![](jenkins2.png)
