桑格芝士网

百科知识分享平台,汇聚全球百科全书与学习资源

使用Jenkins更新Openshift上的应用

前言

本篇介绍了一种利用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来尽量模拟这一过程。个人认为这不是一个正常的路子,还是应该通过脚本来操作,并以自动化的方式运行。

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言