接上篇《授之以漁-運維平臺發(fā)布模塊二(Jenkins篇)》混卵,今天介紹下項目的回滾機制。
我的平臺用到Jenkins API的創(chuàng)建項目魂莫,刪除,更新爹耗,查看變更耙考,構(gòu)建。
一潭兽、利用Jenkins-python 創(chuàng)建項目
def create_job(self, name, config_xml):
if self.job_exists(name):
raise JenkinsException('job[%s] already exists' % (name))
headers = {'Content-Type': 'text/xml'}
self.jenkins_open(urllib2.Request(
self.server + CREATE_JOB % locals(), config_xml, headers))
if not self.job_exists(name):
raise JenkinsException('create[%s] failed' % (name))
我的平臺需要做的就是傳遞一個項目名稱倦始,一個config_xml(配置文檔),在用程序生成config_xml,我傳遞了下面幾個參數(shù):svn_daysToKeep
,svn_numToKeep
,svn_name
,svn_publish_address
,svn_address
,svn_name
,svn_name
svn_daysToKeep
:構(gòu)建項目保存時間山卦,過了時間會自動刪除以前的
svn_numToKeep
:構(gòu)建項目保存版本號數(shù)量鞋邑,過了數(shù)量會自動刪除以前的
svn_name
:構(gòu)建項目名稱
svn_publish_address
:發(fā)布地址(RPM包安裝地址,構(gòu)建FPM請參看之前的文章)
svn_address
:SVN地址
config_xml如下:
"""<?xml version='1.0' encoding='UTF-8'?>
<project>
<actions/>
<description></description>
<logRotator class="hudson.tasks.LogRotator">
<daysToKeep>%s</daysToKeep>
<numToKeep>%s</numToKeep>
<artifactDaysToKeep>-1</artifactDaysToKeep>
<artifactNumToKeep>-1</artifactNumToKeep>
</logRotator>
<keepDependencies>false</keepDependencies>
<properties>
<hudson.plugins.batch__task.BatchTaskProperty plugin="batch-task@1.16">
<tasks>
<hudson.plugins.batch__task.BatchTask>
<name>%s</name>
<script>mkdir -p /home/release/$JOB_NAME && fpm -s dir -x .svn -t rpm -n $JOB_NAME -v $BUILD_NUMBER --prefix %s -C /var/lib/jenkins/workspace/$JOB_NAME -p /home/release/$JOB_NAME ./ && createrepo --update /home/release/$JOB_NAME/ && curl -d "job_id=$JOB_NAME" http://172.18.18.24/cmdb/salt_jenkins_post/</script>
</hudson.plugins.batch__task.BatchTask>
</tasks>
</hudson.plugins.batch__task.BatchTaskProperty>
</properties>
<scm class="hudson.scm.SubversionSCM" plugin="subversion@1.45">
<locations>
<hudson.scm.SubversionSCM_-ModuleLocation>
<remote>%s</remote>
<local>.</local>
<depthOption>infinity</depthOption>
<ignoreExternalsOption>false</ignoreExternalsOption>
</hudson.scm.SubversionSCM_-ModuleLocation>
</locations>
<excludedRegions></excludedRegions>
<includedRegions></includedRegions>
<excludedUsers></excludedUsers>
<excludedRevprop></excludedRevprop>
<excludedCommitMessages></excludedCommitMessages>
<workspaceUpdater class="hudson.scm.subversion.UpdateUpdater"/>
<ignoreDirPropChanges>false</ignoreDirPropChanges>
<filterChangelog>false</filterChangelog>
</scm>
<canRoam>true</canRoam>
<disabled>false</disabled>
<blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
<blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
<triggers/>
<concurrentBuild>false</concurrentBuild>
<builders/>
<publishers>
<hudson.tasks.Mailer plugin="mailer@1.5">
<recipients>21186716@qq.com</recipients>
<dontNotifyEveryUnstableBuild>false</dontNotifyEveryUnstableBuild>
<sendToIndividuals>true</sendToIndividuals>
</hudson.tasks.Mailer>
<hudson.plugins.batch__task.BatchTaskInvoker plugin="batch-task@1.16">
<configs>
<hudson.plugins.batch__task.BatchTaskInvoker_-Config>
<project>%s</project>
<task>%s</task>
</hudson.plugins.batch__task.BatchTaskInvoker_-Config>
</configs>
<threshold>
<name>UNSTABLE</name>
<ordinal>1</ordinal>
<color>YELLOW</color>
</threshold>
</hudson.plugins.batch__task.BatchTaskInvoker>
</publishers>
<buildWrappers/>
</project>"""%(svn_daysToKeep,svn_numToKeep,svn_name,svn_publish_address,svn_address,svn_name,svn_name)
有了項目名和配置文件账蓉,只需要調(diào)用jenkins-python接口即可:
from cmdb.myapi.jenkins import jenkins_remote
J = jenkins_remote.Jenkins(jenkins服務(wù)器地址,jenkins用戶名,jenkins token)
J.create_job(項目名, 項目的config_xml)
至于Jenkins的Token如何生成呢枚碗?
登錄系統(tǒng)-點擊右上角你的用戶名稱
點擊設(shè)置-點擊Show API Token
二、利用Jenkins-python 構(gòu)建項目
def build_job(self, name, parameters=None, token=None):
if not self.job_exists(name):
raise JenkinsException('no such job[%s]' % (name))
return self.jenkins_open(urllib2.Request(
self.build_job_url(name, parameters, token)))
構(gòu)建一樣要用token铸本,然后的動作就是傳入項目名了肮雨。
怎么判斷一次構(gòu)建是否完成呢?
def get_job_info(self, name):
try:
response = self.jenkins_open(urllib2.Request(
self.server + JOB_INFO % locals()))
if response:
return json.loads(response)
else:
raise JenkinsException('job[%s] does not exist' % name)
except urllib2.HTTPError:
raise JenkinsException('job[%s] does not exist' % name)
except ValueError:
raise JenkinsException(
"Could not parse JSON info for job[%s]" % name)
返回結(jié)果
>>> J.get_job_info('bbs.youth.cn')
{u'scm': {}, u'color': u'blue', u'lastSuccessfulBuild': {u'url': u'http://172.18.11.98:8080/job/bbs.youth.cn/137/', u'number': 137}, u'actions': [{}, {}], u'lastCompletedBuild': {u'url': u'http://172.18.11.98:8080/job/bbs.youth.cn/137/', u'number': 137}, u'lastUnsuccessfulBuild': None, u'upstreamProjects': [], u'lastFailedBuild': None, u'healthReport': [{u'iconUrl': u'health-80plus.png', u'score': 100, u'description': u'Build stability: No recent builds failed.'}], u'queueItem': None, u'lastBuild': {u'url': u'http://172.18.11.98:8080/job/bbs.youth.cn/137/', u'number': 137}, u'lastStableBuild': {u'url': u'http://172.18.11.98:8080/job/bbs.youth.cn/137/', u'number': 137}, u'description': u'\u9752\u7f51\u8bba\u575b', u'downstreamProjects': [], u'concurrentBuild': False, u'lastUnstableBuild': None, u'buildable': True, u'displayNameOrNull': None, u'inQueue': False, u'keepDependencies': False, u'name': u'bbs.youth.cn', u'displayName': u'bbs.youth.cn', u'builds': [{u'url': u'http://172.18.11.98:8080/job/bbs.youth.cn/137/', u'number': 137}, {u'url': u'http://172.18.11.98:8080/job/bbs.youth.cn/136/', u'number': 136}, {u'url': u'http://172.18.11.98:8080/job/bbs.youth.cn/135/', u'number': 135}, {u'url': u'http://172.18.11.98:8080/job/bbs.youth.cn/134/', u'number': 134}, {u'url': u'http://172.18.11.98:8080/job/bbs.youth.cn/133/', u'number': 133}, {u'url': u'http://172.18.11.98:8080/job/bbs.youth.cn/132/', u'number': 132}, {u'url': u'http://172.18.11.98:8080/job/bbs.youth.cn/131/', u'number': 131}, {u'url': u'http://172.18.11.98:8080/job/bbs.youth.cn/130/', u'number': 130}, {u'url': u'http://172.18.11.98:8080/job/bbs.youth.cn/129/', u'number': 129}, {u'url': u'http://172.18.11.98:8080/job/bbs.youth.cn/128/', u'number': 128}], u'url': u'http://172.18.11.98:8080/job/bbs.youth.cn/', u'firstBuild': {u'url': u'http://172.18.11.98:8080/job/bbs.youth.cn/128/', u'number': 128}, u'nextBuildNumber': 138, u'property': [{}]}
本次成功構(gòu)建的版本號lastSuccessfulBuild的number是137箱玷,我需要現(xiàn)將本次構(gòu)建的nextBuildNumber存入數(shù)據(jù)庫的svn_next_no字段里(比如138)怨规,下次構(gòu)就通過while循環(huán)獲取get_job_info來獲得lastSuccessfulBuild的number(最后一次成功構(gòu)建的版本號),直到項目從137變成138后锡足,在和數(shù)據(jù)庫中的nextBuildNumber(下次構(gòu)建的版本號)進行對比波丰,如果一樣我就認為項目構(gòu)建成功了。
我的平臺代碼如下:
# -*- coding: utf-8 -*-
from cmdb.models import *
from cmdb.myredis.redis_conect import *
from cmdb.myapi.jenkins import jenkins_remote
from django.conf import settings
import time
def release_build_f(job):
J = jenkins_remote.Jenkins(settings.JENKINS['server'],settings.JENKINS['username'],settings.JENKINS['token'])
"""初始化檢測構(gòu)建項目的當(dāng)前版本號和下一次版本號"""
if Svn.objects.get(svn_name=job).svn_next_no == "None" or int(Svn.objects.get(svn_name=job).svn_next_no) != int(J.get_job_info(job)['lastSuccessfulBuild']['number']):
Svn.objects.filter(svn_name=job).update(svn_next_no = J.get_job_info(job)['nextBuildNumber'])
else:
pass
"""構(gòu)建項目"""
J.build_job("%s"%job)
"""進入監(jiān)測版本號變更循環(huán)"""
while True:
try:
"""如當(dāng)前構(gòu)建版本號等于下次構(gòu)建版本號舶得,跳出循環(huán)"""
time.sleep(5)
if int(J.get_job_info(job)['lastSuccessfulBuild']['number']) == int(Svn.objects.get(svn_name=job).svn_next_no):
"""跳出"""
break
except Exception as err:
break
return ("構(gòu)建異常",err)
"""項目發(fā)布后同步構(gòu)建項目的當(dāng)前版本號和下一次版本號"""
try:
Svn.objects.filter(svn_name=job).update(svn_no = J.get_job_info(job)['lastSuccessfulBuild']['number'],
svn_next_no = J.get_job_info(job)['nextBuildNumber'],
svn_time = '%s %s' %( str(J.get_build_info(job,J.get_job_info(job)['lastSuccessfulBuild']['number'])['id']).split('_')[0],
str(J.get_build_info(job,J.get_job_info(job)['lastSuccessfulBuild']['number'])['id']).split('_')[1].replace('-',':'))
)
except Exception as err:
print err
"""清空項目對應(yīng)的發(fā)布狀態(tài)緩存"""
try:
r = redis_conect_db6()
r.delete(*r.keys('%s*' % job))
except:
pass
return ("%s進入構(gòu)建隊列,版本變更為%s,10秒鐘后刷新更新頁面"%(job, J.get_job_info(job)['lastSuccessfulBuild']['number']))
三掰烟、利用Jenkins-python 查看構(gòu)建項目變更
def get_build_info(self, name, number):
try:
response = self.jenkins_open(urllib2.Request(
self.server + BUILD_INFO % locals()))
if response:
return json.loads(response)
else:
raise JenkinsException('job[%s] number[%d] does not exist'
% (name, number))
except urllib2.HTTPError:
raise JenkinsException('job[%s] number[%d] does not exist'
% (name, number))
except ValueError:
raise JenkinsException(
'Could not parse JSON info for job[%s] number[%d]'
% (name, number)
)
我們需要傳入項目名,然后就是需要查看項目變更的構(gòu)建版本號
返回結(jié)果
>>> J.get_build_info('bbs.youth.cn',137)
{u'building': False, u'changeSet': {u'items': [], u'kind': u'svn', u'revisions': [{u'module': u'http://172.18.11.96/svndata/bbs_youth/trunk', u'revision': 209}]}, u'builtOn': u'', u'description': None, u'artifacts': [], u'timestamp': 1497414529627, u'number': 137, u'actions': [{u'causes': [{u'userName': u'\u5f20\u5e06', u'userId': u'zhangfan', u'shortDescription': u'Started by user \u5f20\u5e06'}]}, {}, {}, {}], u'id': u'2017-06-14_12-28-49', u'keepLog': False, u'url': u'http://172.18.11.98:8080/job/bbs.youth.cn/137/', u'culprits': [], u'result': u'SUCCESS', u'executor': None, u'duration': 1535, u'fullDisplayName': u'bbs.youth.cn #137', u'estimatedDuration': 1865}
changeSet里msg就是變更內(nèi)容啦