CleanWs not removing folder when used with docker

Hi, i’m currently facing a problem with the Workspace Cleanup Plugin.
My envoirment consists of : Jenkins 2.392 - Workspace Cleanup Plugin 0.44 - Ubuntu 22.04 .

In the pipeline the directory where to build the project is created by the built-in node.

Afterwards the docker agent execute the build and finally the built-in node creates the artifacts and execute the cleanWs() command.

The issue appears at this stage, the built-in node seems to be unable to delete the “foo_ws-cleanup_foo” folder created during the build. Is there any workaround about this?

Thinking about of some kind of permession issue i’ve also tried to add a cleanWs() command at the begin of the build; my hope was to have the leftover “foo_ws-cleanup_foo” folder to be cleaned up at the begin of the piepeline, but it didn’t work as well.

Thank you in advance.

Hi @velieri
whats the error it is throwing?

When i run this:

post {
        always {
            node(null){
                cleanWs(deleteDirs: true, disableDeferredWipeout: true)
            }
        }
    }

the error is:

[Pipeline] {
[Pipeline] cleanWs
[WS-CLEANUP] Deleting project workspace...
[WS-CLEANUP] Deferred wipeout is disabled by the job configuration...
ERROR: Cannot delete workspace :Unable to delete '/var/lib/jenkins/workspace/TestProject/preo-bsp/MS1372/buildroot/board/amarula/a64-relic/extlinux.conf'. Tried 3 times (of a maximum of 3) waiting 0.1 sec between attempts.
[Pipeline] }
[Pipeline] // node
Error when executing always post condition:
hudson.AbortException: Cannot delete workspace: Unable to delete '/var/lib/jenkins/workspace/TestProject/preo-bsp/MS1372/buildroot/board/amarula/a64-relic/extlinux.conf'. Tried 3 times (of a maximum of 3) waiting 0.1 sec between attempts.
	at hudson.plugins.ws_cleanup.WsCleanup.perform(WsCleanup.java:245)
	at jenkins.tasks.SimpleBuildStep.perform(SimpleBuildStep.java:123)
	at org.jenkinsci.plugins.workflow.steps.CoreStep$Execution.run(CoreStep.java:101)
	at org.jenkinsci.plugins.workflow.steps.CoreStep$Execution.run(CoreStep.java:71)
	at org.jenkinsci.plugins.workflow.steps.SynchronousNonBlockingStepExecution.lambda$start$0(SynchronousNonBlockingStepExecution.java:47)
	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at java.base/java.lang.Thread.run(Thread.java:829)

[Pipeline] }
[Pipeline] // stage
[Pipeline] End of Pipeline
ERROR: Cannot delete workspace: Unable to delete '/var/lib/jenkins/workspace/TestProject/preo-bsp/MS1372/buildroot/board/amarula/a64-relic/extlinux.conf'. Tried 3 times (of a maximum of 3) waiting 0.1 sec between attempts.
Finished: FAILURE

what are the permissions for /var/lib/jenkins/workspace/TestProject

The result of ls -al is:

drwxrwxrwx 4 jenkins jenkins 4096

That’s not valid declarative pipeline. Node {} is for scripted pipelines. If you drop the node I think you’ll have better results

Regarding the permession i’ve also tried to add

sh 'echo "ms" | sudo su root -c "chmod 777 ../../TestProject"'

but it leaded to the same error as above.

I tried to remove the

node(null){}

keeping just

cleanWs(deleteDirs: true, disableDeferredWipeout: true)

But it throws this error:

[Pipeline] { (Declarative: Post Actions)
[Pipeline] cleanWs
[Pipeline] error
Error when executing always post condition:
hudson.AbortException: Attempted to execute a step that requires a node context while ‘agent none’ was specified. Be sure to specify your own ‘node { ... }’ blocks when using ‘agent none’.
	at org.jenkinsci.plugins.workflow.steps.ErrorStep$Execution.run(ErrorStep.java:64)
	at org.jenkinsci.plugins.workflow.steps.ErrorStep$Execution.run(ErrorStep.java:51)
	at org.jenkinsci.plugins.workflow.steps.SynchronousStepExecution.start(SynchronousStepExecution.java:37)
	at org.jenkinsci.plugins.workflow.cps.DSL.invokeStep(DSL.java:322)
	at org.jenkinsci.plugins.workflow.cps.DSL.invokeMethod(DSL.java:196)
	at org.jenkinsci.plugins.workflow.cps.CpsScript.invokeMethod(CpsScript.java:124)
	at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.call(PogoMetaClassSite.java:47)
	at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47)
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:116)
	at com.cloudbees.groovy.cps.sandbox.DefaultInvoker.methodCall(DefaultInvoker.java:20)
	at org.jenkinsci.plugins.pipeline.modeldefinition.ModelInterpreter.catchRequiredContextForNode(ModelInterpreter.groovy:399)
	at org.jenkinsci.plugins.pipeline.modeldefinition.ModelInterpreter.catchRequiredContextForNode(ModelInterpreter.groovy:393)
	at org.jenkinsci.plugins.pipeline.modeldefinition.ModelInterpreter.runPostConditions(ModelInterpreter.groovy:760)
	at com.cloudbees.groovy.cps.CpsDefaultGroovyMethods.each(CpsDefaultGroovyMethods:2125)
	at com.cloudbees.groovy.cps.CpsDefaultGroovyMethods.each(CpsDefaultGroovyMethods:2110)
	at com.cloudbees.groovy.cps.CpsDefaultGroovyMethods.each(CpsDefaultGroovyMethods:2151)
	at org.jenkinsci.plugins.pipeline.modeldefinition.ModelInterpreter.runPostConditions(ModelInterpreter.groovy:750)
	at org.jenkinsci.plugins.pipeline.modeldefinition.ModelInterpreter.runPostConditions(ModelInterpreter.groovy)
	at org.jenkinsci.plugins.pipeline.modeldefinition.ModelInterpreter.executePostBuild(ModelInterpreter.groovy:728)
	at ___cps.transform___(Native Method)
	at com.cloudbees.groovy.cps.impl.ContinuationGroup.methodCall(ContinuationGroup.java:90)
	at com.cloudbees.groovy.cps.impl.FunctionCallBlock$ContinuationImpl.dispatchOrArg(FunctionCallBlock.java:116)
	at com.cloudbees.groovy.cps.impl.FunctionCallBlock$ContinuationImpl.fixArg(FunctionCallBlock.java:85)
	at jdk.internal.reflect.GeneratedMethodAccessor303.invoke(Unknown Source)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at com.cloudbees.groovy.cps.impl.ContinuationPtr$ContinuationImpl.receive(ContinuationPtr.java:72)
	at com.cloudbees.groovy.cps.impl.ContinuationGroup.methodCall(ContinuationGroup.java:93)
	at com.cloudbees.groovy.cps.impl.FunctionCallBlock$ContinuationImpl.dispatchOrArg(FunctionCallBlock.java:116)
	at com.cloudbees.groovy.cps.impl.FunctionCallBlock$ContinuationImpl.fixName(FunctionCallBlock.java:80)
	at jdk.internal.reflect.GeneratedMethodAccessor305.invoke(Unknown Source)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at com.cloudbees.groovy.cps.impl.ContinuationPtr$ContinuationImpl.receive(ContinuationPtr.java:72)
	at com.cloudbees.groovy.cps.impl.ConstantBlock.eval(ConstantBlock.java:21)
	at com.cloudbees.groovy.cps.Next.step(Next.java:83)
	at com.cloudbees.groovy.cps.Continuable$1.call(Continuable.java:152)
	at com.cloudbees.groovy.cps.Continuable$1.call(Continuable.java:146)
	at org.codehaus.groovy.runtime.GroovyCategorySupport$ThreadCategoryInfo.use(GroovyCategorySupport.java:136)
	at org.codehaus.groovy.runtime.GroovyCategorySupport.use(GroovyCategorySupport.java:275)
	at com.cloudbees.groovy.cps.Continuable.run0(Continuable.java:146)
	at org.jenkinsci.plugins.workflow.cps.SandboxContinuable.access$001(SandboxContinuable.java:18)
	at org.jenkinsci.plugins.workflow.cps.SandboxContinuable.run0(SandboxContinuable.java:51)
	at org.jenkinsci.plugins.workflow.cps.CpsThread.runNextChunk(CpsThread.java:187)
	at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.run(CpsThreadGroup.java:420)
	at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup$2.call(CpsThreadGroup.java:330)
	at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup$2.call(CpsThreadGroup.java:294)
	at org.jenkinsci.plugins.workflow.cps.CpsVmExecutorService$2.call(CpsVmExecutorService.java:67)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at hudson.remoting.SingleLaneExecutorService$1.run(SingleLaneExecutorService.java:139)
	at jenkins.util.ContextResettingExecutorService$1.run(ContextResettingExecutorService.java:28)
	at jenkins.security.ImpersonatingExecutorService$1.run(ImpersonatingExecutorService.java:68)
	at jenkins.util.ErrorLoggingExecutorService.lambda$wrap$0(ErrorLoggingExecutorService.java:51)
	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at java.base/java.lang.Thread.run(Thread.java:829)

