Commit 613dce70 authored by qhoangxuan's avatar qhoangxuan

wi4

parent a47a16f6
Zoe Continuous Integration
--------------------------
Overview
########
- Integrate Zoe repository to Jenkins and SonarQube
- Each commit to Zoe repository trigger a build at Jenkins:
- Run SonarQube Scanner to analyze the codebase
- Create two containers for zoe-master, zoe-api
- Run integration test [testing rest api]
- Build new images if no errors happen
- Deploy Zoe with latest images
Software Stack
##############
- Jenkins - version 2.7.4
- SonarQube - version 6.1
Configuration
#############
- Jenkins: all the configurations in this section is configured on Jenkins Server
- Required:
- Plugins: Github plugin, SonarQube Plugin, Quality Gates, Email Plugin (optional), Cobertura Coverage Report (optional)
- Softwares: Java, Python, Docker
- Go to **Manage Jenkins**, then **Global Tool Configuration** to setup Java SDK, SonarQube Scanner
- SonarQube server configuration: this aims to connect Jenkins and SonarQube together
- Go to **Manage Jenkins** then **Configure System**
- SonarQube servers: input name, server URL, server version, **sever authentication token** (created on SonarQube Server)
- Quality Gates configuration:
- Go to **Manage Jenkins** then **Configure System**
- Quality Gates: input name, server URL, username and password to login into SonarQube server
- Github Servers configuration:
- Go to **Manage Jenkins** then **Configure System**
- Github: **Add Github Server**, the API URL would be ``https://api.github.com``. The credentials creation is well defined in the document of Github plugin:
- You can create your own [personal access token](https://github.com/settings/tokens/new) in your account GitHub settings.
- Token should be registered with scopes:
- admin:repo_hook - for managing hooks (read, write and delete old ones)
- repo - to see private repos
- repo:status - to manipulate commit statuses
- In Jenkins create credentials as «Secret Text», provided by Plain Credentials Plugin
- Create credentials for Github account: this is similar when you want to [connect to Github over SSH](https://help.github.com/articles/connecting-to-github-with-ssh/), here, beside adding your public key to Github, you also need to add your private key to Jenkins.
- Create SSH key pair on the machine run Jenkins:
- Add public key to Github
- Add private key to Jenkins credentials
- Create new item as a **freestyle project**: this aims to create a Jenkins job with the github repository
- General
- Select Github project
- Insert project URL
- Source Code Management
- Select **Git**
- Repositories
- Repository URL: use **SSH URL** of Github repository
- Credentials: select the one created above
- Build Triggers
- For Github plugin with version before 1.25.1: Select **Build when a change is pushed to Github**
- For Github plugin with version from 1.25.1: Select **GitHub hook trigger for GITScm polling**
- Build
- Add **Execute SonarQube Scanner** to do SonarQube Analysis
- Add **Quality Gates** to break the build when the SonarQube quality gate is not passed
- Add **Execute Shell** to run script for testing, deploying. Please refer to the Appendix section for the script.
- Post-build Actions [Optional]
- Add **Publish Covetura Coverage Report** for getting report from coverage. Due to the shell script in Appendix, the xml file generated by coverage is located at ``test`` folder, so, we should put ``**/tests/coverage.xml`` as the input of the field **Cobertura xml report pattern**.
- Add **E-mail Notification** for notifying when jobs finish
- Github
- Add new SSH key (the one created on Jenkins server)
- Go to the project (which is integrated to Jenkins) settings
- Integration & Services
- Add Service, choose **Jenkins (Github plugin)**
- Add Jenkins hook url
- For github plugin, this one would have the format: http://your-jenkins.com/github-webhook
- In case your Jenkins doens't expose to the world, try https://ngrok.com/
- SonarQube: all the configurations in this section is configured on SonarQube Server
- On **Administration**, go to **My Account**, then **Security**
- Generate Tokens, copy this and paste to **server authentication token** on Jenkins configuration
- The project needs to provides **sonar-properties** file in the repo:(http://docs.sonarqube.org/display/SCAN/Analyzing+with+SonarQube+Scanner)
- Then, on System then Update Center, install two plugins for Python and TypeScript.
Appendix
########
- Sonar properties files
- Take a look at sonar-project.properties files in root, ``zoe_api``, ``zoe_master``, ``zoe_lib``, ``zoe_fe`` folders.
- Execute Shell Script
- Push this script inside the execute shell script of Jenkins job you created above, the zoe_rest_api can be changed in the ``test_config.py`` file.
::
# Run Style checker for Sphinx RST documentation
doc8 docs/
# Build new container images
python3 ci/zoeci.py 1 tcp://192.168.12.2:2375 192.168.12.2:5000/zoe:$BUILD_ID
# Deploy new zoe with the above images for testing
python3 ci/zoeci.py 0 tcp://192.168.12.2:2375 ci/docker-compose-test.yml 192.168.12.2:5000/zoe:$BUILD_ID
# Run integration test
cd tests
coverage run -p basic_auth_success_test.py
coverage run -p cookie_auth_success_test.py
coverage combine
coverage xml
cd ..
# Push the built images above to local registry
python3 ci/zoeci.py 2 tcp://192.168.12.2:2375 192.168.12.2:5000/zoe:$BUILD_ID
# Redeploy zoe with new images
python3 ci/zoeci.py 0 tcp://192.168.12.2:2375 ci/docker-compose-prod.yml 192.168.12.2:5000/zoe:$BUILD_ID
- Screenshots
- Jenkins Server configuration
- Plugin configuration
- Java SDK Configuration
.. image:: imgs/1.java.config.png
- SonarQube Scanner Configuration
.. image:: imgs/1.2.sonar.config.PNG
- SonarQube Server Configuration
.. image:: imgs/2.sonar.config.png
- Quality Gates Configuration
.. image:: imgs/2.1.sonar.quality.gates.png
- Github Server Configuration
.. image:: imgs/4.1.github.server.config.png
- Github Server Credential Creation
.. image:: imgs/4.1.github.server.credential.png
- Email Notification Configuration
.. image:: 3.email.config.png
- Create Github credentials
.. image:: 4.github.credential.png
- Create Freestyle project
.. image:: imgs/5.1.freestyle.project.png
.. image:: imgs/5.2.freestyle.project.png
.. image:: imgs/5.3.freestyle.project.png
.. image:: imgs/5.4.1.freestyle.project.png
.. image:: imgs/5.4.2.freestyle.project.png
.. image:: imgs/5.4.3.freestyle.project.png
.. image:: imgs/5.5.freestyle.project.png
- SonarQube Configuration
.. image:: imgs/6.sonar.token.png
- Github Repository Configuration
- Create webhook service
.. image:: imgs/7.github.repo.png
- Create access token
.. image:: imgs/7.1.github.access.token.png
......@@ -3,10 +3,12 @@ import json
import time
import unittest
from config import ZOE_API_URI, ZOE_AUTH
class ZoeRestTestSuccess(unittest.TestCase):
uri = 'http://localhost:5001/api/0.7/'
auth = ('test', '1234')
uri = ZOE_API_URI
auth = ZOE_AUTH
wrong_auth = ('test', '123')
id = '-1'
......
......@@ -3,10 +3,12 @@ import json
import time
import unittest
from test_config import ZOE_API_URI, ZOE_AUTH
class ZoeRestTestSuccess(unittest.TestCase):
uri = 'http://192.168.12.2:5100/api/0.7/'
auth = ('admin', 'admin')
uri = ZOE_API_URI
auth = ZOE_AUTH
id = ''
def tearDown(self):
......@@ -54,7 +56,7 @@ class ZoeRestTestSuccess(unittest.TestCase):
data = []
with open('tf.json', encoding='utf-8') as data_file:
with open('zapp.json', encoding='utf-8') as data_file:
data = json.loads(data_file.read())
r = requests.post(self.__class__.uri + 'execution', auth=self.__class__.auth, json={"application": data, "name": "requests"})
......
......@@ -3,9 +3,11 @@ import json
import time
import unittest
from config import ZOE_API_URI, ZOE_AUTH
class ZoeRestTestSuccess(unittest.TestCase):
uri = 'http://localhost:5001/api/0.7/'
uri = ZOE_API_URI
id = '-1'
s = None
......
......@@ -3,9 +3,11 @@ import json
import time
import unittest
from config import ZOE_API_URI, ZOE_AUTH
class ZoeRestTestSuccess(unittest.TestCase):
uri = 'http://192.168.12.2:5100/api/0.7/'
uri = ZOE_API_URI
id = ''
s = None
......@@ -15,7 +17,7 @@ class ZoeRestTestSuccess(unittest.TestCase):
def test_0_login(self):
print('Test login api endpoint')
s = requests.Session()
r = s.get(self.__class__.uri + 'login', auth=('admin','admin'))
r = s.get(self.__class__.uri + 'login', ZOE_AUTH)
self.assertEqual(r.status_code, 200)
......@@ -79,7 +81,7 @@ class ZoeRestTestSuccess(unittest.TestCase):
data = []
with open('tf.json', encoding='utf-8') as data_file:
with open('zapp.json', encoding='utf-8') as data_file:
data = json.loads(data_file.read())
s = self.__class__.s
......
import requests
import json
import time
import unittest
class ZoeRestTestSuccess(unittest.TestCase):
uri = 'http://localhost:5001/api/0.6/'
auth = ('test', '1234')
id = ''
def tearDown(self):
time.sleep(3)
def test_3_execution_details(self):
print('Test execution details api endpoint')
r = requests.get(self.__class__.uri + 'execution/' + self.__class__.id, auth=self.__class__.auth)
self.assertEqual(r.status_code, 200)
def test_4_terminate_execution(self):
print('Test terminate no execution api endpoint')
r = requests.delete(self.__class__.uri + 'execution/' + self.__class__.id, auth=self.__class__.auth)
self.assertEqual(r.status_code, 400)
def test_6_delete_execution(self):
print('Test delete execution api endpoint')
r = requests.delete(self.__class__.uri + 'execution/delete/' + self.__class__.id, auth=self.__class__.auth)
self.assertEqual(r.status_code, 204)
def test_2_start_failed_execution(self):
print('Test fail submit execution api endpoint')
data = []
with open('dummy.json', encoding='utf-8') as data_file:
data = json.loads(data_file.read())
r = requests.post(self.__class__.uri + 'execution', auth=self.__class__.auth, json={"application": data, "name": "requests"})
self.assertEqual(r.status_code, 201)
self.__class__.id = str(r.json()['execution_id'])
if __name__ == '__main__':
unittest.main()
{
"name": "alpine",
"priority": 512,
"requires_binary": true,
"services": [
{
"docker_image": "alpine",
"environment": [],
"essential_count": 1,
"monitor": true,
"name": "tensorflow",
"networks": [],
"ports": [],
"required_resources": {
"memory": 33554432
},
"startup_order": 0,
"total_count": 1
}
],
"version": 2,
"will_end": false
}
# Copyright (c) 2017, Quang-Nhat HOANG-XUAN
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Testing configuration."""
ZOE_API_URI = 'http://192.168.12.2:5100/api/0.7/'
ZOE_AUTH = ('admin', 'admin')
{
"name": "tensorflow",
"priority": 512,
"requires_binary": true,
"services": [
{
"docker_image": "gcr.io/tensorflow/tensorflow",
"environment": [],
"essential_count": 1,
"monitor": true,
"name": "tensorflow",
"networks": [],
"ports": [
{
"expose": false,
"is_main_endpoint": true,
"name": "Jupyter Notebook interface",
"path": "/",
"port_number": 8888,
"protocol": "http"
}
],
"required_resources": {
"memory": 536870912
},
"startup_order": 0,
"total_count": 1
}
],
"version": 2,
"will_end": false
}
{
"name": "tensorflow",
"name": "nginx",
"priority": 512,
"requires_binary": true,
"services": [
{
"docker_image": "gcr.io/tensorflow/tensorflow",
"docker_image": "nginx:alpine",
"environment": [],
"essential_count": 1,
"monitor": true,
"name": "tensorflow",
"name": "nginx",
"networks": [],
"ports": [
{
"expose": false,
"is_main_endpoint": true,
"name": "Jupyter Notebook interface",
"name": "Nginx Webserver",
"path": "/",
"port_number": 8888,
"port_number": 80,
"protocol": "http"
}
],
......
---
- config:
- testset: "Basic Tests"
- timeout: 100
- test:
- name: "info endpoint"
- url: "/api/0.6/info"
- method: "GET"
- expected_status: [200]
- group: "Successful"
- validators:
- compare: {jsonpath_mini: "api_version", comparator: "eq", expected: "0.6"}
- compare: {jsonpath_mini: "deployment_name", comparator: "eq", expected: "compose"}
- compare: {jsonpath_mini: "version", comparator: "eq", expected: "0.10.1-beta"}
- compare: {jsonpath_mini: "application_format_version", comparator: "eq", expected: 2}
- test:
- name: "execution details"
- url: "/api/0.6/execution/23"
- method: "GET"
- auth_username: "test"
- auth_password: "1234"
- expected_status: [200]
- group: "Successful"
- test:
- name: "terminate execution"
- url: "/api/0.6/execution/23"
- method: "DELETE"
- auth_username: "test"
- auth_password: "1234"
- expected_status: [400]
- group: "Successful"
- validators:
- compare: {jsonpath_mini: "message", comparator: "eq", expected: "Execution is not running"}
- test:
- name: "list all executions"
- url: "/api/0.6/execution"
- method: "GET"
- auth_username: "test"
- auth_password: "1234"
- expected_status: [200]
- group: "Successful"
- test:
- name: "start execution"
- url: "/api/0.6/execution"
- method: "POST"
- auth_username: "test"
- auth_password: "1234"
- expected_status: [400]
- group: "Successful"
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment