├── .gitignore ├── Jenkinsfile ├── Jenkinsfile-arquillian ├── Jenkinsfile-basic ├── Jenkinsfile-declarative ├── Jenkinsfile-declarative-one-agent ├── Jenkinsfile-declarative-restricted ├── Jenkinsfile-declarative-runas ├── Jenkinsfile-jnlp-base ├── Jenkinsfile-mavenlocalrepo ├── Jenkinsfile-yaml ├── LICENSE ├── README.md ├── app-users ├── README.md ├── application-local.properties ├── bootstrap.properties ├── configuration │ └── settings.xml ├── pom.xml ├── run.sh └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── mycompany │ │ │ └── k8sit │ │ │ ├── Application.java │ │ │ ├── model │ │ │ ├── Address.java │ │ │ └── User.java │ │ │ └── route │ │ │ └── UserRoute.java │ └── resources │ │ ├── application-arquillian.properties │ │ ├── application-k8sit.properties │ │ ├── application.properties │ │ ├── bootstrap.properties │ │ └── spring │ │ └── application-context.xml │ └── test │ ├── java │ └── com │ │ └── mycompany │ │ └── k8sit │ │ └── ApplicationTest.java │ └── resources │ ├── application-unittest.properties │ ├── bootstrap.properties │ ├── data.sql │ └── schema.sql ├── arquillian-test ├── README.md ├── podtemplate-arquillian.xml ├── pom.xml └── src │ └── test │ ├── java │ └── com │ │ └── mycompany │ │ └── k8sit │ │ └── ArquillianTest.java │ └── resources │ ├── after-namespace.sh │ ├── app-users-test.yaml │ ├── arquillian.xml │ ├── logback.xml │ └── sql │ └── sql-load.sql ├── configuration └── settings.xml ├── integration-test ├── mockserver │ ├── setup.sh │ ├── testRetry@test.com.err │ └── testSucc@test.com.ok ├── pom.xml ├── run.sh ├── sql │ ├── create-tables.sql │ ├── load-data.sql │ └── setup.sh └── src │ └── test │ └── java │ └── com │ └── mycompany │ └── k8sit │ ├── IntegrationTest.java │ └── SpringConfiguration.java └── pod.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | **/target 2 | **/.settings 3 | **/.project 4 | **/.classpath 5 | **/*.iml 6 | **/.idea 7 | -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | podTemplate( 2 | name: 'app-users-it', 3 | label: 'app-users-it', 4 | cloud: 'openshift', 5 | containers: [ 6 | //Java agent, test executor 7 | containerTemplate(name: 'jnlp', 8 | image: 'registry.access.redhat.com/openshift3/jenkins-slave-maven-rhel7:v3.9', 9 | resourceLimitMemory: '512Mi', 10 | workingDir: '/home/jenkins', 11 | command: '/bin/sh -c', 12 | args: '"umask 0000; /usr/local/bin/run-jnlp-client ${computer.jnlpmac} ${computer.name}"', 13 | alwaysPullImage: false, 14 | envVars: [ 15 | //Heap for jnlp is 1/2, for mvn and surefire process is 1/4 of resourceLimitMemory by default 16 | envVar(key: 'JNLP_MAX_HEAP_UPPER_BOUND_MB', value: '64') 17 | ]), 18 | //App under test 19 | containerTemplate(name: 'app-users', 20 | image: '172.30.1.1:5000/myproject/app-users:latest', 21 | resourceLimitMemory: '512Mi', 22 | alwaysPullImage: true, 23 | envVars: [ 24 | envVar(key: 'SPRING_PROFILES_ACTIVE', value: 'k8sit'), 25 | envVar(key: 'SPRING_CLOUD_KUBERNETES_ENABLED', value: 'false') 26 | ]), 27 | //DB 28 | containerTemplate(name: 'mariadb', 29 | image: 'registry.access.redhat.com/rhscl/mariadb-102-rhel7:1', 30 | resourceLimitMemory: '256Mi', 31 | alwaysPullImage: false, 32 | envVars: [ 33 | envVar(key: 'MYSQL_USER', value: 'myuser'), 34 | envVar(key: 'MYSQL_PASSWORD', value: 'mypassword'), 35 | envVar(key: 'MYSQL_DATABASE', value: 'testdb'), 36 | envVar(key: 'MYSQL_ROOT_PASSWORD', value: 'secret') 37 | ]), 38 | //AMQ 39 | containerTemplate(name: 'amq', 40 | image: 'registry.access.redhat.com/jboss-amq-6/amq63-openshift:1.3', 41 | resourceLimitMemory: '256Mi', 42 | alwaysPullImage: false, 43 | envVars: [ 44 | envVar(key: 'AMQ_USER', value: 'test'), 45 | envVar(key: 'AMQ_PASSWORD', value: 'secret') 46 | ]), 47 | //External API Third party (provided by mockserver) 48 | containerTemplate(name: 'mockserver', 49 | image: 'jamesdbloom/mockserver:mockserver-5.3.0', 50 | resourceLimitMemory: '256Mi', 51 | alwaysPullImage: false, 52 | envVars: [ 53 | envVar(key: 'LOG_LEVEL', value: 'INFO'), 54 | envVar(key: 'JVM_OPTIONS', value: '-Xmx128m'), 55 | ]) 56 | ] 57 | ) 58 | { 59 | node('maven'){ 60 | stage('Pull source') { 61 | checkout scm //git url: 'https://github.com/bszeti/kubernetes-integration-test.git' 62 | } 63 | 64 | dir ("app-users") { 65 | stage('Build app') { 66 | sh "mvn -B -s ../configuration/settings.xml -DskipTests package" 67 | } 68 | 69 | stage('Build Image') { 70 | // Requires: minishift config set insecure-registry 172.30.0.0/16 71 | sh "oc new-build --name=app-users --docker-image=registry.access.redhat.com/fuse7/fuse-java-openshift:latest --binary=true --labels=app=app-users || true" 72 | sh 'rm -rf oc-build && mkdir -p oc-build/deployments' 73 | sh 'cp target/*.jar oc-build/deployments' 74 | 75 | openshift.withCluster() { 76 | openshift.withProject('myproject') { 77 | openshift.selector('bc', 'app-users').startBuild('--from-dir=oc-build', '--wait=true').logs('-f') 78 | } 79 | 80 | } 81 | } 82 | } 83 | 84 | } 85 | 86 | node('app-users-it') { 87 | stage('Pull source') { 88 | checkout scm 89 | } 90 | dir ("integration-test") { 91 | stage('Prepare test') { 92 | container('mariadb') { 93 | //requires mysql 94 | sh 'sql/setup.sh' 95 | } 96 | container('jnlp') { 97 | //requires curl and python 98 | sh 'mockserver/setup.sh' 99 | } 100 | } 101 | 102 | //These env vars are used the tests to send message to users.in queue 103 | withEnv(['AMQ_USER=test', 104 | 'AMQ_PASSWORD=secret']) { 105 | stage('Build and run test') { 106 | try { 107 | sh 'mvn -s ../configuration/settings.xml -B clean test' 108 | } finally { 109 | junit 'target/surefire-reports/*.xml' 110 | } 111 | } 112 | } 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /Jenkinsfile-arquillian: -------------------------------------------------------------------------------- 1 | //See README.md under arquillian-test how to prepare Jenkins build 2 | node('arquillian'){ 3 | stage('Pull source') { 4 | checkout scm 5 | } 6 | 7 | dir ("arquillian-test") { 8 | try { 9 | stage('Build app') { 10 | sh "mvn -B -s ../configuration/settings.xml test" 11 | } 12 | } finally { 13 | archiveArtifacts artifacts: 'target/surefire-reports/*.log' 14 | junit 'target/surefire-reports/*.xml' 15 | } 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /Jenkinsfile-basic: -------------------------------------------------------------------------------- 1 | podTemplate( 2 | label: 'app-users-it', 3 | cloud: 'openshift', 4 | containers: [ 5 | //Java agent, test executor 6 | containerTemplate(name: 'jnlp', 7 | image: 'registry.access.redhat.com/openshift3/jenkins-slave-maven-rhel7:v3.9', 8 | resourceLimitMemory: '512Mi', 9 | command: '/bin/sh -c', 10 | args: '"umask 0000; /usr/local/bin/run-jnlp-client ${computer.jnlpmac} ${computer.name}"', 11 | envVars: [ 12 | //Heap for jnlp is 1/2, for mvn and surefire process is 1/4 of resourceLimitMemory by default 13 | envVar(key: 'JNLP_MAX_HEAP_UPPER_BOUND_MB', value: '64') 14 | ]), 15 | //App under test 16 | containerTemplate(name: 'app-users', 17 | image: '172.30.1.1:5000/myproject/app-users:latest', 18 | resourceLimitMemory: '512Mi', 19 | envVars: [ 20 | envVar(key: 'SPRING_PROFILES_ACTIVE', value: 'k8sit'), 21 | envVar(key: 'SPRING_CLOUD_KUBERNETES_ENABLED', value: 'false') 22 | ]), 23 | //DB 24 | containerTemplate(name: 'mariadb', 25 | image: 'registry.access.redhat.com/rhscl/mariadb-102-rhel7:1', 26 | resourceLimitMemory: '256Mi', 27 | envVars: [ 28 | envVar(key: 'MYSQL_USER', value: 'myuser'), 29 | envVar(key: 'MYSQL_PASSWORD', value: 'mypassword'), 30 | envVar(key: 'MYSQL_DATABASE', value: 'testdb'), 31 | envVar(key: 'MYSQL_ROOT_PASSWORD', value: 'secret') 32 | ]), 33 | //AMQ 34 | containerTemplate(name: 'amq', 35 | image: 'registry.access.redhat.com/jboss-amq-6/amq63-openshift:1.3', 36 | resourceLimitMemory: '256Mi', 37 | envVars: [ 38 | envVar(key: 'AMQ_USER', value: 'test'), 39 | envVar(key: 'AMQ_PASSWORD', value: 'secret') 40 | ]), 41 | //External API Third party (provided by mockserver) 42 | containerTemplate(name: 'mockserver', 43 | image: 'jamesdbloom/mockserver:mockserver-5.3.0', 44 | resourceLimitMemory: '256Mi', 45 | envVars: [ 46 | envVar(key: 'LOG_LEVEL', value: 'INFO'), 47 | envVar(key: 'JVM_OPTIONS', value: '-Xmx128m'), 48 | ]) 49 | ] 50 | ) 51 | { 52 | node('app-users-it') { 53 | stage('Pull source') { 54 | checkout scm 55 | } 56 | dir ("integration-test") { 57 | stage('Prepare test') { 58 | container('mariadb') { 59 | //requires mysql 60 | sh 'sql/setup.sh' 61 | } 62 | //requires curl and python 63 | sh 'mockserver/setup.sh' 64 | } 65 | 66 | //These env vars are used the tests to send message to users.in queue 67 | withEnv(['AMQ_USER=test', 68 | 'AMQ_PASSWORD=secret']) { 69 | stage('Build and run test') { 70 | try { 71 | sh 'mvn -s ../configuration/settings.xml -B clean test' 72 | } finally { 73 | junit 'target/surefire-reports/*.xml' 74 | } 75 | } 76 | } 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Jenkinsfile-declarative: -------------------------------------------------------------------------------- 1 | //Update kubernetes-plugin to 1.7 2 | pipeline { 3 | agent none //Using different agent for each stage 4 | stages { 5 | 6 | /******** 7 | * Build the jar and the docker image using the default 'maven' node 8 | ********/ 9 | stage('Build App and Image') { 10 | agent{ label 'maven' } 11 | steps { 12 | dir ("app-users") { 13 | sh "mvn -B -s ../configuration/settings.xml -DskipTests package" 14 | 15 | // Requires: minishift config set insecure-registry 172.30.0.0/16 16 | sh "oc new-build --name=app-users --docker-image=registry.access.redhat.com/fuse7/fuse-java-openshift:latest --binary=true --labels=app=app-users || true" 17 | sh 'rm -rf oc-build && mkdir -p oc-build/deployments' 18 | sh 'cp target/*.jar oc-build/deployments' 19 | 20 | script { 21 | openshift.withCluster() { 22 | openshift.withProject('myproject') { 23 | openshift.selector('bc', 'app-users').startBuild('--from-dir=oc-build', '--wait=true').logs('-f') 24 | } 25 | 26 | } 27 | } 28 | } 29 | } 30 | } 31 | 32 | 33 | /******** 34 | * Run the integration test with the custom pod defined with a yaml 35 | ********/ 36 | stage('Run integration test') { 37 | agent{ 38 | kubernetes { 39 | label 'app-users-it' 40 | cloud 'openshift' 41 | defaultContainer 'jnlp' 42 | yaml ''' 43 | apiVersion: v1 44 | kind: Pod 45 | metadata: 46 | labels: 47 | app: app-users 48 | spec: 49 | containers: 50 | #Java agent, test executor 51 | - name: jnlp 52 | image: registry.access.redhat.com/openshift3/jenkins-slave-maven-rhel7:v3.9 53 | command: 54 | - /bin/sh 55 | args: 56 | - -c 57 | - umask 0000; /usr/local/bin/run-jnlp-client $(JENKINS_SECRET) $(JENKINS_NAME) 58 | resources: 59 | limits: 60 | memory: 512Mi 61 | workingDir: /home/jenkins 62 | env: 63 | - name: JNLP_MAX_HEAP_UPPER_BOUND_MB 64 | value: 64 65 | #App under test 66 | - name: app-users 67 | image: 172.30.1.1:5000/myproject/app-users:latest 68 | resources: 69 | limits: 70 | memory: 512Mi 71 | env: 72 | - name: SPRING_PROFILES_ACTIVE 73 | value: k8sit 74 | - name: SPRING_CLOUD_KUBERNETES_ENABLED 75 | value: false 76 | #DB 77 | - name: mariadb 78 | image: registry.access.redhat.com/rhscl/mariadb-102-rhel7:1 79 | resources: 80 | limits: 81 | memory: 256Mi 82 | env: 83 | - name: MYSQL_USER 84 | value: myuser 85 | - name: MYSQL_PASSWORD 86 | value: mypassword 87 | - name: MYSQL_DATABASE 88 | value: testdb 89 | - name: MYSQL_ROOT_PASSWORD 90 | value: secret 91 | readinessProbe: 92 | tcpSocket: 93 | port: 3306 94 | initialDelaySeconds: 5 95 | #AMQ 96 | - name: amq 97 | image: registry.access.redhat.com/jboss-amq-6/amq63-openshift:1.3 98 | resources: 99 | limits: 100 | memory: 256Mi 101 | env: 102 | - name: AMQ_USER 103 | value: test 104 | - name: AMQ_PASSWORD 105 | value: secret 106 | readinessProbe: 107 | tcpSocket: 108 | port: 61616 109 | initialDelaySeconds: 5 110 | #External API Third party (provided by mockserver) 111 | - name: mockserver 112 | image: jamesdbloom/mockserver:mockserver-5.3.0 113 | resources: 114 | limits: 115 | memory: 256Mi 116 | env: 117 | - name: LOG_LEVEL 118 | value: INFO 119 | - name: JVM_OPTIONS 120 | value: -Xmx128m 121 | readinessProbe: 122 | tcpSocket: 123 | port: 1080 124 | initialDelaySeconds: 5 125 | ''' 126 | } 127 | } 128 | environment { 129 | //These env vars are used the tests to send message to users.in queue 130 | AMQ_USER = 'test' 131 | AMQ_PASSWORD = 'secret' 132 | } 133 | steps { 134 | dir ("integration-test") { 135 | container('mariadb') { 136 | //requires mysql 137 | sh 'sql/setup.sh' 138 | } 139 | 140 | // Default container 'jnlp' 141 | // this script requires curl and python. 142 | sh 'mockserver/setup.sh' 143 | 144 | //Run the tests. 145 | //Somehow simply "mvn ..." doesn't work here 146 | sh '/bin/bash -c "mvn -s ../configuration/settings.xml -B clean test"' 147 | } 148 | } 149 | post { 150 | always { 151 | junit testResults: 'integration-test/target/surefire-reports/*.xml', allowEmptyResults: true 152 | } 153 | } 154 | } 155 | 156 | } 157 | 158 | } 159 | -------------------------------------------------------------------------------- /Jenkinsfile-declarative-one-agent: -------------------------------------------------------------------------------- 1 | pipeline { 2 | agent{ 3 | kubernetes { 4 | label 'app-users-it' 5 | cloud 'openshift' 6 | defaultContainer 'jnlp' 7 | yaml ''' 8 | apiVersion: v1 9 | kind: Pod 10 | metadata: 11 | labels: 12 | app: app-users 13 | spec: 14 | containers: 15 | #Java agent, test executor 16 | - name: jnlp 17 | image: registry.access.redhat.com/openshift3/jenkins-slave-maven-rhel7:v3.9 18 | command: 19 | - /bin/sh 20 | args: 21 | - -c 22 | - umask 0000; /usr/local/bin/run-jnlp-client $(JENKINS_SECRET) $(JENKINS_NAME) 23 | resources: 24 | limits: 25 | memory: 512Mi 26 | workingDir: /home/jenkins 27 | env: 28 | - name: JNLP_MAX_HEAP_UPPER_BOUND_MB 29 | value: 64 30 | #App under test 31 | - name: app-users 32 | image: 172.30.1.1:5000/myproject/app-users:latest 33 | resources: 34 | limits: 35 | memory: 512Mi 36 | env: 37 | - name: SPRING_PROFILES_ACTIVE 38 | value: k8sit 39 | - name: SPRING_CLOUD_KUBERNETES_ENABLED 40 | value: false 41 | #DB 42 | - name: mariadb 43 | image: registry.access.redhat.com/rhscl/mariadb-102-rhel7:1 44 | resources: 45 | limits: 46 | memory: 256Mi 47 | env: 48 | - name: MYSQL_USER 49 | value: myuser 50 | - name: MYSQL_PASSWORD 51 | value: mypassword 52 | - name: MYSQL_DATABASE 53 | value: testdb 54 | - name: MYSQL_ROOT_PASSWORD 55 | value: secret 56 | readinessProbe: 57 | tcpSocket: 58 | port: 3306 59 | initialDelaySeconds: 5 60 | #AMQ 61 | - name: amq 62 | image: registry.access.redhat.com/jboss-amq-6/amq63-openshift:1.3 63 | resources: 64 | limits: 65 | memory: 256Mi 66 | env: 67 | - name: AMQ_USER 68 | value: test 69 | - name: AMQ_PASSWORD 70 | value: secret 71 | readinessProbe: 72 | tcpSocket: 73 | port: 61616 74 | initialDelaySeconds: 5 75 | #External API Third party (provided by mockserver) 76 | - name: mockserver 77 | image: jamesdbloom/mockserver:mockserver-5.3.0 78 | resources: 79 | limits: 80 | memory: 256Mi 81 | env: 82 | - name: LOG_LEVEL 83 | value: INFO 84 | - name: JVM_OPTIONS 85 | value: -Xmx128m 86 | readinessProbe: 87 | tcpSocket: 88 | port: 1080 89 | initialDelaySeconds: 5 90 | ''' 91 | } 92 | } 93 | 94 | stages { 95 | 96 | /******** 97 | * Run the integration test with the custom pod defined with a yaml 98 | ********/ 99 | stage('Run integration test') { 100 | 101 | environment { 102 | //These env vars are used the tests to send message to users.in queue 103 | AMQ_USER = 'test' 104 | AMQ_PASSWORD = 'secret' 105 | } 106 | steps { 107 | dir ("integration-test") { 108 | container('mariadb') { 109 | //requires mysql 110 | sh 'sql/setup.sh' 111 | } 112 | 113 | // Default container 'jnlp' 114 | // this script requires curl and python. 115 | sh 'mockserver/setup.sh' 116 | 117 | //Run the tests. 118 | //Somehow simply "mvn ..." doesn't work here 119 | sh '/bin/bash -c "mvn -s ../configuration/settings.xml -B clean test"' 120 | } 121 | } 122 | post { 123 | always { 124 | junit testResults: 'integration-test/target/surefire-reports/*.xml', allowEmptyResults: true 125 | } 126 | } 127 | } 128 | 129 | } 130 | 131 | } 132 | -------------------------------------------------------------------------------- /Jenkinsfile-declarative-restricted: -------------------------------------------------------------------------------- 1 | //Update kubernetes-plugin to 1.7 2 | pipeline { 3 | agent none //Using different agent for each stage 4 | stages { 5 | 6 | /******** 7 | * Run the integration test with the custom pod defined with a yaml 8 | ********/ 9 | stage('Run integration test') { 10 | agent{ 11 | kubernetes { 12 | label 'app-users-it' 13 | cloud 'openshift' 14 | defaultContainer 'jnlp' 15 | yaml ''' 16 | apiVersion: v1 17 | kind: Pod 18 | metadata: 19 | labels: 20 | app: app-users 21 | spec: 22 | #To resolve the file permission problem of /home/jenkins/workspace we have to make sure that all the containers are using the same runAsUser. fsGroup or supplementalGroups are not enough. 23 | #If the jenkins serviceaccount has access only to 'restricted' scc, the same random uid will be enforced on the containers, so we should not set runAsUser here. 24 | containers: 25 | #Java agent, test executor 26 | - name: jnlp 27 | image: registry.access.redhat.com/openshift3/jenkins-slave-maven-rhel7:v3.9 28 | command: 29 | - /usr/local/bin/run-jnlp-client 30 | args: 31 | - $(JENKINS_SECRET) 32 | - $(JENKINS_NAME) 33 | resources: 34 | limits: 35 | memory: 512Mi 36 | workingDir: /home/jenkins 37 | env: 38 | - name: JNLP_MAX_HEAP_UPPER_BOUND_MB 39 | value: 64 40 | #App under test 41 | - name: app-users 42 | image: 172.30.1.1:5000/myproject/app-users:latest 43 | resources: 44 | limits: 45 | memory: 512Mi 46 | env: 47 | - name: SPRING_PROFILES_ACTIVE 48 | value: k8sit 49 | - name: SPRING_CLOUD_KUBERNETES_ENABLED 50 | value: false 51 | #DB 52 | - name: mariadb 53 | image: registry.access.redhat.com/rhscl/mariadb-102-rhel7:1 54 | resources: 55 | limits: 56 | memory: 256Mi 57 | env: 58 | - name: MYSQL_USER 59 | value: myuser 60 | - name: MYSQL_PASSWORD 61 | value: mypassword 62 | - name: MYSQL_DATABASE 63 | value: testdb 64 | - name: MYSQL_ROOT_PASSWORD 65 | value: secret 66 | readinessProbe: 67 | tcpSocket: 68 | port: 3306 69 | initialDelaySeconds: 5 70 | #AMQ 71 | - name: amq 72 | image: registry.access.redhat.com/jboss-amq-6/amq63-openshift:1.3 73 | resources: 74 | limits: 75 | memory: 256Mi 76 | env: 77 | - name: AMQ_USER 78 | value: test 79 | - name: AMQ_PASSWORD 80 | value: secret 81 | readinessProbe: 82 | tcpSocket: 83 | port: 61616 84 | initialDelaySeconds: 5 85 | #External API Third party (provided by mockserver) 86 | - name: mockserver 87 | image: jamesdbloom/mockserver:mockserver-5.3.0 88 | resources: 89 | limits: 90 | memory: 256Mi 91 | env: 92 | - name: LOG_LEVEL 93 | value: INFO 94 | - name: JVM_OPTIONS 95 | value: -Xmx128m 96 | readinessProbe: 97 | tcpSocket: 98 | port: 1080 99 | initialDelaySeconds: 5 100 | ''' 101 | } 102 | } 103 | environment { 104 | //These env vars are used the tests to send message to users.in queue 105 | AMQ_USER = 'test' 106 | AMQ_PASSWORD = 'secret' 107 | } 108 | steps { 109 | dir ("integration-test") { 110 | container('mariadb') { 111 | //requires mysql 112 | sh 'sql/setup.sh' 113 | } 114 | 115 | // Default container 'jnlp' 116 | // this script requires curl and python. 117 | sh 'mockserver/setup.sh' 118 | 119 | //Run the tests. 120 | //Somehow simply "mvn ..." doesn't work here 121 | sh '/bin/bash -c "mvn -s ../configuration/settings.xml -B clean test"' 122 | } 123 | } 124 | post { 125 | always { 126 | junit testResults: 'integration-test/target/surefire-reports/*.xml', allowEmptyResults: true 127 | } 128 | } 129 | } 130 | 131 | } 132 | 133 | } 134 | -------------------------------------------------------------------------------- /Jenkinsfile-declarative-runas: -------------------------------------------------------------------------------- 1 | //Update kubernetes-plugin to 1.7 2 | pipeline { 3 | agent none //Using different agent for each stage 4 | stages { 5 | 6 | /******** 7 | * Build the jar and the docker image using the default 'maven' node 8 | ********/ 9 | stage('Build App and Image') { 10 | agent{ label 'maven' } 11 | steps { 12 | dir ("app-users") { 13 | sh "mvn -B -s ../configuration/settings.xml -DskipTests package" 14 | 15 | // Requires: minishift config set insecure-registry 172.30.0.0/16 16 | sh "oc new-build --name=app-users --docker-image=registry.access.redhat.com/fuse7/fuse-java-openshift:latest --binary=true --labels=app=app-users || true" 17 | sh 'rm -rf oc-build && mkdir -p oc-build/deployments' 18 | sh 'cp target/*.jar oc-build/deployments' 19 | 20 | script { 21 | openshift.withCluster() { 22 | openshift.withProject('myproject') { 23 | openshift.selector('bc', 'app-users').startBuild('--from-dir=oc-build', '--wait=true').logs('-f') 24 | } 25 | 26 | } 27 | } 28 | } 29 | } 30 | } 31 | 32 | 33 | /******** 34 | * Run the integration test with the custom pod defined with a yaml 35 | ********/ 36 | stage('Run integration test') { 37 | agent{ 38 | kubernetes { 39 | label 'app-users-it' 40 | cloud 'openshift' 41 | defaultContainer 'jnlp' 42 | yaml ''' 43 | apiVersion: v1 44 | kind: Pod 45 | metadata: 46 | labels: 47 | app: app-users 48 | spec: 49 | #To resolve the file permission problem of /home/jenkins/workspace we have to make sure that all the containers are using the same runAsUser. fsGroup or supplementalGroups are not enough. 50 | #If the jenkins serviceaccount has access to 'anyuid' scc, we need to set the runAsUser here to avoid having different uid in the containers. 51 | securityContext: 52 | runAsUser: 1002 53 | containers: 54 | #Java agent, test executor 55 | - name: jnlp 56 | image: registry.access.redhat.com/openshift3/jenkins-slave-maven-rhel7:v3.9 57 | command: 58 | - /usr/local/bin/run-jnlp-client 59 | args: 60 | - $(JENKINS_SECRET) 61 | - $(JENKINS_NAME) 62 | resources: 63 | limits: 64 | memory: 512Mi 65 | workingDir: /home/jenkins 66 | env: 67 | - name: JNLP_MAX_HEAP_UPPER_BOUND_MB 68 | value: 64 69 | #App under test 70 | - name: app-users 71 | image: 172.30.1.1:5000/myproject/app-users:latest 72 | resources: 73 | limits: 74 | memory: 512Mi 75 | env: 76 | - name: SPRING_PROFILES_ACTIVE 77 | value: k8sit 78 | - name: SPRING_CLOUD_KUBERNETES_ENABLED 79 | value: false 80 | #DB 81 | - name: mariadb 82 | image: registry.access.redhat.com/rhscl/mariadb-102-rhel7:1 83 | resources: 84 | limits: 85 | memory: 256Mi 86 | env: 87 | - name: MYSQL_USER 88 | value: myuser 89 | - name: MYSQL_PASSWORD 90 | value: mypassword 91 | - name: MYSQL_DATABASE 92 | value: testdb 93 | - name: MYSQL_ROOT_PASSWORD 94 | value: secret 95 | readinessProbe: 96 | tcpSocket: 97 | port: 3306 98 | initialDelaySeconds: 5 99 | #AMQ 100 | - name: amq 101 | image: registry.access.redhat.com/jboss-amq-6/amq63-openshift:1.3 102 | resources: 103 | limits: 104 | memory: 256Mi 105 | env: 106 | - name: AMQ_USER 107 | value: test 108 | - name: AMQ_PASSWORD 109 | value: secret 110 | readinessProbe: 111 | tcpSocket: 112 | port: 61616 113 | initialDelaySeconds: 5 114 | #External API Third party (provided by mockserver) 115 | - name: mockserver 116 | image: jamesdbloom/mockserver:mockserver-5.3.0 117 | resources: 118 | limits: 119 | memory: 256Mi 120 | env: 121 | - name: LOG_LEVEL 122 | value: INFO 123 | - name: JVM_OPTIONS 124 | value: -Xmx128m 125 | readinessProbe: 126 | tcpSocket: 127 | port: 1080 128 | initialDelaySeconds: 5 129 | ''' 130 | } 131 | } 132 | environment { 133 | //These env vars are used the tests to send message to users.in queue 134 | AMQ_USER = 'test' 135 | AMQ_PASSWORD = 'secret' 136 | } 137 | steps { 138 | dir ("integration-test") { 139 | container('mariadb') { 140 | //requires mysql 141 | sh 'sql/setup.sh' 142 | } 143 | 144 | // Default container 'jnlp' 145 | // this script requires curl and python. 146 | sh 'mockserver/setup.sh' 147 | 148 | //Run the tests. 149 | //Somehow simply "mvn ..." doesn't work here 150 | sh '/bin/bash -c "mvn -s ../configuration/settings.xml -B clean test"' 151 | } 152 | } 153 | post { 154 | always { 155 | junit testResults: 'integration-test/target/surefire-reports/*.xml', allowEmptyResults: true 156 | } 157 | } 158 | } 159 | 160 | } 161 | 162 | } 163 | -------------------------------------------------------------------------------- /Jenkinsfile-jnlp-base: -------------------------------------------------------------------------------- 1 | podTemplate( 2 | label: 'app-users-it', 3 | cloud: 'openshift', 4 | containers: [ 5 | //Jenkins agent - a jnlp container is mandatory (or added automatically) 6 | containerTemplate(name: 'jnlp', 7 | image: 'registry.access.redhat.com/openshift3/jenkins-slave-base-rhel7:v3.9', 8 | resourceLimitMemory: '256Mi', 9 | command: '/bin/sh -c', 10 | args: '"umask 0000; /usr/local/bin/run-jnlp-client ${computer.jnlpmac} ${computer.name}"' 11 | ), 12 | //Container running the test 13 | containerTemplate(name: 'it', 14 | image: 'fabric8/maven-builder:vd8fbad4', 15 | resourceLimitMemory: '512Mi', 16 | envVars: [ 17 | //Manage heap, there are two java processes: maven + surefire 18 | envVar(key: 'JAVA_TOOL_OPTIONS', value: '-Xms128m -Xmx128m') 19 | ]), 20 | //App under test 21 | containerTemplate(name: 'app-users', 22 | image: '172.30.1.1:5000/myproject/app-users:latest', 23 | resourceLimitMemory: '512Mi', 24 | envVars: [ 25 | envVar(key: 'SPRING_PROFILES_ACTIVE', value: 'k8sit'), 26 | envVar(key: 'SPRING_CLOUD_KUBERNETES_ENABLED', value: 'false') 27 | ]), 28 | //DB 29 | containerTemplate(name: 'mariadb', 30 | image: 'registry.access.redhat.com/rhscl/mariadb-102-rhel7:1', 31 | resourceLimitMemory: '256Mi', 32 | envVars: [ 33 | envVar(key: 'MYSQL_USER', value: 'myuser'), 34 | envVar(key: 'MYSQL_PASSWORD', value: 'mypassword'), 35 | envVar(key: 'MYSQL_DATABASE', value: 'testdb'), 36 | envVar(key: 'MYSQL_ROOT_PASSWORD', value: 'secret') 37 | ]), 38 | //AMQ 39 | containerTemplate(name: 'amq', 40 | image: 'registry.access.redhat.com/jboss-amq-6/amq63-openshift:1.3', 41 | resourceLimitMemory: '256Mi', 42 | envVars: [ 43 | envVar(key: 'AMQ_USER', value: 'test'), 44 | envVar(key: 'AMQ_PASSWORD', value: 'secret') 45 | ]), 46 | //External API Third party (provided by mockserver) 47 | containerTemplate(name: 'mockserver', 48 | image: 'jamesdbloom/mockserver:mockserver-5.3.0', 49 | resourceLimitMemory: '256Mi', 50 | envVars: [ 51 | envVar(key: 'LOG_LEVEL', value: 'INFO'), 52 | envVar(key: 'JVM_OPTIONS', value: '-Xmx128m'), 53 | ]) 54 | ] 55 | ) 56 | { 57 | node('app-users-it') { 58 | stage('Pull source') { 59 | checkout scm 60 | } 61 | dir ("integration-test") { 62 | stage('Prepare test') { 63 | container('mariadb') { 64 | //requires mysql 65 | sh 'sql/setup.sh' 66 | } 67 | //requires curl and python 68 | sh 'mockserver/setup.sh' 69 | } 70 | 71 | //These env vars are used the tests to send message to users.in queue 72 | withEnv(['AMQ_USER=test', 73 | 'AMQ_PASSWORD=secret']) { 74 | stage('Build and run test') { 75 | container('it'){ 76 | try { 77 | sh 'mvn -s ../configuration/settings.xml -B clean test' 78 | } finally { 79 | junit 'target/surefire-reports/*.xml' 80 | } 81 | } 82 | } 83 | } 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /Jenkinsfile-mavenlocalrepo: -------------------------------------------------------------------------------- 1 | podTemplate( 2 | name: 'app-users-it', 3 | label: 'app-users-it', 4 | cloud: 'openshift', 5 | containers: [ 6 | //Java agent, test executor 7 | containerTemplate(name: 'jnlp', 8 | image: 'registry.access.redhat.com/openshift3/jenkins-agent-maven-35-rhel7:v3.10', 9 | resourceLimitMemory: '512Mi', 10 | workingDir: '/home/jenkins', 11 | command: '/bin/sh -c', 12 | args: '"umask 0000; /usr/local/bin/run-jnlp-client ${computer.jnlpmac} ${computer.name}"', 13 | alwaysPullImage: false, 14 | envVars: [ 15 | //Heap for jnlp is 1/2, for mvn and surefire process is 1/4 of resourceLimitMemory by default 16 | envVar(key: 'JNLP_MAX_HEAP_UPPER_BOUND_MB', value: '64') 17 | ]), 18 | //App under test 19 | containerTemplate(name: 'app-users', 20 | image: '172.30.1.1:5000/myproject/app-users:latest', 21 | resourceLimitMemory: '512Mi', 22 | alwaysPullImage: true, 23 | envVars: [ 24 | envVar(key: 'SPRING_PROFILES_ACTIVE', value: 'k8sit'), 25 | envVar(key: 'SPRING_CLOUD_KUBERNETES_ENABLED', value: 'false') 26 | ]), 27 | //DB 28 | containerTemplate(name: 'mariadb', 29 | image: 'registry.access.redhat.com/rhscl/mariadb-102-rhel7:1', 30 | resourceLimitMemory: '256Mi', 31 | alwaysPullImage: false, 32 | envVars: [ 33 | envVar(key: 'MYSQL_USER', value: 'myuser'), 34 | envVar(key: 'MYSQL_PASSWORD', value: 'mypassword'), 35 | envVar(key: 'MYSQL_DATABASE', value: 'testdb'), 36 | envVar(key: 'MYSQL_ROOT_PASSWORD', value: 'secret') 37 | ]), 38 | //AMQ 39 | containerTemplate(name: 'amq', 40 | image: 'registry.access.redhat.com/jboss-amq-6/amq63-openshift:1.3', 41 | resourceLimitMemory: '256Mi', 42 | alwaysPullImage: false, 43 | envVars: [ 44 | envVar(key: 'AMQ_USER', value: 'test'), 45 | envVar(key: 'AMQ_PASSWORD', value: 'secret') 46 | ]), 47 | //External API Third party (provided by mockserver) 48 | containerTemplate(name: 'mockserver', 49 | image: 'jamesdbloom/mockserver:mockserver-5.3.0', 50 | resourceLimitMemory: '256Mi', 51 | alwaysPullImage: false, 52 | envVars: [ 53 | envVar(key: 'LOG_LEVEL', value: 'INFO'), 54 | envVar(key: 'JVM_OPTIONS', value: '-Xmx128m'), 55 | ]) 56 | ], 57 | volumes:[ 58 | persistentVolumeClaim(mountPath: '/home/jenkins/.m2/repository', claimName:'mavenlocalrepo') //mounted as root drwxrwxrwx 59 | ], 60 | envVars: [ 61 | envVar(key: 'ENV_NAME', value: 'my-k8sit') 62 | ] 63 | ) 64 | { 65 | node('maven'){ 66 | stage('Pull source') { 67 | checkout scm //git url: 'https://github.com/bszeti/kubernetes-integration-test.git' 68 | } 69 | 70 | dir ("app-users") { 71 | stage('Build app') { 72 | sh "mvn -B -s ../configuration/settings.xml -DskipTests package" 73 | } 74 | 75 | stage('Build Image') { 76 | // Requires: minishift config set insecure-registry 172.30.0.0/16 77 | sh "oc new-build --name=app-users --docker-image=registry.access.redhat.com/fuse7/fuse-java-openshift:latest --binary=true --labels=app=app-users || true" 78 | sh 'rm -rf oc-build && mkdir -p oc-build/deployments' 79 | sh 'cp target/*.jar oc-build/deployments' 80 | 81 | openshift.withCluster() { 82 | openshift.withProject('myproject') { 83 | openshift.selector('bc', 'app-users').startBuild('--from-dir=oc-build', '--wait=true').logs('-f') 84 | } 85 | 86 | } 87 | } 88 | } 89 | 90 | } 91 | 92 | node('app-users-it') { 93 | stage('Pull source') { 94 | checkout scm 95 | } 96 | dir ("integration-test") { 97 | stage('Prepare test') { 98 | container('mariadb') { 99 | //requires mysql 100 | sh 'sql/setup.sh' 101 | } 102 | container('jnlp') { 103 | //requires curl and python 104 | sh 'mockserver/setup.sh' 105 | } 106 | } 107 | 108 | //These env vars are used the tests to send message to users.in queue 109 | withEnv(['AMQ_USER=test', 110 | 'AMQ_PASSWORD=secret']) { 111 | stage('Build and run test') { 112 | try { 113 | sh 'mvn -s ../configuration/settings.xml -B clean test' 114 | } finally { 115 | junit 'target/surefire-reports/*.xml' 116 | } 117 | } 118 | } 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /Jenkinsfile-yaml: -------------------------------------------------------------------------------- 1 | //Update kubernetes-plugin to 1.7 2 | node('master'){ 3 | checkout scm 4 | yamlFromFile = readFile 'pod.yaml' //Read yaml file 5 | } 6 | 7 | podTemplate( 8 | name: 'app-users-it', 9 | label: 'app-users-it', 10 | cloud: 'openshift', 11 | yaml: yamlFromFile //Use the yaml read from file 12 | ) 13 | { 14 | node('maven'){ 15 | stage('Pull source') { 16 | checkout scm //git url: 'https://github.com/bszeti/kubernetes-integration-test.git' 17 | } 18 | 19 | dir ("app-users") { 20 | stage('Build app') { 21 | sh "mvn -B -s ../configuration/settings.xml -DskipTests package" 22 | } 23 | 24 | stage('Build Image') { 25 | // Requires: minishift config set insecure-registry 172.30.0.0/16 26 | sh "oc new-build --name=app-users --docker-image=registry.access.redhat.com/fuse7/fuse-java-openshift:latest --binary=true --labels=app=app-users || true" 27 | sh 'rm -rf oc-build && mkdir -p oc-build/deployments' 28 | sh 'cp target/*.jar oc-build/deployments' 29 | 30 | openshift.withCluster() { 31 | openshift.withProject('myproject') { 32 | openshift.selector('bc', 'app-users').startBuild('--from-dir=oc-build', '--wait=true').logs('-f') 33 | } 34 | 35 | } 36 | } 37 | } 38 | 39 | } 40 | 41 | node('app-users-it') { 42 | stage('Pull source') { 43 | checkout scm 44 | } 45 | dir ("integration-test") { 46 | stage('Prepare test') { 47 | container('mariadb') { 48 | //requires mysql 49 | sh 'sql/setup.sh' 50 | } 51 | container('jnlp') { 52 | //requires curl and python 53 | sh 'mockserver/setup.sh' 54 | } 55 | } 56 | 57 | withEnv(['AMQ_USER=test', 58 | 'AMQ_PASSWORD=secret']) { 59 | stage('Build and run test') { 60 | try { 61 | sh 'mvn -s ../configuration/settings.xml -B clean test' 62 | } finally { 63 | junit 'target/surefire-reports/*.xml' 64 | } 65 | } 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Running integration tests with Kubernetes 2 | 3 | Integration test on Kubernetes with Jenkins pipeline 4 | 5 | Blog post: https://itnext.io/running-integration-tests-with-kubernetes-ae0fb71e207b 6 | 7 | ### Directories: 8 | * **app-users**: Example Red Hat Fuse 7 application that takes messages from AMQ, makes database queries and calls a REST api 9 | * **configuration/settings.xml**: Maven settings.xml with the requires repos to build 10 | * **integration-test**: Tests written in Java with Junit, can be executed with Maven 11 | * **arquillian-test**: Test with Arquillian Cube on OpenShift 12 | 13 | ### Jenkins pipelines: 14 | * **Jenkinsfile**: Builds app and executes integration test. 15 | * **Jenkinsfile-basic**: Executes integration test only. Setting only the important parameters. 16 | * **Jenkinsfile-declarative**: Using declarative syntax. Setting a different agent at stage level. Builds app and executes integration test. 17 | * **Jenkinsfile-declarative-one-agent**: Using declarative syntax. Agent is defined at the pipeline level. Executes integration test only. 18 | * **Jenkinsfile-declarative-runas**: Using declarative syntax. Pod sets runAsUser to make sure all containers are started with the same uid. No "umask 0000" is needed in the command. Requires 'anyuid' scc. 19 | * **Jenkinsfile-declarative-restricted**: Using declarative syntax. Only works if 'restricted' scc is being used, so the same random uid is set for all containers. No "umask 0000" is needed in the command. 20 | * **Jenkinsfile-jnlp-base**: Using separate container for jnlp and maven. Executes integration test only. 21 | * **Jenkinsfile-mavenlocalrepo**: Mounting a persisted volume as maven local repository to avoid downloading jars every time. Requires a 'mavenlocalrepo' persistent volume claim. Builds app and executes integration test. 22 | * **Jenkinsfile-yaml**: Reads yaml pod template from 'pod.yaml'. Builds app and executes integration test. 23 | * **Jenkinsfile-arquillian**: Run the arquillian-test integration test project. See README.md in its directory. 24 | 25 | ### App-users 26 | A Spring Boot application running a Camel route based on the Red Hat Fuse 7 stack. 27 | 28 | Steps: 29 | * Take messages from _user.in_ queue. Json :`{"email": "me@mycompany.com"}` 30 | * Query phone number from table _users_ 31 | * Call a REST api to get address. E.g. https://myhost/v1/address/email/me@mycompany.com 32 | * Send the enriched user info to queue _user.out_ 33 | 34 | Build and run unit tests with Maven: 35 | * Use the provided settings.xml to access the required Maven repositories: 36 | 37 | `mvn -s ../configuration/settings.xml clean package` 38 | 39 | ### Integration-test 40 | 41 | Tests written in Java using Junit5. Run with Maven: `mvn clean test` 42 | 43 | Env variables required: 44 | * AMQ_USER: AMQ connection username 45 | * AMQ_PASSWORD: AMQ connection password 46 | 47 | Test cases: 48 | * testSucc: Send message to _user.in_. Expect enriched message on _user.out_. 49 | * testRetry: Test AMQ transacted. REST api throws http 500 3x times before sending response. Send message to _user.in_. Expect enriched message on _user.out_. 50 | 51 | ### Arquillian-test 52 | 53 | Arquillian Cube based test. See README.md in its directory. 54 | -------------------------------------------------------------------------------- /app-users/README.md: -------------------------------------------------------------------------------- 1 | ### Building 2 | 3 | The example can be built with 4 | 5 | mvn clean install 6 | 7 | ### Running on localhost 8 | 9 | mvn spring-boot:run 10 | 11 | ### Running the example in OpenShift 12 | 13 | The example can then be built and deployed using a single goal: 14 | 15 | $ mvn fabric8:deploy 16 | 17 | ### Accessing the Spring REST Actuator services 18 | 19 | curl http://localhost:8080/health 20 | -------------------------------------------------------------------------------- /app-users/application-local.properties: -------------------------------------------------------------------------------- 1 | endpoints.enabled=true 2 | 3 | logging.level.com.mycompany=DEBUG 4 | 5 | amq.brokerUrl=tcp://localhost:61616 6 | amq.username=test 7 | amq.password=secret 8 | 9 | spring.datasource.url=jdbc:mariadb://127.0.0.1/testdb 10 | spring.datasource.username=myuser 11 | spring.datasource.password=mypassword 12 | spring.datasource.driver-class-name=org.mariadb.jdbc.Driver 13 | 14 | api.url=http://localhost:1080/v1/ 15 | -------------------------------------------------------------------------------- /app-users/bootstrap.properties: -------------------------------------------------------------------------------- 1 | # Disable OpenShift integration for unit tests 2 | spring.cloud.kubernetes.enabled=false -------------------------------------------------------------------------------- /app-users/configuration/settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | redhat-repos 6 | 7 | true 8 | 9 | 10 | 11 | central 12 | http://repo1.maven.org/maven2 13 | 14 | true 15 | 16 | 17 | false 18 | 19 | 20 | 21 | redhatga 22 | Red Hat 23 | http://maven.repository.redhat.com/ga 24 | 25 | 26 | redhatea 27 | Red Hat Early Access 28 | http://maven.repository.redhat.com/earlyaccess/all 29 | 30 | 31 | 32 | 33 | 34 | central 35 | http://repo1.maven.org/maven2 36 | 37 | true 38 | 39 | 40 | false 41 | 42 | 43 | 44 | redhatga 45 | Red Hat 46 | http://maven.repository.redhat.com/ga 47 | 48 | 49 | redhatea 50 | Red Hat Early Access 51 | http://maven.repository.redhat.com/earlyaccess/all 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /app-users/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | com.mycompany.k8sit 6 | app-users 7 | 1.0-SNAPSHOT 8 | Example :: Integration test on Kubernetes with Jenkins pipeline :: App Users 9 | 10 | 11 | UTF-8 12 | 13 | 14 | 7.0.0.fuse-000027-redhat-1 15 | 2.2.5 16 | 17 | 18 | 0.2.0.RELEASE 19 | 20 | 21 | 3.7.0 22 | 2.21.0 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | org.jboss.redhat-fuse 32 | fuse-springboot-bom 33 | ${fuse.bom.version} 34 | pom 35 | import 36 | 37 | 38 | 39 | org.mariadb.jdbc 40 | mariadb-java-client 41 | ${mariadb-java-client.version} 42 | 43 | 44 | 45 | org.springframework.cloud 46 | spring-cloud-starter-kubernetes-config 47 | ${spring-cloud-kubernetes.version} 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | org.springframework.boot 57 | spring-boot-starter-web 58 | 59 | 60 | org.springframework.boot 61 | spring-boot-starter-tomcat 62 | 63 | 64 | 65 | 66 | org.springframework.boot 67 | spring-boot-starter-undertow 68 | 69 | 70 | org.springframework.boot 71 | spring-boot-starter-actuator 72 | 73 | 74 | org.springframework.boot 75 | spring-boot-starter-jdbc 76 | 77 | 78 | 79 | 80 | org.apache.camel 81 | camel-metrics-starter 82 | 83 | 84 | org.apache.camel 85 | camel-http4-starter 86 | 87 | 88 | org.apache.camel 89 | camel-jackson-starter 90 | 91 | 92 | org.fusesource 93 | camel-activemq 94 | 95 | 96 | org.apache.camel 97 | camel-sjms-starter 98 | 99 | 100 | org.apache.camel 101 | camel-bean-validator 102 | 103 | 104 | org.apache.camel 105 | camel-sql-starter 106 | 107 | 108 | 109 | org.mariadb.jdbc 110 | mariadb-java-client 111 | 112 | 113 | 114 | 115 | 116 | org.springframework.cloud 117 | spring-cloud-starter-kubernetes-config 118 | 119 | 120 | 121 | 122 | org.springframework.boot 123 | spring-boot-starter-test 124 | test 125 | 126 | 127 | org.apache.camel 128 | camel-test-spring 129 | test 130 | 131 | 132 | org.apache.camel 133 | camel-servlet-starter 134 | test 135 | 136 | 137 | org.hsqldb 138 | hsqldb 139 | test 140 | 141 | 142 | org.apache.camel 143 | camel-jsonpath 144 | test 145 | 146 | 147 | 148 | 149 | 150 | spring-boot:run 151 | 152 | 153 | 154 | maven-surefire-plugin 155 | ${maven-surefire-plugin.version} 156 | 157 | 158 | maven-compiler-plugin 159 | ${maven-compiler-plugin.version} 160 | 161 | 162 | org.jboss.redhat-fuse 163 | spring-boot-maven-plugin 164 | ${fuse.bom.version} 165 | 166 | 167 | 168 | 169 | 170 | 171 | maven-compiler-plugin 172 | 173 | 1.8 174 | 1.8 175 | 176 | 177 | 178 | 179 | org.jboss.redhat-fuse 180 | spring-boot-maven-plugin 181 | 182 | 183 | 184 | repackage 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | -------------------------------------------------------------------------------- /app-users/run.sh: -------------------------------------------------------------------------------- 1 | #sudo docker run --name mockserver -d -p 1080:1080 docker.io/jamesdbloom/mockserver 2 | #sudo docker run --name mariadb -e MYSQL_USER=myuser -e MYSQL_PASSWORD=mypassword -e MYSQL_ROOT_PASSWORD=secret -e MYSQL_DATABASE=testdb -d -p 3306:3306 registry.access.redhat.com/rhscl/mariadb-102-rhel7 3 | #sudo docker run --name amq -e AMQ_USER=test -e AMQ_PASSWORD=secret -d -p 61616:61616 -p 8181:8181 registry.access.redhat.com/jboss-amq-6/amq63-openshift 4 | 5 | mvn -Dspring.profiles.active=local clean spring-boot:run 6 | -------------------------------------------------------------------------------- /app-users/src/main/java/com/mycompany/k8sit/Application.java: -------------------------------------------------------------------------------- 1 | package com.mycompany.k8sit; 2 | 3 | import org.apache.camel.CamelContext; 4 | import org.apache.camel.component.metrics.routepolicy.MetricsRoutePolicyFactory; 5 | import org.apache.camel.spring.boot.CamelContextConfiguration; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.boot.SpringApplication; 9 | import org.springframework.boot.autoconfigure.SpringBootApplication; 10 | import org.springframework.context.annotation.Bean; 11 | import org.springframework.context.annotation.ImportResource; 12 | 13 | @SpringBootApplication 14 | @ImportResource({"classpath:spring/application-context.xml"}) 15 | public class Application { 16 | private static final Logger log = LoggerFactory.getLogger(Application.class); 17 | 18 | public static void main(String[] args) { 19 | SpringApplication.run(Application.class, args); 20 | } 21 | 22 | /*** 23 | * Customize CamelContext 24 | */ 25 | @Bean 26 | CamelContextConfiguration contextConfiguration() { 27 | return new CamelContextConfiguration() { 28 | @Override 29 | public void beforeApplicationStart(CamelContext context) { 30 | //Enable metrics for all routes 31 | context.addRoutePolicyFactory(new MetricsRoutePolicyFactory()); 32 | } 33 | 34 | @Override 35 | public void afterApplicationStart(CamelContext context) { 36 | } 37 | }; 38 | } 39 | 40 | 41 | } 42 | -------------------------------------------------------------------------------- /app-users/src/main/java/com/mycompany/k8sit/model/Address.java: -------------------------------------------------------------------------------- 1 | package com.mycompany.k8sit.model; 2 | 3 | public class Address { 4 | private String state; 5 | private String city; 6 | private String address; 7 | private String zip; 8 | 9 | public String getState() { 10 | return state; 11 | } 12 | 13 | public void setState(String state) { 14 | this.state = state; 15 | } 16 | 17 | public String getCity() { 18 | return city; 19 | } 20 | 21 | public void setCity(String city) { 22 | this.city = city; 23 | } 24 | 25 | public String getZip() { 26 | return zip; 27 | } 28 | 29 | public void setZip(String zip) { 30 | this.zip = zip; 31 | } 32 | 33 | public String getAddress() { 34 | return address; 35 | } 36 | 37 | public void setAddress(String address) { 38 | this.address = address; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app-users/src/main/java/com/mycompany/k8sit/model/User.java: -------------------------------------------------------------------------------- 1 | package com.mycompany.k8sit.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import org.hibernate.validator.constraints.Email; 5 | 6 | import javax.validation.constraints.NotNull; 7 | 8 | @JsonIgnoreProperties(ignoreUnknown = true) 9 | public class User { 10 | @NotNull 11 | @Email 12 | private String email; 13 | 14 | private String phone; 15 | 16 | private Address address; 17 | 18 | public String getEmail() { 19 | return email; 20 | } 21 | 22 | public void setEmail(String email) { 23 | this.email = email; 24 | } 25 | 26 | public String getPhone() { 27 | return phone; 28 | } 29 | 30 | public void setPhone(String phone) { 31 | this.phone = phone; 32 | } 33 | 34 | public Address getAddress() { 35 | return address; 36 | } 37 | 38 | public void setAddress(Address address) { 39 | this.address = address; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app-users/src/main/java/com/mycompany/k8sit/route/UserRoute.java: -------------------------------------------------------------------------------- 1 | package com.mycompany.k8sit.route; 2 | 3 | import com.mycompany.k8sit.model.Address; 4 | import com.mycompany.k8sit.model.User; 5 | import org.apache.camel.Exchange; 6 | import org.apache.camel.LoggingLevel; 7 | import org.apache.camel.builder.RouteBuilder; 8 | import org.apache.camel.model.dataformat.JsonLibrary; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | import org.springframework.stereotype.Component; 12 | 13 | @Component 14 | public class UserRoute extends RouteBuilder { 15 | private static final Logger log = LoggerFactory.getLogger(UserRoute.class); 16 | 17 | @Override 18 | public void configure() throws Exception { 19 | 20 | //There is no Camel retry enabled by default, but the endpoint is transacted, so redelivery is triggered by AMQ 21 | //Try transacted=false&synchronous=false to fail the testRetry test 22 | from("amq:user.in?consumerCount={{amq.consumerCount}}&transacted=true").routeId("user.in") 23 | .streamCaching() 24 | 25 | //Receive user email from body 26 | .log(LoggingLevel.DEBUG,log,"User received: ${body}") 27 | .unmarshal().json(JsonLibrary.Jackson,User.class) 28 | .to("bean-validator:user-in") 29 | 30 | //We populate this object during the route 31 | .setProperty("user",body()) 32 | 33 | //Select phone number from database 34 | .to("sql:SELECT phone FROM users WHERE email=:#${exchangeProperty.user.email}?dataSource=#dataSource&outputType=SelectOne&outputHeader=phone") 35 | .script().simple("${exchangeProperty.user.setPhone(${header.phone})}") 36 | 37 | //Call api for address 38 | .removeHeaders("*") 39 | .setBody(constant(null)) 40 | .setHeader(Exchange.HTTP_URI).constant("{{api.url}}") 41 | .setHeader(Exchange.HTTP_PATH).simple("address/email/${exchangeProperty.user.email}") 42 | .setHeader("CamelHttpMethod").constant("GET") 43 | .setHeader("Accept").simple("application/json") 44 | .to("http4:apiCall?synchronous=true") 45 | .log(LoggingLevel.DEBUG,log,"Address received: ${body}") 46 | .unmarshal().json(JsonLibrary.Jackson,Address.class) 47 | .script().simple("${exchangeProperty.user.setAddress(${body})}") 48 | 49 | 50 | //Send user with added fields to out queue 51 | .setBody(exchangeProperty("user")) 52 | .marshal().json(JsonLibrary.Jackson) 53 | .convertBodyTo(String.class) 54 | 55 | .removeHeaders("*") 56 | .log(LoggingLevel.DEBUG,log,"Send user: ${body}") 57 | .to("amq:user.out?prefillPool=false") 58 | ; 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /app-users/src/main/resources/application-arquillian.properties: -------------------------------------------------------------------------------- 1 | endpoints.enabled=true 2 | management.security.enabled=false 3 | 4 | logging.level.com.mycompany=DEBUG 5 | 6 | amq.brokerUrl=tcp://amqsvc:61616 7 | amq.username=test 8 | amq.password=secret 9 | 10 | spring.datasource.url=jdbc:mariadb://mariadbsvc/testdb 11 | spring.datasource.username=myuser 12 | spring.datasource.password=mypassword 13 | spring.datasource.driver-class-name=org.mariadb.jdbc.Driver 14 | 15 | api.url=http://mockserversvc/v1/ 16 | -------------------------------------------------------------------------------- /app-users/src/main/resources/application-k8sit.properties: -------------------------------------------------------------------------------- 1 | endpoints.enabled=true 2 | 3 | logging.level.com.mycompany=DEBUG 4 | 5 | amq.brokerUrl=tcp://localhost:61616 6 | amq.username=test 7 | amq.password=secret 8 | 9 | spring.datasource.url=jdbc:mariadb://127.0.0.1/testdb 10 | spring.datasource.username=myuser 11 | spring.datasource.password=mypassword 12 | spring.datasource.driver-class-name=org.mariadb.jdbc.Driver 13 | 14 | api.url=http://localhost:1080/v1/ 15 | -------------------------------------------------------------------------------- /app-users/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | amq.brokerUrl=tcp://localhost:61616 2 | amq.username=admin 3 | amq.password=admin 4 | amq.consumerCount=1 5 | amq.connectionCount=1 6 | 7 | api.url=https://service.mycompany.com/v1/ -------------------------------------------------------------------------------- /app-users/src/main/resources/bootstrap.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=app-users 2 | -------------------------------------------------------------------------------- /app-users/src/main/resources/spring/application-context.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app-users/src/test/java/com/mycompany/k8sit/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.mycompany.k8sit; 2 | 3 | import com.jayway.jsonpath.JsonPath; 4 | import org.apache.camel.*; 5 | import org.apache.camel.builder.AdviceWithRouteBuilder; 6 | import org.apache.camel.builder.RouteBuilder; 7 | import org.apache.camel.component.mock.MockEndpoint; 8 | import org.apache.camel.model.rest.RestBindingMode; 9 | import org.apache.camel.test.spring.CamelSpringBootRunner; 10 | import org.apache.camel.test.spring.MockEndpointsAndSkip; 11 | import org.apache.camel.test.spring.UseAdviceWith; 12 | import org.junit.Before; 13 | import org.junit.Test; 14 | import org.junit.runner.RunWith; 15 | import org.slf4j.Logger; 16 | import org.slf4j.LoggerFactory; 17 | import org.springframework.beans.factory.annotation.Autowired; 18 | import org.springframework.boot.test.context.SpringBootTest; 19 | import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; 20 | import org.springframework.boot.test.context.TestConfiguration; 21 | import org.springframework.context.annotation.Bean; 22 | import org.springframework.http.MediaType; 23 | import org.springframework.test.annotation.DirtiesContext; 24 | import org.springframework.test.context.ActiveProfiles; 25 | 26 | import static org.junit.Assert.assertEquals; 27 | 28 | @RunWith(CamelSpringBootRunner.class) 29 | @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) 30 | @ActiveProfiles("unittest") 31 | @UseAdviceWith 32 | @MockEndpointsAndSkip("amq:*") 33 | @DirtiesContext 34 | public class ApplicationTest { 35 | private static final Logger log = LoggerFactory.getLogger(ApplicationTest.class); 36 | 37 | @Autowired 38 | private CamelContext context; 39 | 40 | @Produce(uri = "direct:user.in") 41 | private ProducerTemplate producer; 42 | 43 | @EndpointInject(uri = "mock:amq:user.out") 44 | private MockEndpoint mockUserOut; 45 | 46 | @EndpointInject(uri = "mock:addressByEmail") 47 | private MockEndpoint addressByEmail; 48 | 49 | 50 | 51 | @TestConfiguration 52 | static class TestSpringConfiguration { 53 | 54 | @Bean 55 | public RouteBuilder addTestHttpService() { 56 | return new RouteBuilder() { 57 | @Override 58 | public void configure() throws Exception { 59 | //See ServletMappingConfiguration mapping properties: camel.component.servlet.mapping 60 | restConfiguration().component("servlet") //Requires "CamelServlet" to be registered 61 | .bindingMode(RestBindingMode.off); 62 | 63 | rest("address") 64 | .produces(MediaType.APPLICATION_JSON_VALUE) 65 | .get("email/{email}") 66 | .route().routeId("test-http") 67 | .setBody().constant("{\"address\":\"1428 Elm Street\"}") 68 | .to("mock:addressByEmail") 69 | .endRest(); 70 | } 71 | }; 72 | } 73 | } 74 | 75 | 76 | @Before 77 | public void before() throws Exception{ 78 | 79 | if (context.getStatus()==ServiceStatus.Stopped) { 80 | //Execute adviseWith only once 81 | context.getRouteDefinition("user.in").adviceWith(context, new AdviceWithRouteBuilder() { 82 | @Override 83 | public void configure() throws Exception { 84 | replaceFromWith("direct:user.in"); 85 | } 86 | }); 87 | 88 | 89 | } 90 | 91 | //Reset mock endpoint 92 | mockUserOut.reset(); 93 | addressByEmail.reset(); 94 | } 95 | 96 | @Test 97 | public void testSucc() throws Exception{ 98 | context.start(); 99 | mockUserOut.expectedMessageCount(1); 100 | 101 | producer.sendBody("{\"email\":\"testSucc@test.com\"}"); 102 | 103 | mockUserOut.assertIsSatisfied(); 104 | Exchange sentToSync = mockUserOut.getExchanges().get(0); 105 | String response = sentToSync.getIn().getBody(String.class); 106 | 107 | assertEquals("testSucc@test.com",JsonPath.read(response,"$.email")); 108 | assertEquals("5551234567",JsonPath.read(response,"$.phone")); 109 | assertEquals("1428 Elm Street",JsonPath.read(response,"$.address.address")); 110 | 111 | } 112 | 113 | 114 | } 115 | -------------------------------------------------------------------------------- /app-users/src/test/resources/application-unittest.properties: -------------------------------------------------------------------------------- 1 | camel.springboot.name=CamelTest 2 | 3 | # disable all management enpoints except health 4 | endpoints.enabled=false 5 | 6 | logging.level.com.mycompany=DEBUG 7 | 8 | camel.component.servlet.mapping.context-path=/test/* 9 | 10 | #Enforce an embedded (hsql) database for unit tests 11 | spring.datasource.url= 12 | spring.datasource.driver-class-name= 13 | #spring.datasource.schema=classpath:sql/schema.sql 14 | #spring.datasource.data=classpath:sql/data.sql 15 | 16 | #We mock the component, so don't want to fill the pool 17 | amq.connectionCount=0 18 | 19 | api.url=http://localhost:{{local.server.port}}/test/ -------------------------------------------------------------------------------- /app-users/src/test/resources/bootstrap.properties: -------------------------------------------------------------------------------- 1 | # Disable OpenShift integration for unit tests 2 | spring.cloud.kubernetes.enabled=false -------------------------------------------------------------------------------- /app-users/src/test/resources/data.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO users (email, phone) VALUES ('testSucc@test.com', '5551234567'); -------------------------------------------------------------------------------- /app-users/src/test/resources/schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE users ( 2 | email VARCHAR(256), 3 | phone VARCHAR(64), 4 | PRIMARY KEY (email) 5 | ); -------------------------------------------------------------------------------- /arquillian-test/README.md: -------------------------------------------------------------------------------- 1 | # Integration tests with Arquillian Cube on OpenShift 2 | 3 | Arquillian Cube test project that creates a new temporary namespace with all the required containers in OpenShift. The test itself is being run outside this temporary namespace from the Jenkins' namespace (or even outside OpenShift). This was tested on Minishift with Jenkins deployed by the _jenkins-persistent_ template in the default _myproject_ namespace. 4 | 5 | ### Prepare run 6 | Preparing the right permissions is required to run this test project. 7 | * **image-puller**: The _app-users_ image should exist and be accessible from the temporary namespace. The _default_ serviceaccount should have _image-puller_ role, but we don't know the random name of the namespace in advance. Easiest is to grant permission to all service accounts from the build namespace _myproject_: 8 | 9 | `oc policy add-role-to-group system:image-puller system:serviceaccounts -n myproject` 10 | 11 | * **serviceaccount:arquillian**: Create SA that will run the test: 12 | 13 | `oc create sa arquillian -n myproject` 14 | 15 | Grant permission to create new projects: 16 | 17 | `oc adm policy add-cluster-role-to-user self-provisioner system:serviceaccount:myproject:arquillian` 18 | 19 | * **arquillian PodTemplate**: Copy the existing _maven_ PodTemplate in Jenkins Kubernetes plugin config. Use label _arquillian_ and set it to use service account _arquillian_. See _podtemplate-arquillian.xml_. 20 | 21 | After these steps a Jenkins pipeline project can be created running the _Jenkins-arquillian_ pipeline (see parent directory). 22 | 23 | ### Steps: 24 | These are the important steps of the Arquillian Cube test execution: 25 | * Create temporary namespace with random name (e.g. 'arquillian-frp71'). Prefix is set in _namespace.prefix_ property. 26 | * Run an optional script for preparation steps. In this test we need to add _view_ role for the _default_ sa in the temporary namespace to support using configMaps. See _env.setup.script.url_ property. 27 | * Apply the yaml file to create resources in the temporary namespace. See _env.config.resource.name_ property. 28 | * Populate components (database, mockserver) with initial data. See _ArquillianTest.before()_. 29 | * Run tests 30 | * Shutdown and delete the whole temporary namespace. 31 | 32 | ### Tests in ArquillianTest.class: 33 | * **testSucc**: Happy path. Send a json message to _user.in_ and expecting the enriched json on _user.out_. 34 | * **shutDownMariadb**: Tolerate temporary database issues. Delete _mariadb_ pod, send the test message then recreate _mariadb_ pod. The test message is automatically retried until the database is back online. 35 | -------------------------------------------------------------------------------- /arquillian-test/podtemplate-arquillian.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | arquillian 4 | 5 | false 6 | false 7 | 2147483647 8 | 100 9 | 0 10 | 0 11 | 12 | arquillian 13 | 14 | NORMAL 15 | false 16 | 17 | false 18 | 19 | 20 | 21 | 22 | jnlp 23 | registry.access.redhat.com/openshift3/jenkins-slave-maven-rhel7:v3.9 24 | false 25 | false 26 | /tmp 27 | 28 | ${computer.jnlpmac} ${computer.name} 29 | false 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 0 39 | 0 40 | 0 41 | 0 42 | 0 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /arquillian-test/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.mycompany.k8sit 8 | arquillian-test 9 | 1.0-SNAPSHOT 10 | Example :: Integration test on Kubernetes with Jenkins pipeline :: Arquillian Test 11 | 12 | 13 | UTF-8 14 | 15 | 16 | 1.17.1 17 | 18 | 5.0.6.RELEASE 19 | 2.4.0 20 | 5.15.4 21 | 5.3.0 22 | 1.2.3 23 | 24 | 3.7.0 25 | 2.21.0 26 | 27 | 28 | 29 | 30 | 31 | org.arquillian.cube 32 | arquillian-cube-bom 33 | ${arquillian.version} 34 | import 35 | pom 36 | 37 | 38 | 39 | org.springframework 40 | spring-framework-bom 41 | ${spring.version} 42 | pom 43 | import 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | org.arquillian.cube 52 | arquillian-cube-openshift-starter 53 | 54 | 55 | 56 | 57 | io.fabric8 58 | openshift-client 59 | 4.0.3 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | org.arquillian.cube 69 | arquillian-cube-requirement 70 | 71 | 72 | 73 | ch.qos.logback 74 | logback-classic 75 | ${logback.version} 76 | 77 | 78 | 79 | 80 | org.springframework 81 | spring-jms 82 | 83 | 84 | 85 | org.apache.activemq 86 | activemq-client 87 | ${activemq.version} 88 | 89 | 90 | 91 | org.mock-server 92 | mockserver-client-java 93 | ${mockserver.version} 94 | 95 | 96 | 97 | com.jayway.jsonpath 98 | json-path 99 | ${json-path.version} 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | maven-compiler-plugin 108 | ${maven-compiler-plugin.version} 109 | 110 | 1.8 111 | 1.8 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /arquillian-test/src/test/java/com/mycompany/k8sit/ArquillianTest.java: -------------------------------------------------------------------------------- 1 | package com.mycompany.k8sit; 2 | 3 | import com.jayway.jsonpath.JsonPath; 4 | import io.fabric8.kubernetes.api.model.ObjectMeta; 5 | import io.fabric8.kubernetes.api.model.Pod; 6 | import io.fabric8.kubernetes.api.model.v4_0.PodList; 7 | import io.fabric8.kubernetes.api.model.v4_0.ServiceList; 8 | import io.fabric8.kubernetes.client.dsl.ExecListener; 9 | import io.fabric8.kubernetes.client.dsl.ExecWatch; 10 | import okhttp3.Response; 11 | import org.apache.activemq.ActiveMQConnectionFactory; 12 | import org.arquillian.cube.kubernetes.annotations.Named; 13 | import org.arquillian.cube.kubernetes.annotations.PortForward; 14 | import org.arquillian.cube.kubernetes.api.Session; 15 | import org.arquillian.cube.openshift.impl.client.OpenShiftAssistant; 16 | import org.arquillian.cube.openshift.impl.enricher.RouteURL; 17 | import org.arquillian.cube.openshift.impl.requirement.RequiresOpenshift; 18 | import org.arquillian.cube.requirement.ArquillianConditionalRunner; 19 | import org.awaitility.Awaitility; 20 | import org.jboss.arquillian.test.api.ArquillianResource; 21 | import org.junit.Before; 22 | import org.junit.BeforeClass; 23 | import org.junit.Test; 24 | import org.junit.experimental.categories.Category; 25 | import org.junit.runner.RunWith; 26 | import org.mockserver.client.server.MockServerClient; 27 | import org.slf4j.Logger; 28 | import org.slf4j.LoggerFactory; 29 | import org.springframework.jms.core.JmsTemplate; 30 | 31 | import javax.jms.TextMessage; 32 | import java.io.ByteArrayOutputStream; 33 | import java.io.OutputStream; 34 | import java.net.URL; 35 | import java.util.concurrent.CountDownLatch; 36 | import java.util.concurrent.TimeUnit; 37 | 38 | import static org.junit.Assert.assertEquals; 39 | import static org.junit.Assert.assertTrue; 40 | import static org.mockserver.model.HttpRequest.request; 41 | import static org.mockserver.model.HttpResponse.response; 42 | 43 | @Category(RequiresOpenshift.class) 44 | @RunWith(ArquillianConditionalRunner.class) 45 | @RequiresOpenshift 46 | //@RunWith(Arquillian.class) //Can use this simply if don't want to force condition "with OpenShift only" 47 | public class ArquillianTest { 48 | private static final Logger log = LoggerFactory.getLogger(ArquillianTest.class); 49 | 50 | @ArquillianResource 51 | io.fabric8.openshift.client.OpenShiftClient oc; 52 | @ArquillianResource 53 | io.fabric8.kubernetes.client.KubernetesClient client; 54 | 55 | //Causes: java.lang.IllegalArgumentException: Can not set io.fabric8.openshift.clnt.v4_0.OpenShiftClient field com.mycompany.k8sit.ArquillianTest.oc4 to io.fabric8.kubernetes.clnt.v4_0.DefaultKubernetesClient 56 | // @ArquillianResource 57 | // io.fabric8.kubernetes.clnt.v4_0.KubernetesClient client4; 58 | // @ArquillianResource 59 | // io.fabric8.openshift.clnt.v4_0.OpenShiftClient oc4; 60 | 61 | 62 | //Forward amq port so it's available on a localhost port. 63 | @Named("amqsvc") 64 | @PortForward 65 | @ArquillianResource 66 | URL amqsvcUrl; 67 | 68 | //We can of course do the same port-forward here, but let's use a Route instead as this service is http 69 | // @AwaitRoute //It requires a GET endpoint, but mock-server has none by default 70 | @RouteURL("mockserverroute") 71 | URL mockserver; 72 | 73 | @Named("mariadb") 74 | @ArquillianResource 75 | Pod mariadb; 76 | 77 | 78 | //Not used, these are here only for example: 79 | @ArquillianResource //Arquillian namespace: session.getNamespace() or oc.getNamespace() 80 | Session session; 81 | @ArquillianResource //Same as oc.services().list() 82 | ServiceList services; 83 | @ArquillianResource //Same as oc.pods().list() 84 | PodList pods; 85 | @ArquillianResource //Additional helper to deploy 86 | OpenShiftAssistant openShiftAssistant; 87 | 88 | 89 | @BeforeClass 90 | public static void beforeClass(){ 91 | //clients are not available yet (even if static) 92 | } 93 | 94 | private boolean beforeDone = false; 95 | @Before 96 | public void before() throws Exception{ 97 | if (!beforeDone){ 98 | 99 | log.info("Before is running. oc client: {}",oc.getOpenshiftUrl()); 100 | 101 | // Prepare database; 102 | // Run mysql client in container with oc cli tool 103 | // oc exec waits for the command to finish 104 | //Runtime rt = Runtime.getRuntime(); 105 | //Process mysql = rt.exec("oc exec -i -n "+session.getNamespace()+" mariadb -- /opt/rh/rh-mariadb102/root/usr/bin/mysql -u myuser -pmypassword -h 127.0.0.1 testdb"); 106 | //IOUtils.copy(this.getClass().getClassLoader().getResourceAsStream("sql/sql-load.sql"),mysql.getOutputStream() ); 107 | //mysql.getOutputStream().close(); 108 | //log.info("waitFor: {}",mysql.waitFor()); 109 | //log.info("output: {}", IOUtils.toString(mysql.getInputStream())); 110 | //log.info("error: {}", IOUtils.toString(mysql.getErrorStream())); 111 | 112 | // Prepare database 113 | loadSql(); 114 | 115 | 116 | //Prepare MockServer response for test 117 | log.info("mockserver URL: {}",mockserver); 118 | int port = mockserver.getPort() == -1 ? 80 : mockserver.getPort(); 119 | log.info("mockserver {} {}",mockserver.getHost(),port); 120 | //We could use any http client to call the MockServer expectation api, but this java client is nicer 121 | MockServerClient mockServerClient = new MockServerClient(mockserver.getHost(),port); 122 | mockServerClient 123 | .when( 124 | request() 125 | .withMethod("GET") 126 | .withPath("/v1/address/email/testSucc@test.com") 127 | ) 128 | .respond( 129 | response() 130 | .withBody("{\n" + 131 | "\"state\": \"Test State\",\n" + 132 | "\"city\": \"Test City\",\n" + 133 | "\"address\": \"1 Test St\",\n" + 134 | "\"zip\": \"T001\"\n" + 135 | "}\n") 136 | ); 137 | 138 | beforeDone=true; 139 | } 140 | } 141 | 142 | //Happy path 143 | @Test 144 | public void testSucc() throws Exception { 145 | 146 | log.info("Test resources are in namespace:"+session.getNamespace()); 147 | 148 | //OpenShift client - here for example 149 | log.info("OpenShift - getMasterUrl: {}", oc.getMasterUrl()); 150 | log.info("OpenShift - getApiVersion: {}", oc.getApiVersion()); 151 | log.info("OpenShift - currentUser: {}", oc.currentUser().getMetadata().getName()); 152 | log.info("OpenShift - getNamespace: {}", oc.getNamespace()); 153 | log.info("OpenShift - getConfiguration: {}", oc.getConfiguration()); 154 | log.info("OpenShift - getClass: {}", oc.getClass()); 155 | 156 | //Kubernetes client - here for example 157 | log.info("Kubernetes - getMasterUrl: {}", client.getMasterUrl()); 158 | log.info("Kubernetes - getApiVersion: {}", client.getApiVersion()); 159 | log.info("Kubernetes - getConfiguration: {}", client.getConfiguration()); 160 | log.info("Kubernetes - getClass: {}", client.getClass()); 161 | 162 | //Service in the current namespace 163 | oc.services().list().getItems().stream() 164 | .map(s->s.getMetadata().getNamespace()+" - "+s.getMetadata().getName()) 165 | .forEach(s->log.info("Service: {}",s)); 166 | 167 | /************* 168 | * Start test 169 | *************/ 170 | //The port-foward url has http schema, but it's actually the amq 61616 port 171 | String brokerUrl = "tcp://"+ amqsvcUrl.getHost()+":"+ amqsvcUrl.getPort(); 172 | log.info("brokerUrl: {}",brokerUrl); 173 | ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory("test","secret",brokerUrl); 174 | JmsTemplate jmsTemplate = new JmsTemplate(connectionFactory); 175 | 176 | //Send test message and receive outcome 177 | jmsTemplate.convertAndSend("user.in", "{\"email\":\"testSucc@test.com\"}"); 178 | TextMessage message = (TextMessage) jmsTemplate.receive("user.out"); 179 | String response = message.getText(); 180 | 181 | log.info("Response: {}",response); 182 | 183 | //Asserts 184 | assertEquals("testSucc@test.com", JsonPath.read(response, "$.email")); 185 | assertEquals("5551234567", JsonPath.read(response, "$.phone")); 186 | assertEquals("Test State", JsonPath.read(response, "$.address.state")); 187 | assertEquals("Test City", JsonPath.read(response, "$.address.city")); 188 | assertEquals("1 Test St", JsonPath.read(response, "$.address.address")); 189 | assertEquals("T001", JsonPath.read(response, "$.address.zip")); 190 | 191 | } 192 | 193 | //Test verifies that the retry works in case of temporary database downtime 194 | @Test 195 | public void shutDownMariadb() throws Exception{ 196 | //Delete mariadb pod 197 | log.info("mariadb: {}", oc.pods().withName("mariadb").get()); //not null 198 | log.info("Delete mariadb pod."); 199 | assertTrue( oc.pods().withName(mariadb.getMetadata().getName()).delete() ); 200 | Awaitility.await().atMost(30,TimeUnit.SECONDS).until(()->oc.pods().withName("mariadb").get()==null); 201 | 202 | //The port-foward url has http schema, but it's actually the amq 61616 port 203 | String brokerUrl = "tcp://"+ amqsvcUrl.getHost()+":"+ amqsvcUrl.getPort(); 204 | log.info("brokerUrl: {}",brokerUrl); 205 | ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory("test","secret",brokerUrl); 206 | JmsTemplate jmsTemplate = new JmsTemplate(connectionFactory); 207 | 208 | //Send test message and receive outcome 209 | jmsTemplate.convertAndSend("user.in", "{\"email\":\"testSucc@test.com\"}"); 210 | 211 | //Wait a sec to make sure message fails 212 | Thread.sleep(3000); 213 | 214 | //Create mariadb Pod and load sql 215 | log.info("Create mariadb pod."); 216 | ObjectMeta emptyObjectMeta = new ObjectMeta(); 217 | emptyObjectMeta.setName(mariadb.getMetadata().getName()); 218 | emptyObjectMeta.setLabels(mariadb.getMetadata().getLabels()); 219 | mariadb.setMetadata(emptyObjectMeta); 220 | mariadb.setStatus(null); 221 | Pod newPod = oc.pods().create(mariadb); 222 | log.info("mariadb: {}", oc.pods().withName("mariadb").get()); 223 | Awaitility.await().atMost(30,TimeUnit.SECONDS).until(()->oc.pods().withName("mariadb").isReady()); 224 | //Recreate table, load data 225 | loadSql(); 226 | 227 | //Receive response message 228 | TextMessage message = (TextMessage) jmsTemplate.receive("user.out"); 229 | String response = message.getText(); 230 | 231 | log.info("Response: {}",response); 232 | 233 | //Asserts 234 | assertEquals("testSucc@test.com", JsonPath.read(response, "$.email")); 235 | assertEquals("5551234567", JsonPath.read(response, "$.phone")); 236 | assertEquals("Test State", JsonPath.read(response, "$.address.state")); 237 | assertEquals("Test City", JsonPath.read(response, "$.address.city")); 238 | assertEquals("1 Test St", JsonPath.read(response, "$.address.address")); 239 | assertEquals("T001", JsonPath.read(response, "$.address.zip")); 240 | 241 | } 242 | 243 | private void loadSql() throws Exception{ 244 | // Run command in container using OpenShiftClient java client - run sql in mysql 245 | log.info("Sql load - start"); 246 | final CountDownLatch latch = new CountDownLatch(1); 247 | OutputStream execOut = new ByteArrayOutputStream(); 248 | OutputStream execErr = new ByteArrayOutputStream(); 249 | ExecWatch exec = oc.pods().withName("mariadb") 250 | .readingInput(this.getClass().getClassLoader().getResourceAsStream("sql/sql-load.sql")) 251 | .writingOutput(execOut) 252 | .writingError(execErr) 253 | //.withTTY() //Optional 254 | .usingListener(createCountDownListener(latch)) 255 | .exec("/opt/rh/rh-mariadb102/root/usr/bin/mysql","-u", "myuser", "-pmypassword", "-h", "127.0.0.1", "testdb") 256 | ; 257 | if (!latch.await(20, TimeUnit.SECONDS)) { 258 | throw new Exception("Exec timeout"); 259 | } 260 | log.info("Exec out: {}", ((ByteArrayOutputStream) execOut).toString()); 261 | log.info("Exec err: {}", ((ByteArrayOutputStream) execErr).toString()); 262 | log.info("Sql load - end"); 263 | } 264 | 265 | //Helper method for OpenShiftClient exec() 266 | public static ExecListener createCountDownListener(CountDownLatch latch){ 267 | return new ExecListener() { 268 | @Override 269 | public void onOpen(Response response) { 270 | log.info("onOpen response: {}",response); 271 | } 272 | 273 | @Override 274 | public void onFailure(Throwable t, Response response) { 275 | log.info("onFailure response: {}",response,t); 276 | latch.countDown(); 277 | 278 | } 279 | 280 | @Override 281 | public void onClose(int code, String reason) { 282 | log.info("onClose reason: {} {}",code, reason); 283 | latch.countDown(); 284 | 285 | } 286 | }; 287 | } 288 | 289 | } 290 | -------------------------------------------------------------------------------- /arquillian-test/src/test/resources/after-namespace.sh: -------------------------------------------------------------------------------- 1 | #This script is run after the namespace is created/chosen (see $KUBERNETES_NAMESPACE), but before applying kubernetes.yaml. 2 | #It's run under the Arquillian test process, so 'oc' should be available on the test host as it's used in this script. 3 | 4 | #The best practice is to have 'oc' on the test host logged in with a user (e.g. system:serviceaccount:myproject:arquillian) 5 | # and don't overwrite in arquillian.xml, so this script and Arquillian uses the same oc user 6 | #Make sure that this user can create new projects: 7 | # oc adm policy add-cluster-role-to-user self-provisioner system:serviceaccount:myproject:arquillian 8 | 9 | #Check where we are 10 | pwd 11 | env 12 | 13 | #oc is independent from arquillian.xml, so the "current" logged in user is used (e.g. service account of the pod) 14 | #the current 'oc project' is also NOT the namespace created by Arquillian 15 | oc whoami 16 | oc project 17 | 18 | 19 | #Make sure the 'default' SA in our $KUBERNETES_NAMESPACE can pull image from the CI/CD project 'myproject' 20 | #Easiest is to allow any serviceaccount to pull images from the ci/cd project in advance 21 | # oc policy add-role-to-group system:image-puller system:serviceaccounts -n myproject 22 | #Or if the current user has admin permissions, you can add image-puller role for the new namespace's default SA 23 | # oc policy add-role-to-user system:image-puller system:serviceaccount:$KUBERNETES_NAMESPACE:default -n myproject 24 | 25 | 26 | #Allow 'default' SA to read configMap, actually this is why we have this script in this example. This is not needed if you don't want to read configMaps in your pods. 27 | #We want to add this permission in advance otherwise the app-users pod fails to start and test execution won't start 28 | #Make sure that the current 'oc whoami' has the right permissions. If Arquillian created the project, the current user can run this 29 | oc policy add-role-to-user view system:serviceaccount:$KUBERNETES_NAMESPACE:default -n $KUBERNETES_NAMESPACE -------------------------------------------------------------------------------- /arquillian-test/src/test/resources/app-users-test.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: List 3 | items: 4 | #App under test 5 | - apiVersion: v1 6 | kind: Pod 7 | metadata: 8 | name: app-users 9 | labels: 10 | app: app-users 11 | spec: 12 | containers: 13 | - name: app-users 14 | image: 172.30.1.1:5000/myproject/app-users:latest 15 | resources: 16 | limits: 17 | memory: 512Mi 18 | env: 19 | - name: SPRING_PROFILES_ACTIVE 20 | value: xarquillian 21 | - name: SPRING_CLOUD_KUBERNETES_ENABLED 22 | value: true 23 | readinessProbe: 24 | httpGet: 25 | path: /health 26 | port: 8080 27 | initialDelaySeconds: 5 28 | ports: 29 | - name: jolokia 30 | containerPort: 8778 31 | protocol: TCP 32 | volumeMounts: 33 | - name: config-volume 34 | mountPath: /etc/config 35 | volumes: 36 | - name: config-volume 37 | configMap: 38 | name: app-users 39 | #ConfigMap for App under test 40 | - apiVersion: v1 41 | kind: ConfigMap 42 | metadata: 43 | name: app-users 44 | labels: 45 | app: app-users 46 | data: 47 | logging.level.com.mycompany: DEBUG 48 | management.security.enabled: false 49 | amq.brokerUrl: tcp://amqsvc:61616 50 | amq.username: test 51 | amq.password: secret 52 | spring.datasource.url: jdbc:mariadb://mariadbsvc/testdb 53 | spring.datasource.username: myuser 54 | spring.datasource.password: mypassword 55 | spring.datasource.driver-class-name: org.mariadb.jdbc.Driver 56 | api.url: http://mockserversvc:1080/v1/ 57 | 58 | #DB Pod and Service 59 | - apiVersion: v1 60 | kind: Pod 61 | metadata: 62 | name: mariadb 63 | labels: 64 | app: mariadb 65 | spec: 66 | containers: 67 | - name: mariadb 68 | image: registry.access.redhat.com/rhscl/mariadb-102-rhel7:1 69 | resources: 70 | limits: 71 | memory: 256Mi 72 | env: 73 | - name: MYSQL_USER 74 | value: myuser 75 | - name: MYSQL_PASSWORD 76 | value: mypassword 77 | - name: MYSQL_DATABASE 78 | value: testdb 79 | - name: MYSQL_ROOT_PASSWORD 80 | value: secret 81 | readinessProbe: 82 | tcpSocket: 83 | port: 3306 84 | initialDelaySeconds: 5 85 | - apiVersion: v1 86 | kind: Service 87 | metadata: 88 | name: mariadbsvc 89 | spec: 90 | ports: 91 | - port: 3306 92 | targetPort: 3306 93 | selector: 94 | app: mariadb 95 | 96 | #AMQ Pod and Service 97 | - apiVersion: v1 98 | kind: Pod 99 | metadata: 100 | name: amq 101 | labels: 102 | app: amq 103 | spec: 104 | containers: 105 | - name: amq 106 | image: registry.access.redhat.com/jboss-amq-6/amq63-openshift:1.3 107 | resources: 108 | limits: 109 | memory: 256Mi 110 | env: 111 | - name: AMQ_USER 112 | value: test 113 | - name: AMQ_PASSWORD 114 | value: secret 115 | readinessProbe: 116 | tcpSocket: 117 | port: 61616 118 | initialDelaySeconds: 5 119 | ports: 120 | - name: jolokia 121 | containerPort: 8778 122 | protocol: TCP 123 | - apiVersion: v1 124 | kind: Service 125 | metadata: 126 | name: amqsvc 127 | spec: 128 | ports: 129 | - port: 61616 130 | targetPort: 61616 131 | selector: 132 | app: amq 133 | 134 | #External API Third party (provided by mockserver) 135 | - apiVersion: v1 136 | kind: Pod 137 | metadata: 138 | name: mockserver 139 | labels: 140 | app: mockserver 141 | spec: 142 | containers: 143 | - name: mockserver 144 | image: jamesdbloom/mockserver:mockserver-5.3.0 145 | resources: 146 | limits: 147 | memory: 256Mi 148 | env: 149 | - name: LOG_LEVEL 150 | value: INFO 151 | - name: JVM_OPTIONS 152 | value: -Xmx128m 153 | readinessProbe: 154 | tcpSocket: 155 | port: 1080 156 | initialDelaySeconds: 5 157 | - apiVersion: v1 158 | kind: Service 159 | metadata: 160 | name: mockserversvc 161 | spec: 162 | ports: 163 | - port: 1080 164 | targetPort: 1080 165 | selector: 166 | app: mockserver 167 | - apiVersion: v1 168 | kind: Route 169 | metadata: 170 | name: mockserverroute 171 | spec: 172 | to: 173 | kind: Service 174 | name: mockserversvc -------------------------------------------------------------------------------- /arquillian-test/src/test/resources/arquillian.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | app-users-test.yaml 7 | arquillian 8 | after-namespace.sh 9 | true 10 | 11 | 12 | -------------------------------------------------------------------------------- /arquillian-test/src/test/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | %d{HH:mm:ss.SSS} [%16.16thread] %-5level %-30.30logger{30} - %m%n 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /arquillian-test/src/test/resources/sql/sql-load.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE users ( 2 | email VARCHAR(256), 3 | phone VARCHAR(64), 4 | ts_created DATETIME DEFAULT CURRENT_TIMESTAMP, 5 | PRIMARY KEY (email) 6 | ); 7 | INSERT INTO users (email, phone) VALUES ('testSucc@test.com', '5551234567'); 8 | INSERT INTO users (email, phone) VALUES ('testRetry@test.com', '5551230000'); 9 | select * from users; 10 | exit 11 | -------------------------------------------------------------------------------- /configuration/settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | redhat-repos 6 | 7 | true 8 | 9 | 10 | 11 | central 12 | http://repo1.maven.org/maven2 13 | 14 | true 15 | 16 | 17 | false 18 | 19 | 20 | 21 | redhatga 22 | Red Hat 23 | http://maven.repository.redhat.com/ga 24 | 25 | 26 | redhatea 27 | Red Hat Early Access 28 | http://maven.repository.redhat.com/earlyaccess/all 29 | 30 | 31 | 32 | 33 | 34 | central 35 | http://repo1.maven.org/maven2 36 | 37 | true 38 | 39 | 40 | false 41 | 42 | 43 | 44 | redhatga 45 | Red Hat 46 | http://maven.repository.redhat.com/ga 47 | 48 | 49 | redhatea 50 | Red Hat Early Access 51 | http://maven.repository.redhat.com/earlyaccess/all 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /integration-test/mockserver/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo Prepare MockServer responses 3 | 4 | json_escape_file () { 5 | [ -f "$1" ] || (echo "File not found! $1"; return 1) 6 | cat "$1" | python -c 'import json,sys; print(json.dumps(sys.stdin.read()))' 7 | } 8 | 9 | #Load all .ok with times=unlimited 10 | for file in $(find ./mockserver/ -name '*.ok'); do 11 | filename=$(basename $file) 12 | id="${filename%.*}" 13 | echo Add $id 14 | 15 | URL="/v1/address/email/$id" 16 | BODY="$(json_escape_file $file)" 17 | 18 | curl -sX PUT "http://localhost:1080/expectation" -d "{ 19 | httpRequest : { 20 | path: \"$URL\" 21 | }, 22 | httpResponse : { 23 | body: $BODY 24 | }, 25 | times: { 26 | unlimited: true 27 | }, 28 | timeToLive: { 29 | unlimited: true 30 | } 31 | }" 32 | done 33 | 34 | #Load 2 error then unlimited for testRetry 35 | file="./mockserver/testRetry@test.com.err" 36 | filename=$(basename $file) 37 | id="${filename%.*}" 38 | echo Add $id 39 | 40 | URL="/v1/address/email/$id" 41 | BODY="$(json_escape_file $file)" 42 | 43 | curl -sX PUT "http://localhost:1080/expectation" -d "{ 44 | httpRequest : { 45 | path: \"$URL\" 46 | }, 47 | httpResponse : { 48 | statusCode: 500 49 | }, 50 | times: { 51 | remainingTimes : 2, 52 | unlimited : false 53 | }, 54 | timeToLive: { 55 | unlimited: true 56 | } 57 | }" 58 | 59 | curl -sX PUT "http://localhost:1080/expectation" -d "{ 60 | httpRequest : { 61 | path: \"$URL\" 62 | }, 63 | httpResponse : { 64 | body: $BODY 65 | }, 66 | times: { 67 | unlimited: true 68 | }, 69 | timeToLive: { 70 | unlimited: true 71 | } 72 | }" 73 | -------------------------------------------------------------------------------- /integration-test/mockserver/testRetry@test.com.err: -------------------------------------------------------------------------------- 1 | { 2 | "state": "Test State", 3 | "city": "Test City", 4 | "address": "2 Test St", 5 | "zip": "T002" 6 | } 7 | -------------------------------------------------------------------------------- /integration-test/mockserver/testSucc@test.com.ok: -------------------------------------------------------------------------------- 1 | { 2 | "state": "Test State", 3 | "city": "Test City", 4 | "address": "1 Test St", 5 | "zip": "T001" 6 | } 7 | -------------------------------------------------------------------------------- /integration-test/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | com.mycompany.k8sit 6 | integration-test 7 | 1.0-SNAPSHOT 8 | Example :: Integration test on Kubernetes with Jenkins pipeline :: Integration Tests 9 | 10 | 11 | UTF-8 12 | 13 | 5.2.0 14 | 5.0.6.RELEASE 15 | 1.2.0 16 | 2.4.0 17 | 5.15.4 18 | 19 | 3.7.0 20 | 2.21.0 21 | 22 | 23 | 24 | 25 | 26 | 27 | org.springframework 28 | spring-framework-bom 29 | ${spring.version} 30 | pom 31 | import 32 | 33 | 34 | 35 | org.junit 36 | junit-bom 37 | ${junit.version} 38 | pom 39 | import 40 | 41 | 42 | 43 | com.jayway.jsonpath 44 | json-path 45 | ${json-path.version} 46 | 47 | 48 | 49 | org.apache.activemq 50 | activemq-client 51 | ${activemq.version} 52 | test 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | org.springframework 61 | spring-context 62 | test 63 | 64 | 65 | 66 | org.springframework 67 | spring-test 68 | test 69 | 70 | 71 | org.springframework 72 | spring-jms 73 | test 74 | 75 | 76 | 77 | org.apache.activemq 78 | activemq-client 79 | test 80 | 81 | 82 | 83 | com.jayway.jsonpath 84 | json-path 85 | test 86 | 87 | 88 | 89 | org.junit.jupiter 90 | junit-jupiter-api 91 | test 92 | 93 | 94 | 95 | org.slf4j 96 | slf4j-simple 97 | 1.7.22 98 | test 99 | 100 | 101 | 102 | 103 | 104 | 105 | maven-compiler-plugin 106 | ${maven-compiler-plugin.version} 107 | 108 | 1.8 109 | 1.8 110 | 111 | 112 | 113 | 114 | maven-surefire-plugin 115 | ${maven-surefire-plugin.version} 116 | 117 | 118 | org.junit.platform 119 | junit-platform-surefire-provider 120 | ${junit-platform.version} 121 | 122 | 123 | 124 | org.junit.jupiter 125 | junit-jupiter-engine 126 | ${junit.version} 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /integration-test/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | sudo docker rm -f mockserver 3 | sudo docker rm -f mariadb 4 | sudo docker rm -f amq 5 | 6 | sudo docker run --name mockserver -d -p 1080:1080 docker.io/jamesdbloom/mockserver 7 | sudo docker run --name mariadb -e MYSQL_USER=myuser -e MYSQL_PASSWORD=mypassword -e MYSQL_ROOT_PASSWORD=secret -e MYSQL_DATABASE=testdb -d -p 3306:3306 registry.access.redhat.com/rhscl/mariadb-102-rhel7 8 | sudo docker run --name amq -e AMQ_USER=test -e AMQ_PASSWORD=secret -d -p 61616:61616 -p 8181:8181 registry.access.redhat.com/jboss-amq-6/amq63-openshift 9 | 10 | sleep 10 11 | 12 | sh sql/setup.sh 13 | sh mockserver/setup.sh 14 | 15 | cd ../app-users 16 | java -Dspring.profiles.active=k8sit -jar target/app-users-1.0-SNAPSHOT.jar & 17 | pid=$! 18 | cd - 19 | 20 | export AMQ_USER=test 21 | export AMQ_PASSWORD=secret 22 | mvn clean test 23 | 24 | kill $pid -------------------------------------------------------------------------------- /integration-test/sql/create-tables.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE users ( 2 | email VARCHAR(256), 3 | phone VARCHAR(64), 4 | ts_created DATETIME DEFAULT CURRENT_TIMESTAMP, 5 | PRIMARY KEY (email) 6 | ); 7 | -------------------------------------------------------------------------------- /integration-test/sql/load-data.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO users (email, phone) VALUES ('testSucc@test.com', '5551234567'); 2 | INSERT INTO users (email, phone) VALUES ('testRetry@test.com', '5551230000'); -------------------------------------------------------------------------------- /integration-test/sql/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | mysql -u myuser -pmypassword -h 127.0.0.1 testdb