12

So, most of the questions and answers I've found on this subject is for people who want to use the SAME workspace for different runs. (Which baffles me, but then I require a clean slate each time I start a job. Leftover stuff will only break things)

My issue is the EXACT opposite - I MUST have a separate workspace for each run (or I need to know how to create files with the same name in different runs that stay with that run only, and which are easily reachable from bash scripts started by the pipeline!)

So, my question is - how do I either force Jenkins to NOT use the same workspace for two concurrently-running jobs on different hosts, OR what variable can I use in the 'custom workspace' field to accomplish this?

After I responded to the question by @Joerg S I realized that I'm saying the thing that Joerg S says CAN'T happen is EXACTLY what I'm observing! Jenkins is using the SAME workspace for 2 different, concurrent, jobs on 2 different hosts. Is this a Jenkins pipeline bug?

See below for a bewildering amount of information.

Given the way I have to go onto and off of nodes during the run, I've found that I can start 2 different builds on different hosts of the same job, and they SHARE the workspace dir! Since each job has shell scripts which are busy writing files into that directory, this is extremely bad.

In Custom workspace in jenkins we are told to use custom workspace, and I'm set up just like that

In Jenkins: how to run builds in unique directories we are told to use ${BUILD_NUMBER} in the above custom workspace field, so what I tried was:

 ${JENKINS_HOME}/workspace/${ITEM_FULLNAME}/${BUILD_NUMBER}

All that happens to me when I use that is that the workspace name is, you guessed it, "${BUILD_NUMBER}" (and I even got a "${BUILD_NUMBER}@2" just for good measure!)

I tried {$BUILD_ID}, same thing (uses that literally, does not substitute the number).

I have the 'allow concurrent builds' turned on.

I'm using pipelines exclusively.

All jobs here, as part of normal execution, cause the slave, non-master host to reboot into an OS that does not have the capability to run slave.jar (indeed, it has no network access at all), so I cannot run the entire pipeline on that host.