[Pipeline] }
[Pipeline] // stage
[Pipeline] End of Pipeline
ERROR: Attempted to execute a step that requires a node context while ‘agent none’ was specified. Be sure to specify your own ‘node { ... }’ blocks when using ‘agent none’.
Finished: FAILURE

whats your full pipeline like? Sounds like you have no agent selected, which makes cleaning up the workdir seem not very useful.

This is the pipeline:

pipeline {
    agent none
    stages {
        stage('prepare build folder') {
            agent any
            steps {
            //cleanWs(disableDeferredWipeout: true, deleteDirs: true)
            sh 'mkdir MS1372'
            }
        }
        
        stage('git clone project') {
            agent any
            steps {

		foo               
                sh 'echo $(ls -d)'
                sh 'echo "ms" | sudo su root -c "chmod 777 ../../TestProject"'
                
            }
        }
        
        stage('Build Docker') {
            agent {
                docker {
                    image "ubuntu_jenkins"
                    args "-it -u root:sudo -v /var/lib/jenkins/TestProject/test/MS1372:/output"
                }
            }
            
            steps {
                sh 'git config --global --add safe.directory /var/lib/jenkins/workspace/TestProject/test/MS1372/buildroot'
                sh 'git config --global --add safe.directory /var/lib/jenkins/workspace/TestProject/test/MS1372/kernel'
                
				dir("MS1372/scripts") {
                    sh './buildTestBsp.sh'
                }
            }
        }
        
        stage('create artifact') {
            agent any
            steps {
                dir("MS1372") {
                    sh 'tar -cvf images.tar.gz ./scripts/build-test/images/'
                    archiveArtifacts artifacts: 'images.tar.gz', fingerprint: true
                }
                dir("MS1372/scripts/build-test/images") {
                    archiveArtifacts artifacts: '*.swu', fingerprint: true
                }
                dir("MS1372/scripts/build-test/images") {
                    archiveArtifacts artifacts: 'arm-buildroot-linux-gnueabihf_sdk-buildroot.tar.gz', fingerprint: true
                }
            }
        }
    }
    
	 post {
        always {
                cleanWs(deleteDirs: true, disableDeferredWipeout: true)
        }
    }
}

For what i can understand the usage of more than one agent requires the first agent none; then it is necessary to define, at every stage, which agent is going to perform the stage itself.
This leaded to the previous

post {
        always {
            node(null){
                cleanWs(deleteDirs: true, disableDeferredWipeout: true)
            }
        }
    }

That’s not a true statement. You only need to define a new agent when you want to use a different agent than the top level one.

Also agent none runs things on the controller.

I’m not even sure this is valid, but i would guess node of null will grab any random agent you have, or maybe the controller itself, but I wouldn’t know specifically.

I would personally add a reuseNode flag to the docker agent, so it runs that docker statement on the same agent as everything else.

I will say that jenkins won’t be able to clean up something written as root to /output from that docker container. You’ll have to chmod or chown any files in there inside the container before outside container can interact with them.

You can kinda seen an example of reuse node