Leoncino
Leoncino

Reputation: 126

Run cleanup function in Python when Jenkins job is aborted

I wrote a script in Python that is running for some time and performing a clean up as soon as it terminates regularly like:

import time

def cleanUp():
     #delete some temporary files and so on
     print("Clean up done")

if __name__=="__main__":
     startTime = time.time()
     while time.time() - startTime < 60:
          # Performing some commands
          print("Still running")
     
     cleanUp()

I'm running this code as a Jenkins job. In order to clean up no matter what, i.e. also when I abort the Jenkins job, I used signal.signal. Doing research on the signal that is sent by Jenkins in order to terminate a job (https://gist.github.com/datagrok/dfe9604cb907523f4a2f) resulted that Jenkins sends "TERM" signal upon stopping the process. Therefore I added the following + a control mechanism to see whether it works, as Jenkins will not show the rest of the log after stopping a job (will immediately show "ABORTED", see link):

import time
import signal

pythonObj = None

def cleanUp():
     # object that I defined globally, reconstructing it in a separate script is quite cumbersome
     global pythonObj
     #delete some temporary files and so on
     print("Clean up done")
     # Write some text to a file, to see if this part was actually run
     with open(r"C:\temp\abx.txt", 'w') as file:
        file.write("Process has been finished prematurely")

signal.signal(signal.SIGTERM, cleanUp)

if __name__=="__main__":
     startTime = time.time()
     while time.time() - startTime < 60:
          # Creation of python object
          pythonObj = someFunct()
          print("Still running")
     
     cleanUp()

Sadly, I cannot find the "control" file in C:\temp of the PC on which the Jenkins Job is run.

My question: Is it a proper approach to use signal.signal(signal.SIGTERM, cleanUp) (or should I use something else?) or did I make mistake in the usage of signal.signal?

I also tried to register cleanUp with atexit but this didn't give me the results I hoped for either.

EDIT 1: One thing that I didn't mention above is the fact, that I need to hand over some complex inputs to the cleanUp function. I have already thought about using post build actions within a pipeline.

EDIT 2: As mentioned below, in theory it would be possible to hand over the inputs to recreate the object. However, this recreation is quite cumbersome and since I only want to have a quick clean up, which is not possible if I need to recreate the whole process, I try to avoid this option.

Thanks a lot!

Upvotes: -2

Views: 218

Answers (1)

Alexander Pletnev
Alexander Pletnev

Reputation: 3303

A proper approach would be to use the built-in functionality - Jenkinsfile syntax has a block specifically dedicated for your use case:

From Pipeline Syntax:

The post section defines one or more additional steps that are run upon the completion of a Pipeline’s or stage’s run (depending on the location of the post section within the Pipeline). post can support any of the following post-condition blocks: always, changed, fixed, regression, aborted, failure, success, unstable, unsuccessful, and cleanup. These condition blocks allow the execution of steps inside each condition depending on the completion status of the Pipeline or stage. The condition blocks are executed in the order shown below.

// Jenkinsfile
def arg1 = 'one'
def arg2

pipeline {
  // ...
  stages {
    stage('Some build logic') {
      steps {
        script {
          arg2 = 'two'
          env.ARG3 = 'three'
        }
        // build steps
      }
    }
  }
  post {
    aborted {
      sh "python3 cleanup.py $arg1 $arg2"
    }
  }
}

Depending on your goal, you can choose any post condition(s) to use, and combine them, too.

I need to hand over some inputs to the cleanUp function

In your example, cleanUp() does not take any arguments. But nevertheless, the syntax of aborted, cleanup, or any other post block is the same as of steps within any stage:

Post-condition blocks contain steps the same as the steps section.

That means you have the same control over the variables - you can set them in the previous stages and use in your function. Note the change of single quotes to double quotes to enable the Groovy string interpolation.

Upvotes: 1

Related Questions