前言
本篇介绍了一种利用Jenkins Pipeline脚本更新Openshift容器云平台上已部署应用的方式,主要涉及如下知识:
- 如何利用Openshift在Jenkins的插件更新部署并获取状态
- 如何编写Jenkins Pipeline脚本
- 如何为Pipeline脚本建立动态参数、动态步骤
- 如何让Pipeline步骤并行执行
- 如何从容器镜像库获取镜像Tag列表
以下为运行效果示意,如感兴趣可继续阅读。
运行效果
说明
本例中使用的Jenkins运行于Openshift平台上,可能是Openshift插件可以正常使用的条件。
本例中脚本对镜像Tag个格式有具体要求,详见下文。
如何更新
了解K8S和Openshift的同学应该知道,我们把StatefulSet、Deployment、DeploymentConfig里容器镜像替换掉,就会触发部署。
在以下Groovy方法中,调用了Openshift在Jenkins的插件的selector方法,找到部署对象,然后修改目标容器的镜像,从而触发更新。
def patchImage(appConfig) {
def p = openshift.selector("${appConfig.deploymentType}/${appConfig.deploymentName}").object()
def originalImage = p.spec.template.spec.containers[0].image
def targetImage = "image-registry.openshift-image-registry.svc:5000/${openshift.project()}/${appConfig.imageStreamName}:${appConfig.imageTag}"
p.spec.template.spec.containers[0].image = targetImage
openshift.apply(p)
if (targetImage != originalImage) {
echo "${appConfig.deploymentType}/${appConfig.deploymentName} 镜像已由 ${originalImage} 更新为: ${openshift.project()}/${appConfig.imageStreamName}:${appConfig.imageTag},将自动触发Rollout"
} else {
echo "${appConfig.deploymentType}/${appConfig.deploymentName} 镜像未改变: ${originalImage},将强制rollout。"
openshift.selector("${appConfig.deploymentType}/${appConfig.deploymentName}").rollout().latest()
}
}
如何判断更新状态
Deployment在Rollout的过程中,我们可以通过openshift插件获取拿到最新的部署版本,并判断其是否更新完成。
由于DeploymentConfig(简写为dc)和StatefulSet(简写为sts)有些不同,所以,我们分开进行判断。
对于dc,主要通过比较desiredReplicas(期望副本数)和readyReplicas(就绪副本数)来判断是否完成了更新。
if (appConfig.deploymentType == "dc") {
def latestDeploymentVersion = openshift.selector(appConfig.deploymentType, appConfig.deploymentName).object().status.latestVersion
def rc = openshift.selector('rc', "${appConfig.deploymentName}-${latestDeploymentVersion}")
timeout (time: 30, unit: 'MINUTES') {
rc.untilEach(1){
def rcMap = it.object()
def desiredReplicas = rcMap.metadata.annotations['kubectl.kubernetes.io/desired-replicas']
if (desiredReplicas == null) {
desiredReplicas = rcMap.status.replicas
}
def readyReplicas = rcMap.status.readyReplicas == null ? 0 : rcMap.status.readyReplicas
echo "desired replicas: ${desiredReplicas}, readyReplicas: ${readyReplicas}"
if (waitEachCopy) {
return desiredReplicas != null && (desiredReplicas.equals(readyReplicas))
} else {
return desiredReplicas != null && readyReplicas >=1
}
}
}
}
对于sts,方式是判断每个容器均Ready。
if (appConfig.deploymentType == "sts") {
def latestStsVersion = openshift.selector(appConfig.deploymentType, appConfig.deploymentName).object().status.updateRevision
def stsPods = openshift.selector('pods', [ 'controller-revision-hash': latestStsVersion ])
timeout (time: 30, unit: 'MINUTES') {
stsPods.untilEach(1){
if (waitEachCopy) {
return it.object().status.containerStatuses.every {
echo "pod container ready: ${it.ready}"
it.ready
}
} else {
return it.object().status.containerStatuses.any {
echo "pod container ready: ${it.ready}"
it.ready
}
}
}
}
}
如何将镜像导入内部镜像库
在更新部署对象的镜像之前,我们可以先把容器镜像由外部镜像库导入到Openshift内部镜像库,可以多留存一份镜像,同时利于工作节点快速获取镜像(未验证)。
这里用到了openshit插件的raw方法,可以执行原生oc命令,所以导入镜像实际使用的命令为oc image mirror。不知什么原因,这里经常会报Unknown Blob错误,多试几次就好了。
在同步镜像之前,需要登录源和目标镜像仓库,使用的也是oc命令registry login。
Closure createImportImageStep(appConfig) {
return {
echo "当前集群:${openshift.cluster()} 当前项目: ${openshift.project()}"
echo "登录内部镜像库..."
openshift.raw("registry login --registry=${internalRegistryAddress} --insecure=true --skip-check=true")
echo "登录外部镜像库..."
openshift.raw("registry login --registry ${externalRegistryAddress} --auth-basic=YourUsername:YourPassword --insecure=true")
echo "导入镜像到内部镜像库..."
if (!appConfig.imageTag) {
echo "${appConfig.app} 因无目标镜像跳过镜像导入"
return
}
retry(5) {
importDockerImage(appConfig)
}
}
}
def importDockerImage(appConfig) {
echo "开始导入镜像:istag/${appConfig.imageStreamName}:${appConfig.imageTag}"
def p = openshift.selector("istag/${appConfig.imageStreamName}:${appConfig.imageTag}")
if (p.exists() && importImageForce) {
p.delete()
echo "imagestreamtag/${appConfig.imageStreamName}:${appConfig.imageTag} deleted."
}
if (!p.exists() || importImageForce) {
openshift.raw("image mirror ${externalRegistryAddress}/${appConfig.imagePath}/${appConfig.imageName}:${appConfig.imageTag} ${internalRegistryAddress}/${openshift.project()}/${appConfig.imageStreamName}:${appConfig.imageTag} --insecure=true")
}
echo "完成导入镜像:istag/${appConfig.imageStreamName}:${appConfig.imageTag}"
}
如何列举镜像Tag
为了方便运维同事通过界面选择要更新的应用和Tag,需要Pipeline脚本从外部Docker镜像库读取各应用镜像的Tag。
这里脚本的核心点是调用了镜像库的HTTP API。
//访问镜像库api获取镜像Tag
def getDockerTags(appConfig) {
def url = "http://${externalRegistryAddress}/v2/${appConfig.imagePath}/${appConfig.imageName}/tags/list"
def list = getDockerImageTags(url)
list
}
// 分为3步,获得json文本,转换为json对象,从对象获得所有Tag
def getDockerImageTags(url) {
def myjson = getUrl(url)
def json = jsonParse(myjson);
def tags = json.tags
tags
}
//通过curl访问镜像库API,将结果保存为result.json文件,然后返回文件内容
def getUrl(url) {
sh(returnStdout: true, script: "curl -s --max-time 15 --retry 3 --retry-delay 5 ${url} 2>&1 | tee result.json")
def data = readFile('result.json').trim()
data
}
// 将镜像Tag倒序排列后,取前5个
def getLatestTag(appConfig) {
def tags = getDockerTags(appConfig)
if (tags == null || tags.size() == 0) {
return null
}
def sortedTags = sortReverse(tags)
def topTags = sortedTags.size() > 5 ? new ArrayList(sortedTags.subList(0,4)) : sortedTags
topTags
}
// 对镜像Tag按倒序排列,要求镜像Tag遵循一定格式,如2.1.0-20211105
@NonCPS
def sortReverse(list) {
list.sort{a,b ->
def ay = a.split("[.-]")
def by = b.split("[.-]")
for (int i = 0; i < ay.length; i++) {
def ai = ay[i].isInteger() ? ay[i].toInteger() : ay[i];
def bi = by[i].isInteger() ? by[i].toInteger() : by[i];
if (bi.compareTo(ai) == 0) {
continue
}
return bi.compareTo(ai)
}
}
}
// 将json文本转换为json对象
@NonCPS
def jsonParse(json) {
new groovy.json.JsonSlurperClassic().parseText(json)
}
如何生成选型参数
为Jenkins Pipeline创建选项参数,方法是创建一个ChoiceParameterDefinition的数组就可以了。
//生成镜像Tag选择器
def getTagChoiceParamter() {
def tags = []
tagSet.each { entry -> tags.add(entry.key) }
def sortedTags = sortReverse(tags)
def parameters = []
def para = new ChoiceParameterDefinition("latestTag", sortedTags, sortedTags[0], "最新版本")
parameters.add para
parameters
}
//这个是镜像选择的stage 片段
stage('选择版本') {
def tagSelectorInputTimeout = false
try {
timeout(time: 180, unit: 'SECONDS') {
tagSelectorInput = input(
id: 'tagInput', ok: '继续', message: '请选择要更新到的镜像Tag:', parameters: tagParameters
)
}
} catch(err) {
def user = err.getCauses()[0].getUser()
if('SYSTEM' == user.toString()) {
tagSelectorInputTimeout = true
} else {
tagSelectorInput = null
echo "镜像Tag选择被用户中止: [${user}]"
}
}
}
验证身份
能够访问Pipeline,说明已经当前用户已经登录Jenkins了。为了多一道安全控制,需要用户在Openshift界面或通过命令oc whoami -t获取一个Token,在正式运行Pipeline部署步骤之前验证一下身份。
这里主要使用了Openshift插件的withCluster、withCredentials和raw三个方法,通过执行oc status -v命令验证其身份。
stage('登录Openshift') {
timeout(time: 180, unit: 'SECONDS') {
myToken = input(
id: 'myTokenInput', ok: '继续', message: '请输入访问Openshift的Token', parameters: [password(defaultValue: '', description: 'OCP访问令牌,可以在登录openshift后,通过执行 oc whoami -t 获得', name: 'Token for Openshift')]
)
}
openshift.withCluster() {
openshift.withCredentials(myToken) {
def status = openshift.raw('status', '-v')
echo "登录成功. 集群是:${openshift.cluster()} 默认项目: ${openshift.project()} 流水线版本:${pipelineVersion}"
}
}
}
应用元数据
应用的列表和应用之间的依赖关系,以及各应用部署对象的类型和名称、镜像库路径等等,通过在脚本定义字段实现。
@Field def final myConfigJson = '''
[
{
"app": "activity", //应用
"imagePath": "xxx-group/release/yyy", //应用镜像在镜像库的路径
"imageName": "activity", //应用镜像的名称
"imageStreamName": "activity", //应用镜像在内部镜像库的名称
"deploymentType": "dc", //应用的部署类型,dc即DeploymentConfig
"deploymentName": "activity", //应用的Deployment或StatefulSet的名称
"selected": false, //是否默认选中,如果经常更新该应用,可默认选中
"dependencies": [
], //依赖的其它应用,可以控制更新顺序
"status": "new", //更新状态控制,这里都是new,其它由脚本控制
"imageTag": "", //目标镜像Tag,在运行中赋值
"group": ["积分活动"] //应用组,方便一次性选择多个应用,当前脚本以无此功能
},
{
"app": "point",
"imagePath": "xxx-group/release/yyy",
"imageName": "point",
"imageStreamName": "point",
"deploymentType": "dc",
"deploymentName": "point",
"selected": false,
"dependencies": [
],
"status": "new",
"imageTag": "",
"group": ["积分活动"]
}
]
'''
完整脚本
完整的Pipeline脚本如下,为方便理解,加了一些注释。为方便选择应用,选择应用的方式几经修改,部分方法可能已经不再使用了。使用该脚本在Jenkins建立Pipeline类型的Job即可,不要选择“Use Groovy Sandbox”。可能因安全原因,脚本变更后,每次都需要Jenkins管理员授权。
import groovy.transform.Field
@Field def final pipelineVersion = "20210813" //此Pipeline的版本,用于发生问题时调查依据
@Field def final envType = "" // 环境类型 ".test" 为测试环境,""为生产环境,方便在测试环境和生产环境同时使用
@Field def final externalRegistryAddress = "docker-app.nexus${envType}.ccc" //外部镜像库域名
@Field def final internalRegistryAddress = "default-route-openshift-image-registry.apps.ocp${envType}.ccc" //内部镜像库域名
@Field def ocp_project = 'yyy-prod' //针对哪个Openshift Project
//应用元数据
@Field def final myConfigJson = '''
[
{
"app": "activity", //应用
"imagePath": "xxx-group/release/yyy", //应用镜像在镜像库的路径
"imageName": "activity", //应用镜像的名称
"imageStreamName": "activity", //应用镜像在内部镜像库的名称
"deploymentType": "dc", //应用的部署类型,dc即DeploymentConfig
"deploymentName": "activity", //应用的Deployment或StatefulSet的名称
"selected": false, //是否默认选中,如果经常更新该应用,可默认选中
"dependencies": [
], //依赖的其它应用,可以控制更新顺序
"status": "new", //更新状态控制,这里都是new,其它由脚本控制
"imageTag": "", //目标镜像Tag,在运行中赋值
"group": ["积分活动"] //应用组,方便一次性选择多个应用,当前脚本以无此功能
},
{
"app": "point",
"imagePath": "xxx-group/release/yyy",
"imageName": "point",
"imageStreamName": "point",
"deploymentType": "dc",
"deploymentName": "point",
"selected": false,
"dependencies": [
],
"status": "new",
"imageTag": "",
"group": ["积分活动"]
}
]
'''
@Field def myConfig;
//Openshift Token,二次验证身份用
@Field def myToken
@Field def tagSet = [:] //镜像Tag的Map,key为镜像Tag,value为应用列表
@Field def rolloutGroupSelector //应用组选择器,此版本已废弃
@Field def rolloutAppSelector //应用选择器
@Field def tagParameters //镜像Tag参数
//运行选项
@Field def waitEachCopy = true //等待每个副本更新完成
@Field def waitDepen = true //等待依赖应用更新完成
@Field def importImageForce = false //当镜像已存在于内部镜像库时,强制导入
@Field def stepsForImportImages = [:] //导入镜像的Pipeline步骤,用于并行执行
@Field def stepsForUpdateApp = [:] //更新应用的Pipeline步骤,用于并行执行
node {
stage('登录Openshift') {
timeout(time: 180, unit: 'SECONDS') {
myToken = input(
id: 'myTokenInput', ok: '继续', message: '请输入访问Openshift的Token', parameters: [password(defaultValue: '', description: 'OCP访问令牌,可以在登录openshift后,通过执行 oc whoami -t 获得', name: 'Token for Openshift')]
)
}
openshift.withCluster() {
openshift.withCredentials(myToken) {
def status = openshift.raw('status', '-v')
echo "登录成功. 集群是:${openshift.cluster()} 默认项目: ${openshift.project()} 流水线版本:${pipelineVersion}"
}
}
myConfig = jsonParse(myConfigJson)
getTagSet(myConfig)
tagParameters = getTagChoiceParamter()
}
stage('选择版本') {
def tagSelectorInputTimeout = false
try {
timeout(time: 180, unit: 'SECONDS') {
tagSelectorInput = input(
id: 'tagInput', ok: '继续', message: '请选择要更新到的镜像Tag:', parameters: tagParameters
)
}
} catch(err) {
def user = err.getCauses()[0].getUser()
if('SYSTEM' == user.toString()) {
tagSelectorInputTimeout = true
} else {
tagSelectorInput = null
echo "镜像Tag选择被用户中止: [${user}]"
}
}
if (tagSelectorInputTimeout) {
currentBuild.result = 'FAILURE'
error "选择镜像Tag超时"
} else if (tagSelectorInput == null) {
currentBuild.result = 'FAILURE'
error "选择镜像错误,可能被用户中止"
} else {
echo "成功选择镜像标签"
}
}
stage('选择应用') {
def selectedTag = tagSelectorInput
def rolloutAppSelectorTimeout = false
try {
def paras = []
def appSelectedFollowTag = tagSet.get(selectedTag)
for (i=0; i < appselectedfollowtag.size i paras.add booleanparamdefaultvalue: true description: name: appselectedfollowtagi.app timeouttime: 180 unit: seconds rolloutappselector='input(id:' rolloutappselectorinput ok: message: parameters: paras catcherr def user='err.getCauses()[0].getUser()' ifsystem='= user.toString())' rolloutappselectortimeout='true' else rolloutappselector='null' echo aborted by: user if rolloutappselectortimeout currentbuild.result='FAILURE' error else if rolloutappselector='= null)' currentbuild.result='FAILURE' error else echo rolloutappselector.findallkey value -> value == true}.each { entry ->
echo "应用 ${entry} 已选择。"
}
def apps = getSelectedApp()
for (int i = 0; i < apps.size i def appconfig='apps[i]' appconfig.imagetag='selectedTag' stage def optionsselectorinputtimeout='false' try timeouttime: 180 unit: seconds runoptions='input(id:' runoptionsinput ok: message: parameters: booleanparamdefaultvalue: true description: name: waiteachcopy booleanparamdefaultvalue: true description: name: waitdepen booleanparamdefaultvalue: false description: name: importimageforce catcherr def user='err.getCauses()[0].getUser()' ifsystem='= user.toString())' optionsselectorinputtimeout='true' else runoptions='null' echo : user if optionsselectorinputtimeout currentbuild.result='FAILURE' error else if runoptions='= null)' currentbuild.result='FAILURE' error else waiteachcopy='runOptions.waitEachCopy.value' waitdepen='runOptions.waitDepen.value' importimageforce='runOptions.importImageForce.value' echo stage def apps sortappselectorbydependency.each appconfig ->
wait = waitFinished(appConfig) ? "-等待" : ""
apps += "${appConfig.deploymentType}/${appConfig.deploymentName}:${appConfig.imageTag}${wait}\n"
}
def waitType = ""
if (waitDepen) {
waitType += "\n在更新前,等待被依赖的应用都更新完成。"
} else {
waitType += "\n直接更新,不等待被依赖的应用更新完成。"
}
if (waitEachCopy) {
waitType += "\n等待所有副本都更新完成"
}
else {
waitType += "\n只要有一个副本更新完成即可"
}
def message = """将更新${ocp_project}的如下应用:
${apps}
等待方式:${waitType}
目标镜像、更新顺序及等待方式如上。
强制刷新镜像: ${importImageForce}
请确认以上内容,并选择继续或中止?
"""
input (message: "将更新${ocp_project}的如下应用", ok: '确认并继续', parameters: [
string(name: 'confirmAppsToUpdate', description: message, defaultValue: "---不用输入---")]
)
}
stage ('导入镜像') {
openshift.withCluster() {
openshift.withCredentials(myToken) {
openshift.withProject(ocp_project) {
rolloutAppSelector.findAll{key, value -> value == true}.each { key, value ->
def appConfig = myConfig.find{item -> item.app == key}
stepsForImportImages["导入镜像:${appConfig.app}"] = createImportImageStep(appConfig)
}
stepsForImportImages["failFast"] = true
parallel stepsForImportImages
}
}
}
}
stage ('更新应用') {
openshift.withCluster() {
openshift.withCredentials(myToken) {
openshift.withProject(ocp_project) {
sortAppSelectorByDependency().each { appConfig ->
stepsForUpdateApp["更新应用:${appConfig.app}"] = createUpdateAppStep(appConfig)
}
parallel stepsForUpdateApp
}
}
}
}
}
def getAppGroups() {
def groups = [:]
groups.put("全部", new HashSet())
for (i=0; i < myConfig.size(); i++) {
def curApp = myConfig[i]
groups["全部"].add(curApp.app)
def group = curApp.group
if (group) {
group.each {
if (!groups.containsKey(it)) {
groups.put(it, new HashSet())
}
groups[it].add(curApp.app)
}
}
}
groups
}
def getSelectedAppFollowGroup() {
def selectedApps = new HashSet()
rolloutGroupSelector.findAll{key, value ->
value == true
}.each { key, value ->
configs = myConfig.findAll{config -> "全部".equals(key) || (config.group && config.group.contains(key))}
if (configs) {
configs.each { config -> selectedApps.add(config.app) }
}
}
selectedApps
}
Closure createImportImageStep(appConfig) {
return {
echo "当前集群:${openshift.cluster()} 当前项目: ${openshift.project()}"
echo "登录内部镜像库..."
openshift.raw("registry login --registry=${internalRegistryAddress} --insecure=true --skip-check=true")
echo "登录外部镜像库..."
openshift.raw("registry login --registry ${externalRegistryAddress} --auth-basic=YourUsername:YourPassword --insecure=true")
echo "导入镜像到内部镜像库..."
if (!appConfig.imageTag) {
echo "${appConfig.app} 因无目标镜像跳过镜像导入"
return
}
retry(5) {
importDockerImage(appConfig)
}
}
}
Closure createUpdateAppStep(appConfig) {
return {
if (!appConfig.imageTag) {
echo "${appConfig.app} 因无目标镜像跳过更新"
appConfig.status = "skiped"
return
}
if (waitFinished(appConfig)) {
def selectedApps = getSelectedApp()
timeout (time: 60, unit: 'MINUTES') {
waitUntil(initialRecurrencePeriod: 30000, quiet: false) {
appConfig.dependencies.every {depAppName ->
def depApp = selectedApps.find{item -> item.app.equals(depAppName)}
def depFinished = depApp == null || depApp.status.equals("updated") || depApp.status.equals("skiped")
if (depApp) {
echo "${appConfig.app}依赖的${depApp.app}当前状态是:${depApp.status}"
}
depFinished
}
}
}
}
patchImage(appConfig)
if (appConfig.deploymentType == "dc") {
def latestDeploymentVersion = openshift.selector(appConfig.deploymentType, appConfig.deploymentName).object().status.latestVersion
def rc = openshift.selector('rc', "${appConfig.deploymentName}-${latestDeploymentVersion}")
timeout (time: 30, unit: 'MINUTES') {
rc.untilEach(1){
def rcMap = it.object()
def desiredReplicas = rcMap.metadata.annotations['kubectl.kubernetes.io/desired-replicas']
if (desiredReplicas == null) {
desiredReplicas = rcMap.status.replicas
}
def readyReplicas = rcMap.status.readyReplicas == null ? 0 : rcMap.status.readyReplicas
echo "desired replicas: ${desiredReplicas}, readyReplicas: ${readyReplicas}"
if (waitEachCopy) {
return desiredReplicas != null && (desiredReplicas.equals(readyReplicas))
} else {
return desiredReplicas != null && readyReplicas >=1
}
}
}
} else if (appConfig.deploymentType == "sts") {
def latestStsVersion = openshift.selector(appConfig.deploymentType, appConfig.deploymentName).object().status.updateRevision
def stsPods = openshift.selector('pods', [ 'controller-revision-hash': latestStsVersion ])
timeout (time: 30, unit: 'MINUTES') {
stsPods.untilEach(1){
if (waitEachCopy) {
return it.object().status.containerStatuses.every {
echo "pod container ready: ${it.ready}"
it.ready
}
} else {
return it.object().status.containerStatuses.any {
echo "pod container ready: ${it.ready}"
it.ready
}
}
}
}
}
appConfig.status = "updated"
echo "${appConfig.deploymentType}/${appConfig.deploymentName} rollout success."
}
}
def waitFinished(app) {
def hasDependency = app.dependencies != null && app.dependencies.size() > 0
waitDepen && hasDependency
}
def sortAppSelectorByDependency() {
def apps = getSelectedApp()
sortByDependency(apps)
}
def getSelectedApp() {
def selectedApps = []
rolloutAppSelector.findAll{key, value ->
value == true
}.each { key, value ->
config = myConfig.find{item -> item.app == key}
if (config != null) {
selectedApps.add(config)
}
}
selectedApps
}
@NonCPS
def sortByDependency(list) {
list.sort {a,b ->
b.dependencies.contains(a.app) ? -1 : a.dependencies.contains(b.app) ? 1 : 0
}
}
def patchImage(appConfig) {
def p = openshift.selector("${appConfig.deploymentType}/${appConfig.deploymentName}").object()
def originalImage = p.spec.template.spec.containers[0].image
def targetImage = "image-registry.openshift-image-registry.svc:5000/${openshift.project()}/${appConfig.imageStreamName}:${appConfig.imageTag}"
p.spec.template.spec.containers[0].image = targetImage
openshift.apply(p)
if (targetImage != originalImage) {
echo "${appConfig.deploymentType}/${appConfig.deploymentName} 镜像已由 ${originalImage} 更新为: ${openshift.project()}/${appConfig.imageStreamName}:${appConfig.imageTag},将自动触发Rollout"
} else {
echo "${appConfig.deploymentType}/${appConfig.deploymentName} 镜像未改变: ${originalImage},将强制rollout。"
openshift.selector("${appConfig.deploymentType}/${appConfig.deploymentName}").rollout().latest()
}
}
def importDockerImage(appConfig) {
echo "开始导入镜像:istag/${appConfig.imageStreamName}:${appConfig.imageTag}"
def p = openshift.selector("istag/${appConfig.imageStreamName}:${appConfig.imageTag}")
if (p.exists() && importImageForce) {
p.delete()
echo "imagestreamtag/${appConfig.imageStreamName}:${appConfig.imageTag} deleted."
}
if (!p.exists() || importImageForce) {
openshift.raw("image mirror ${externalRegistryAddress}/${appConfig.imagePath}/${appConfig.imageName}:${appConfig.imageTag} ${internalRegistryAddress}/${openshift.project()}/${appConfig.imageStreamName}:${appConfig.imageTag} --insecure=true")
}
echo "完成导入镜像:istag/${appConfig.imageStreamName}:${appConfig.imageTag}"
}
def mergeTagList(source, target) {
def result = []
if (source != null) {
result.addAll(source)
}
if (target != null) {
if (result.size() == 0) {
result.addAll(target)
} else {
def tmp = []
for (i = 0; i < target.size(); i++) {
if (result.contains(target[i])) {
tmp.add(target[i])
}
}
result = tmp
}
}
result
}
def getTagParameters() {
def parameters = []
def apps = getSelectedApp()
for (i=0; i < apps.size i def para='getAppTagChoiceParamter(apps[i])' if para parameters.add para parameters def getapptagchoiceparamterappconfig def toptags='getLatestTag(appConfig)' if toptags='= null' toptags.size='= 0)' return null def choiceparamter='new' choiceparameterdefinitionappconfig.app toptags toptags0 appconfig.app choiceparamter def gettagchoiceparamter def tags='[]' tagset.each entry -> tags.add(entry.key) }
def sortedTags = sortReverse(tags)
def parameters = []
def para = new ChoiceParameterDefinition("latestTag", sortedTags, sortedTags[0], "最新版本")
parameters.add para
parameters
}
def getTagSet(configs) {
for (i=0; i < configs.size(); i++) {
def curApp = configs[i]
def latestTags = getLatestTag(curApp)
if (latestTags == null || latestTags.size() ==0) {
continue
}
for (int j=0; j < latesttags.size j def tag='latestTags[j]' if tagset.containskeytag tagset.puttag new hashset tagsettag.addcurapp def getlatesttagappconfig def tags='getDockerTags(appConfig)' if tags='= null' tags.size='= 0)' return null def sortedtags='sortReverse(tags)' def toptags='sortedTags.size()'> 5 ? new ArrayList(sortedTags.subList(0,4)) : sortedTags
topTags
}
def getDockerTags(appConfig) {
def url = "http://${externalRegistryAddress}/v2/${appConfig.imagePath}/${appConfig.imageName}/tags/list"
def list = getDockerImageTags(url)
list
}
@NonCPS
def sortReverse(list) {
list.sort{a,b ->
def ay = a.split("[.-]")
def by = b.split("[.-]")
for (int i = 0; i < ay.length i def ai='ay[i].isInteger()' ayi.tointeger : ayi def bi='by[i].isInteger()' byi.tointeger : byi if bi.comparetoai='= 0)' continue return bi.comparetoai def getdockerimagetagsurl def myjson='getUrl(url)' def json='jsonParse(myjson);' def tags='json.tags' tags noncps def jsonparsejson new groovy.json.jsonslurperclassic.parsetextjson def geturlurl shreturnstdout: true script: curl -s --max-time 15 --retry 3 --retry-delay 5 url 2>&1 | tee result.json")
def data = readFile('result.json').trim()
data
}
结语
本例这种方式实际为迁就部分运维同事,因部分运维同事习惯于使用WebLogic Admin Portal在界面中通过勾选操作来更新应用,故通过Jenkins Pipeline来尽量模拟这一过程。个人认为这不是一个正常的路子,还是应该通过脚本来操作,并以自动化的方式运行。