All jobs use the following construct somewhere inside them:

    tests=Arrays.asList(tests.split("\\r?\n"))
    shellerror=231
    for( line in tests){

So let's call an example job 'foo' that loops through a list, as above, that I want to run on 2 different hosts. The pipeline for that job starts running on master (since the above for (line in tests) is REQUIRED to run on a node!)). Then goes back and forth between master and slave, often multiple times.

If I start this job on host A and host B at about the same time, they will BOTH use the workspace ${JENKINS_HOME}/workspace/${JOB_NAME}, or in my case /var/lib/jenkins/jenkins/workspace/job

Since they write different data to files with the same name in that directory, I'm clearly totally broken immediately.

So, how do I force Jenkins to use a unique workspace EVERY SINGLE JOB?

Or, what???

Other things: pipeline build step version 2.5.1, Jenkins 2.46.2

I've been trying to get the workspace statement ('ws') to work, but that doesn't quite work as I expected either - some files are in the workspace I explicitly name, and some are still in the 'built-in' workspace (workspace/).

I was asked to provide code. The 'standard' pipeline I use is about 26K bytes, composing about 590 lines. So, I'm going to GREATLY reduce. That being said:

node("master") { // 1
   ..... lots of stuff....
}  // this matches the "node('master')" above
node(HOST) {
  echo "on $HOST, check what os"
  if (isUnix()) 
      ...some more stuff...
} // end of 'node(HOST)' above
if (isok == 0 ) {
   node("master") { 
      echo "----------------- Running on MASTER 19 $shellerror waiting on boot out of windows ------------"
      sleep 120
      echo "----------------- Leaving MASTER ------------"
   }
}
 ... lots 'o code ...

node(HOST) {
  ... etc 
} // matches the latest 'node HOST' above
node("master") { // 120
    .... code ...
    for( line in tests) {
        ...code...
    }
}
... and on and on and on, switching back and forth from one to the other

FWIW, when I tried to make the above use 'ws' so that I could make certain the ws name was unique, I simply added a 'ws wsname' block directly under (almost) every 'node' opening so it was

node(name) { ws (wsname) { ..stuff that was in node block before... } }

But then I've got two directories to worry about checking - both the 'default' workspace/jobname dir AND the new wsname one.

RustyCar
  • 401
  • 1
  • 4
  • 9
  • 1
    Could you please provide some pipeline code to base an answer on? Do you use descriptive (starting with `pipeline` or scripted (starting with `node`) pipelines? – Michael Oct 31 '18 at 05:44
  • I've added a lot of code snippet above, as well as a comment about how I tried to make a unique ws name (unfortunately, I cannot create the 'permanent' ws name until I've done some other stuff, but I don't believe I make, need, or use any files that I could have put into the 'default' workspace name - once I've created the 'wsname' I'm ready to start creating, using, etc files. – RustyCar Oct 31 '18 at 23:09
  • Sorry but it's hard to tell what's your question. Do you just need to clean to workspace before each build? Why not using `deletedir()`? Using the same workspace on two different hosts is not supported by Jenkins. Either you have a plugin installed which provides that feature but is unknown to me or your VM setup reuses the same disk across the VMs - which would be really bad and break your jobs. – Joerg S Nov 01 '18 at 11:16
  • The workspace is getting re-used WHILE another build is running, so that 2 ACTIVE but separate builds share the same workspace. So I'm trying to work around that - either I need to force Jenkins to use some sort of uniqifier (like job number), or I need to get 'ws' to work as I had expected (and understood). So the question is - HOW do I get Jenkins to not use the same workspace for two different runs that are running at the same time? (And apologies for hitting 'enter' before I was done with this comment) – RustyCar Nov 01 '18 at 15:22
  • I'm pretty sure that this is a bug in Jenkins. In the background I'm working on a demo of the failure. Once that is done, where do I go to submit a bug report? – RustyCar Nov 26 '18 at 17:34

4 Answers4

9

Try using customWorkspace node common option:

pipeline {
  agent {
    node {
      label 'node(s)-defined-label'
      customWorkspace "${JENKINS_HOME}/workspace/${JOB_NAME}/${BUILD_NUMBER}"
    }
  }
  stages {
    // Your pipeline logic here
  }
}

customWorkspace

A string. Run the Pipeline or individual stage this agent is applied to within this custom workspace, rather than the default. It can be either a relative path, in which case the custom workspace will be under the workspace root on the node, or an absolute path.

Edit

Since this doesn't work for your complex pipeline. Maybe try this silly solution:

def WORKSPACE = "${JENKINS_HOME}/workspace/${JOB_NAME}/${BUILD_NUMBER}"

node(HOST) {
   sh(script: "mkdir -p ${WORKSPACE}")
   sh(script: "cd ${WORKSPACE}")
   //Do stuff here
}

or if dir() is accessible:

def WORKSPACE = "${JENKINS_HOME}/workspace/${JOB_NAME}/${BUILD_NUMBER}"

node(HOST) {
   sh(script: "mkdir -p ${WORKSPACE}")
   dir(WORKSPACE) {
   //Do stuff here
   }
}
Raoslaw Szamszur
  • 1,723
  • 12
  • 21
  • Does this work only for declarative pipelines? I have a 'for line in tests' (as well as a lot of conditionals) in my pipeline, so I think I have to use the scripted variant. (I'm going right now to add the 'for (line in tests)' to my code above) – RustyCar Nov 01 '18 at 17:34
  • @RustyCar This option is valid for any `node {}` block (Which I saw you had in your pipeline example), at least it's worth a try (I think it should also work in scripted pipelines). Moreover, I recommend using declarative pipelines it easier to develop and maintain your code. – Raoslaw Szamszur Nov 01 '18 at 19:02
  • unfortunately, my script is pretty involved, and I seem to require a scripted pipeline. Also, I tried, and 'customWorkspace' is not available in 'node' of a scripted pipeline. Also, I tried 'simply' wrapping my entire pipeline script in some declarative stuff, and couldn't get it to work. That is, I removed the 'label' line from your example, and put all my code in the '// your pipeline logic here' area above, but wrapped in a 'script {' block. (Again, all my if/then/else-ing and the absolutely required 'for(line in tests)' line pretty well forces me in to scripted, IIUC). – RustyCar Nov 01 '18 at 19:49
  • Sorry, dumb question - how do I do an '@Raoslaw Szamszur' to link to him when there is a space in the user name??? Maybe just @Raoslaw, since it might assume I mean someone who has commented here? – RustyCar Nov 01 '18 at 19:50
  • @RustyCar I just type @ and first letter, and them click hint with username I want to reply to. I find it also confusing how to proceed with spaces :). You probably can move your pipeline into declarative one but it's not like you can copy it into //you pipeline logic here. Maby if you will show me whole pipeline code I can see what can be done (Unless you can't for some reasons). – Raoslaw Szamszur Nov 01 '18 at 19:59
  • Let's split this in to two sub-threads. I just tried typing '@R (without the '), and nothing happened. I decided to go read all the links in the help, and found that I should be able to say @raoslaws and it should work. We'll see. Ah, it did work, it just wasn't obvious. Ok, so use just the first name, and 'it will just work'. Sorry about that! – RustyCar Nov 01 '18 at 20:19
  • ok, so now on to the real question - can I send you the actual pipeline script. I'll have to ask my boss, and if so then we'll have to have a way other than posting it here, as it is 26K, for one, and for the other I'm pretty sure I won't be allowed to make it fully public... – RustyCar Nov 01 '18 at 20:23
  • @RustyCar I totally understand. In fact, I was assuming you're referring to an actual work case. BTW, I've added in edit some silly solutions, maybe they will work. – Raoslaw Szamszur Nov 01 '18 at 20:44
  • well, I tried getting the declarative pipeline to use a scripted subpipe, but for some reason (that I forgot), it didn't work. I even considered shared libs, but too many restrictions. Right now I got using 'ws(workspace_name)' inside EVERY node("master") block to work (sort of), but I still have 2 places to look for log files. I'm going to try to remove the 'customws' setting in the job config to see what happens - if it uses the job number for the workspace then I'm done. (and apologies - I keep forgetting that 'enter' means "SAVE EDITS', not go to new paragraph!) – RustyCar Nov 05 '18 at 18:14
1
'${SOMEVAR}'

will not get substituted

"${SOMEVAR}"

will - this is how groovy strings are being handled see groovy string handling

so if you have a

ws("/some/path/somewhere/${BUILD_ID}")
{
   //something
}

on your node in your pipeline Jenkinsfile it should do the trick in this regard

the problem with @2 workspaces can occur when you allow concurrent builds of the project - I had the exact same problem with a custom ws() with @2 - simply disallow concurrent builds or work around that.

  • Thanks for the thought - but I see I wasn't clear about where I'd put the ${BUILD_ID} - I'd put it in the 'custom workspace' field in Jenkins config. I've updated the question to make that more clear (and added my 'ws' issue). However, you DID bring up 'ws', which I've been trying to use, and that results in TWO places that the files go! You say 'disallow concurrent builds' - I've been assuming that will keep anyone from running the same job on different hosts - is that correct? (We need to run the same job on multiple hosts at the same time) – RustyCar Oct 31 '18 at 15:06
1

customWorkspace didn't work for me. What worked:

stages {
    stage("SCM (For commit trigger)"){
        steps {
            ws('custom-workspace') { // Because we don't want to switch from the pipeline checkout
                // Generated from http://lstool01:8080/job/Permanent%20Build/pipeline-syntax/
                checkout(xxx)
            }
        }
    }
Denny Weinberg
  • 2,492
  • 1
  • 21
  • 34
0

You need to use the ws block. If you can guarantee /some/path/on/all/nodes is common across them all your nodes, then you can just use one block across all your pipeline:

def wspath = "/some/path/on/all/nodes/${JOB_NAME}/${BUILD_ID}" 

ws(wspath) {
  node("master") {
  ...
  }
  node(HOST) {
  ...
  }
}

If you need more than one (e.g. on different nodes) just move the blocks around (they are basically closures so any code inside the block will operate in side that workspace path). Be sure and use JOB_NAME and BUILD_ID or else you'll have issues with two builds interfering.

Note that for jenkins scripted or declarative pipelines defined in an SCM, to bootstrap that pipeline, Jenkins must checkout a copy of the pipeline code on the local master node before any of your pipeline ws or node code can even run (see this writeup)

If that is giving you problems, you may need to tweak the SCM settings that is retrieving the pipeline.

qneill
  • 1,643
  • 14
  • 18