jenkins - 如何延迟 Jenkins 的工作直到达到某个时间?

标签 jenkins

在我们当前的项目设置中,我们有作业 A、B、C。

成功构建 A、B 或 C 后,我们希望将生成的工件部署到我们的开发服务器。因此我们使用作业 D。

因为部署会重新创建开发数据库,​​所以我们只想每整整一小时运行一次作业。我们的测试人员熟悉这个时间表并以这种方式工作。

实际上,当 D 被 A、B 或 C 触发时,将启动一个脚本,等待服务器分钟为 00。如果其中一个触发器作业在等待期间再次触发 D,则先前等待的脚本将被取消并重新启动。如果脚本到达小时的 00,则部署发生。

主要问题是,在最坏的情况下,作业 D 会阻塞一个建筑槽 59 分钟。

默认情况下每小时运行一次作业不是一种选择,因为这样部署就会发生,即使没有任何改变。

在更改后运行作业也很糟糕,因为测试人员习惯于每小时部署。

我知道有一个“安静时间”选项,但这只能让我设置相对于触发时间的等待时间。我需要的是一个“安静的时间”,将工作推迟到某个时间到达。
有人对如何实现这一目标有任何建议吗?

最佳答案

我开发了以下解决方案:

1) 使用 Conditional BuildStep Plugin在作业 A、B 和 C 中设置作业 D 中的 cron 计划 config.xml :

  • 构建
  • 条件步骤(单)
  • 运行?:执行 shell [仅用于测试,请在此处使用您的构建步骤]
  • 命令

  • #!/bin/bash
    echo "  This script is a placeholder to represent a build step that can succeed or not succeed"
    true   # to test build success → Groovy script should be executed
    #false  # to test not successful build → nothing should be done
     
    

  • Builder:执行系统Groovy脚本
  • ◉ Groovy 命令

  • // From: How to delay Jenkins job until a certain time was reached?
    //       http://stackoverflow.com/questions/27952216/1744774
    
    // -----------------------------------------------------------
    // Adapt these according to your environment
    final String DOWNSTREAM_NAME = 'SO-27952216-Downstream-job'
    final String CRON_SCHEDULE = '0 * * * *'
    // -----------------------------------------------------------
    
    final String SEPARATOR = new String(new char[8]).replace('\0', '-')
    println("  ${SEPARATOR} Adapting configuration of ${DOWNSTREAM_NAME} ${SEPARATOR}")
    
    import jenkins.model.*
    import hudson.model.*
    
    final Project DOWNSTREAM_JOB = Jenkins.instance.getItem(DOWNSTREAM_NAME)
    final String DOWNSTREAM_CONFIG = DOWNSTREAM_JOB.getRootDir().getPath() + "/config.xml"  
    
    //import hudson.triggers.*
    //DOWNSTREAM_JOB.getTrigger(TimerTrigger.class).spec = CRON_SCHEDULE
    // leads to:
    //   ERROR: Build step failed with exception groovy.lang.ReadOnlyPropertyException: 
    //       Cannot set readonly property: spec for class: hudson.triggers.TimerTrigger
    
    import org.w3c.dom.*
    import javax.xml.parsers.*
    import javax.xml.xpath.*
    
    println("  Reading ${DOWNSTREAM_CONFIG}")
    Document doc = 
        DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(DOWNSTREAM_CONFIG)
    XPathExpression expr = XPathFactory.newInstance().newXPath()
        .compile("/project/triggers/hudson.triggers.TimerTrigger/spec");
    final org.w3c.dom.Node SCHEDULE_NODE = expr.evaluate(doc, XPathConstants.NODE)
    
    println(String.format(
        "  Changing Build Triggers → Build periodically → Schedule from '%s' to ' %s'",
        SCHEDULE_NODE.getTextContent(), CRON_SCHEDULE))
    SCHEDULE_NODE.setTextContent(CRON_SCHEDULE)
    
    import javax.xml.transform.*
    import javax.xml.transform.dom.*
    import javax.xml.transform.stream.*
    
    println("  Writing ${DOWNSTREAM_CONFIG}")
    Transformer transformer = TransformerFactory.newInstance().newTransformer()
    transformer.setOutputProperty(OutputKeys.INDENT, "yes")
    transformer.transform(new DOMSource(doc), new StreamResult(new File(DOWNSTREAM_CONFIG)))
    
    println("  ${SEPARATOR} Adapted configuration of ${DOWNSTREAM_NAME} ${SEPARATOR}")
     
    


    2) 使用 构建后操作 Groovy 后构建 在作业 D 中重置其 config.xml 中的 cron 计划:
  • 构建
  • 执行 shell [仅用于测试,请在此处使用您的部署步骤]
  • 命令

  • #!/bin/bash
    echo "  This script is a placeholder to represent a build step"
     
    

  • 构建后操作
  • Groovy 后构建
  • Groovy 脚本

  • // From: How to delay Jenkins job until a certain time was reached?
    //       http://stackoverflow.com/questions/27952216/1744774
    
    // -----------------------------------------------------------
    // Adapt these according to your environment
    final String THIS_JOB_NAME = 'SO-27952216-Downstream-job'
    final String CRON_SCHEDULE = ''
    // -----------------------------------------------------------
    
    final Object LOG = manager.listener.logger
    final String SEPARATOR = new String(new char[8]).replace('\0', '-')
    LOG.println("  ${SEPARATOR} Adapting configuration of ${THIS_JOB_NAME} ${SEPARATOR}")
    
    import jenkins.model.*
    import hudson.model.*
    
    final Project THIS_JOB = Jenkins.instance.getItem(THIS_JOB_NAME)
    final String THIS_JOB_CONFIG = THIS_JOB.getRootDir().getPath() + "/config.xml"  
    
    //import hudson.triggers.*
    //THIS_JOB.getTrigger(TimerTrigger.class).spec = CRON_SCHEDULE
    // leads to:
    //   ERROR: Build step failed with exception groovy.lang.ReadOnlyPropertyException: 
    //       Cannot set readonly property: spec for class: hudson.triggers.TimerTrigger
    
    import org.w3c.dom.*;
    import javax.xml.parsers.*
    import javax.xml.xpath.*
    
    LOG.println("  Reading ${THIS_JOB_CONFIG}")
    Document doc = 
        DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(THIS_JOB_CONFIG)
    XPathExpression expr = XPathFactory.newInstance().newXPath()
        .compile("/project/triggers/hudson.triggers.TimerTrigger/spec")
    final org.w3c.dom.Node SCHEDULE_NODE = expr.evaluate(doc, XPathConstants.NODE)
    
    LOG.println(String.format(
        "  Changing Build Triggers → Build periodically → Schedule from '%s' to ' %s'",
        SCHEDULE_NODE.getTextContent(), CRON_SCHEDULE))
    SCHEDULE_NODE.setTextContent(CRON_SCHEDULE)
    
    import javax.xml.transform.*
    import javax.xml.transform.dom.*
    import javax.xml.transform.stream.*
    
    LOG.println("  Writing ${THIS_JOB_CONFIG}")
    Transformer transformer = TransformerFactory.newInstance().newTransformer()
    transformer.setOutputProperty(OutputKeys.INDENT, "yes")
    transformer.transform(new DOMSource(doc), new StreamResult(new File(THIS_JOB_CONFIG)))
    
    LOG.println("  ${SEPARATOR} Adapted configuration of ${THIS_JOB_NAME} ${SEPARATOR}")
     
    

    欢迎提示为什么println()在此构建后操作中,不会向 Jenkins 的控制台输出打印任何内容。

    更新:明白了!根据 [JENKINS-18651 – Enable correct writing to the job's log from a post build script]它是 manager.listener.logger.println()而不仅仅是 println() .

    需要注意的是,此解决方案可能会在三种情况下导致问题:
  • 作业 A、B、C 或 D 写信给 config.xml并且用户同时通过UI保存D的配置→并发写访问。
  • 如果UI是先打开config.xml我们可以通过异常处理和几秒钟后在我们的脚本中重试来解决这个问题。
  • 如果我们的脚本是先打开config.xml我们运气不好 → 在“恶魔” Jenkins 的统治下,用户界面可能会显示错误:
    .
  • 作业 A 或 B 或 C 写信给 config.xml在与 D 相同的时刻,反之亦然 → 我们可以通过异常处理和几秒钟后在我们的脚本中重试来解决这个问题。
  • 作业 A、B、C 或 D 写信给 config.xml和 Jenkins 的 TimerTrigger同时读取文件(它每分钟执行一次)→ 由于写入尚未完全完成而读取不正确的数据。


  • 我把它作为读者的一个挑战,以避免在多个上游工作的情况下通过以下方式避免代码重复:
  • 将上游作业的 Groovy 代码移动到:http://jenkins/userContent/scripts/JobCronScheduleConfigurator.groovy
  • 在任何上游作业中使用以下内容:
  • 构建
  • 条件步骤(单)
  • builder
  • ◉ Groovy 脚本文件http://jenkins/userContent/scripts/JobCronScheduleConfigurator.groovy

  • 不幸的是构建后操作 Groovy 后构建 不支持 Groovy 脚本文件,因此在这方面仍然存在代码重复(并且日志记录,见上文,无论如何都是不同的)。

    关于jenkins - 如何延迟 Jenkins 的工作直到达到某个时间?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27952216/

    相关文章:

    svn - 如何将 SVN 用户映射到 Jenkins 人员

    Jenkins 构建管道计划触发器

    android - 葫芦 android.util.AndroidException : INSTRUMENTATION_FAILED

    version-control - Jenkins:SCM 触发持续构建,尽管没有变化

    git - 我们可以重新触发 gitlab 管道吗?

    jenkins - 在 Jenkinsfile 中结帐 scm

    由于服务器证书不匹配,jenkins 中的 SVN 命令行失败

    jenkins - 每天在 8 :30 AM except on Friday 安排 Jenkins 作业

    maven - org.apache.maven.surefire.booter.SurefireBooterForkException : There was an error in the forked process null

    git - Jenkins 通过 SSH 访问私有(private) BitBucket 仓库