RSS Feed Subscribe to RSS Feed

 

Dynamically set Jenkins node

This post explains how to dynamically/programmatically set the node a Jenkins/Hudson job will run on. This is relatively easy to do statically, using the NodeLabel Parameter Plugin, but trickier to do programmatically.

Node parameter

Using the NodeLabel Parameter Plugin in Jenkins, you can pass a Node parameter to a job, so that the node the invoked job will run on (i.e. the master node, or one of the slaves) can be controlled and changed:

jenkins-node

“Restrict where this job can run” value

You can also add a “Restrict where this job can run” value in the job itself, thereby limiting the job to running on a certain node.

jenkins-restrict

Dynamically select node

What is slightly trickier, is to have the node a job runs on selected dynamically at job run time, for example, having the node be based on the value of a variable or other parameter.
However, the NodeLabel Parameter Plugin does also support this, when used in combination with the Parameterized Trigger Plugin. When both plugins are installed, a new parameter type is available in the ‘Trigger paramaterized builds on other projects’ section, specifically the NodeLabel parameter. This allows you to set the node to be used on the triggered job.
To set that value dynamically, you need to also use the EnvInject Jenkins plugin.
In a script on your job, set the value of a variable. For example:

node=qaserver

Then, write that value out to a properties file (since you can’t just ‘pass’ it to NodeLabel parameter) by doing something like

echo “NODE=$node” > tmp.properties

You then select ‘Inject environment variables’ and give ‘tmp.properties’ as the value for ‘Properties file Path’ field.
The file content (NODE=qaserver in this example) will be injected in the job.
Now we can use that variable ($NODE) in the jenkins job.

 

jenkins-dynamically-set-node

That’s it. In this example, we are dynamically setting the node that the Server-Setup job will run on, based on the value of the Environment parameter. The solution is a little complicated as it requires 3 plugins (NodeLabel Parameter Plugin, EnvInject Jenkins plugin, Parameterized Trigger Plugin) but it is the only way I have found to make this work!

 

Related links:

 

 

Tags: , , , , ,

11 Responses to “Dynamically set Jenkins node”

  1. Stephen Davidson |

    Hi — the dynamic method you describe is exactly what I need. However, I get this error:

    10:30:07 [EnvInject] – Injecting environment variables from a build step.
    10:30:07 [EnvInject] – Injecting as environment variables the properties file path ‘tmp.properties’
    10:30:07 [EnvInject] – Variables injected successfully.
    10:30:08 No emails were triggered.
    10:30:08 Triggering a new build of 99 Install NMS and UMP #212
    10:30:08 org.jenkinsci.plugins.tokenmacro.MacroEvaluationException: Unrecognized macro ‘NODE’ in ‘$NODE’
    10:30:08 at org.jenkinsci.plugins.tokenmacro.TokenMacro.expand(TokenMacro.java:198)
    10:30:08 at org.jenkinsci.plugins.tokenmacro.TokenMacro.expandAll(TokenMacro.java:233)
    10:30:08 at org.jenkinsci.plugins.tokenmacro.TokenMacro.expandAll(TokenMacro.java:222)
    10:30:08 at org.jvnet.jenkins.plugins.nodelabelparameter.parameterizedtrigger.NodeLabelBuildParameter.getAction(NodeLabelBuildParameter.java:39)
    10:30:08 at hudson.plugins.parameterizedtrigger.BuildTriggerConfig.getBaseActions(BuildTriggerConfig.java:270)
    10:30:08 at hudson.plugins.parameterizedtrigger.BuildTriggerConfig.getBaseActions(BuildTriggerConfig.java:262)
    10:30:08 at hudson.plugins.parameterizedtrigger.ParameterizedDependency.shouldTriggerBuild(ParameterizedDependency.java:58)
    10:30:08 at hudson.tasks.BuildTrigger.execute(BuildTrigger.java:213)
    10:30:08 at hudson.model.AbstractBuild$AbstractBuildExecution.cleanUp(AbstractBuild.java:679)
    10:30:08 at hudson.model.Build$BuildExecution.cleanUp(Build.java:194)
    10:30:08 at hudson.model.Run.execute(Run.java:1733)
    10:30:08 at hudson.model.FreeStyleBuild.run(FreeStyleBuild.java:43)
    10:30:08 at hudson.model.ResourceController.execute(ResourceController.java:88)
    10:30:08 at hudson.model.Executor.run(Executor.java:231)
    10:30:08 define: [LabelParameterValue: node=$NODE]
    10:30:08 Triggering a new build of 99 Install NMS and UMP #212

    Seems like I am just a nudge away from having this knocked out.

    The value I see in the tmp.properties file is
    NODE=ama01-nms-w2k8r

    Any ideas? Thanks for your contribution!

  2. Trinesh |

    Have a look at this plugin: https://wiki.jenkins-ci.org/display/JENKINS/Groovy+Label+Assignment+plugin

  3. leslie |

    Hi, the example above works fine for injecting a single node into the job. Any idea how to inject multiple node names for running a job on multiple slaves? I’ve tried a number of comma delimited values in the properties file, but this doesn’t work.

  4. sabram |

    Hey Leslie, sorry really not sure, it’s been a while since I worked on this. If you do find a solution for multiple nodes, I’d love to hear it though!

    Shaun

  5. leslie |

    Hi Shaun, the solution turned-out to be quite simple. As per your example, write output property file with csv list of nodes: NODE=node1,node2,node3…. nodex. Trigger Builds with NODE-LIST FACTORY (instead of Node Label Parameter). For list of nodes, use $NODE
    The project runs concurrently on all slaves in $NODE.
    Leslie

  6. sabram |

    Glad you found the solution Leslie, and many thanks for posting details back.

    shaun

  7. Sujay Paranjape |

    Thank you for he article Shaun and thank you Leslie for posting your solution. That was really helpful.

  8. Larry Schapker |

    Thank you for this blog post! I have found it (and Lesie’s comment/solution) very useful.

    With that, I have a question which is related.

    I have search “high and low” and am wondering how I can provide a unique “Job count” to each of these jobs I have now scheduled?

    Example:
    I have scheduled 1 job to run concurrently on 3 different nodes. I want to pass a “1” to the first node, a “2” to the second, etc.

    Reasoning:
    In each of the jobs, I’m pulling from source control a very large static dataset, but I do not want the jobs to duplicate the processing of that data. I want to use this value to partition the data each has to process.

    Thank you again very much for this solution!

  9. sabram |

    Larry,
    Great question. I haven’t tried. Will post if I can think of a solution. Again, like Leslie, it would be great if you post back a solution if you think of one!

    Thanks,

    Shaun

  10. Anon |

    Thanks for this … in case someone else hits the same issue I hit (similar to Stephen’s error)

    There is something in almost all cases of the envInject plugin that surrounds the key=value in quotes. The only way I was able to get this to work was by echoing without quotes:

    echo NODE=$VAL > env

    Or something similar.

  11. Devin |

    So it seems like the Node Label Parameter plugin does this all by itself without the need to trigger a downstream job. Just pass the node in as a parameter for the job. The plugin documentation explains how to accomplish exactly this:
    https://wiki.jenkins-ci.org/display/JENKINS/NodeLabel+Parameter+Plugin

Leave a Reply