hudson - significado - jenkins tutorial
Trabajo parametrizado de Jenkins que solo pone en cola una construcción (7)
Imagine un trabajo A de Jenkins que tarda 1 minuto en ejecutarse, y el trabajo B, que demora 5 minutos.
Si configuramos el trabajo A para activar el trabajo B, mientras el trabajo B está ejecutándose, el trabajo A puede ejecutarse 5 veces antes de que B finalice. Sin embargo, Jenkins no agrega 5 compilaciones a la cola del trabajo B, lo cual es excelente porque, de lo contrario, el trabajo A acelerado crearía una acumulación de compilaciones en constante crecimiento para un lento y lento trabajo B.
Sin embargo, ahora queremos tener el disparador A del trabajo A como un trabajo parametrizado, utilizando el plugin de activación parametrizado . Los trabajos parametrizados ponen en cola una acumulación de trabajo, lo que significa que el trabajo A crea felizmente una gran cantidad de construcciones para el trabajo B, que posiblemente no puede mantenerse al día.
Tiene sentido agregar una nueva compilación parametrizada a la cola cada vez que se activa, ya que los parámetros pueden ser diferentes. Jenkins no siempre debe asumir que una nueva compilación parametrizada hace innecesarios a los que estaban previamente en cola.
Sin embargo, en nuestro caso, realmente nos gustaría. Job A crea y empaqueta nuestra aplicación, luego Job B la implementa en un entorno de producción y ejecuta un conjunto más pesado de pruebas de integración. También tenemos una compilación C que se implementa en otro entorno e incluso hace más pruebas, por lo que este es un patrón creciente para nosotros.
Nos gustaría que la cola de nuestro trabajo parametrizado B solo mantenga la última compilación añadida; cada nueva construcción reemplazaría cualquier trabajo actualmente en la cola.
¿Hay alguna buena manera de lograr esto?
Podría deshacerse del Complemento disparador parametrizado y, en su lugar, usar el disparo tradicional. Como dijiste, esto evitaría que la cola de trabajo B se acumulara.
¿Cómo pasar los parámetros de A a B? Haga que el trabajo A arroje los parámetros en la salida de la consola. En el trabajo B, para obtener estos parámetros de compilación, examine la salida de la última compilación A de la consola (¿con una secuencia de comandos de Python, quizás?).
En caso de que esté utilizando Git, ahora esto es compatible con los "hash git Combine Queued" en la opción Triggering / Parameters / Pass-through. La primera versión del complemento de Git que debería funcionar con esto es 1.1.27 (ver Jenkins-15160 )
Agregue un paso previo a la compilación de "Groovy Script" al trabajo B que comprueba si hay trabajos en cola (más nuevos) con el mismo nombre, y rescata si se encuentra:
def name = build.properties.environment.JOB_NAME
def queue = jenkins.model.Jenkins.getInstance().getQueue().getItems()
if (queue.any{ it.task.getName() == name }) {
println "Newer " + name + " job(s) in queue, aborting"
build.doStop()
} else {
println "No newer " + name + " job(s) in queue, proceeding"
}
La solución de Ron funcionó para mí. Si no te gusta tener varias compilaciones canceladas en el historial de compilación, puedes agregar la siguiente secuencia de comandos groovy del sistema al trabajo A antes de activar el trabajo B:
import hudson.model.*
def q = jenkins.model.Jenkins.getInstance().getQueue()
def items = q.getItems()
for (i=0;i<items.length;i++){
if(items[i].task.getName() == "JobB"){
items[i].doCancelQueue()
}
}
Aquí hay una opción más flexible si solo le importan algunos parámetros coincidentes. Esto es especialmente útil cuando un trabajo se desencadena externamente (es decir, desde GitHub o Stash) y algunos parámetros no necesitan una compilación separada.
Si los parámetros marcados coinciden tanto en el valor como en la existencia tanto en la compilación actual como en la compilación en cola, la compilación actual se cancelará y la descripción mostrará que una compilación futura contiene los mismos parámetros verificados (junto con lo que fueron).
Se podría modificar para cancelar todos los demás trabajos en cola excepto el último si no desea que el historial de compilación muestre los trabajos cancelados.
checkedParams = [
"PARAM1",
"PARAM2",
"PARAM3",
"PARAM4",
]
def buildParams = null
def name = build.project.name
def queuedItems = jenkins.model.Jenkins.getInstance().getQueue().getItems()
yieldToQueuedItem = false
for(hudson.model.Queue.Item item : queuedItems.findAll { it.task.getName() == name }) {
if(buildParams == null) {
buildParams = [:]
paramAction = build.getAction(hudson.model.ParametersAction.class)
if(paramAction) {
buildParams = paramAction.getParameters().collectEntries {
[(it.getName()) : it.getValue()]
}
}
}
itemParams = [:]
paramAction = item.getAction(hudson.model.ParametersAction.class)
if(paramAction) {
itemParams = paramAction.getParameters().collectEntries {
[(it.getName()) : it.getValue()]
}
}
equalParams = true
for(String compareParam : checkedParams) {
itemHasKey = itemParams.containsKey(compareParam)
buildHasKey = buildParams.containsKey(compareParam)
if(itemHasKey != buildHasKey || (itemHasKey && itemParams[compareParam] != buildParams[compareParam])) {
equalParams = false
break;
}
}
if(equalParams) {
yieldToQueuedItem = true
break
}
}
if (yieldToQueuedItem) {
out.println "Newer " + name + " job(s) in queue with matching checked parameters, aborting"
build.description = "Yielded to future build with:"
checkedParams.each {
build.description += "<br>" + it + " = " + build.buildVariables[it]
}
build.doStop()
return
} else {
out.println "No newer " + name + " job(s) in queue with matching checked parameters, proceeding"
}
Lo siguiente se basa en la solución de Ron, pero con algunas correcciones para trabajar en mi Jenkins 2, que incluye la eliminación de la excepción java.io.NotSerializableException y el manejo de que el formato de getName()
es a veces diferente del de JOB_NAME
// Exception to distinguish abort due to newer jobs in queue
class NewerJobsException extends hudson.AbortException {
public NewerJobsException(String message) { super(message); }
}
// Find jenkins job name from url name (which is the most consistently named
// field in the task object)
// Known forms:
// job/NAME/
// job/NAME/98/
@NonCPS
def name_from_url(url)
{
url = url.substring(url.indexOf("/") + 1);
url = url.substring(0, url.indexOf("/"));
return url
}
// Depending on installed plugins multiple jobs may be queued. If that is the
// case skip this one.
// http://.com/questions/26845003/how-to-execute-only-the-most-recent-queued-job-in-jenkins
// http://.com/questions/8974170/jenkins-parameterized-job-that-only-queues-one-build
@NonCPS
def check_queue()
{
def name = env.JOB_NAME
def queue = jenkins.model.Jenkins.getInstance().getQueue().getItems()
if (queue.any{ name_from_url(it.task.getUrl()) == name }) {
print "Newer ${name} job(s) in queue, aborting"
throw new NewerJobsException("Newer ${name} job(s) in queue, aborting")
} else {
print "No newer ${name} job(s) in queue, proceeding"
}
}
Aquí hay una solución:
- Crear un trabajo A2B entre los trabajos A y B
- Agregue un paso de compilación en el trabajo A2B que determine si B se está ejecutando. Para lograr eso, verifique:
- Finalmente, active el trabajo B desde A2B solo si no hay construcciones B en cola o en ejecución (llevando los parámetros hasta el final)