Remove Executions in Rundeck
2 min read

Remove Executions in Rundeck

Remove Executions in Rundeck

TL;DR: You have to get the executions' list and remove the appropriate ones using hte API.

Once we've managed to get the list of job executions, we can use the API to remove the relevant executions. The command is rather simple:

def deleteExecutions(server, port, api_key, execution_ids):
    url =  server +':'+port+'/api/12/executions/delete?ids=' +
        ','.join(execution_ids)
    headers = {'Content-Length': '0', 'X-RunDeck-Auth-Token': api_key }
    requests.post(url, headers=headers, verify=False)

This method requires:

  • server - the Rundeck's server address
  • port - the port on which we can connect to the Rundeck server
  • api_key - the API key
  • execution_ids - an array of valid execution IDs

The function will try to remove all execution IDs in one go, since the API accepts a comma-separated list of executions IDs. In practice, one should build a list of maximum 20-30 items, as removing e.g. 1000 items in one go might time out and block the Rundeck instance until the time out.

Note: You can also remove a single execution:

def deleteExecution(server, port, api_key, execution_id):
    url =  server +':'+port+'/api/12/execution/'+execution_id
    headers = {
      'Content-Type': 'application/json',
      'X-RunDeck-Auth-Token': api_key
    }
    requests.delete(url, headers=headers, verify=False)

Helpers

Normally, the function above is called when the list of execution IDs is built based on some criteria. For example, we can choose to select only the executions older than a number of days. In this case, we can build a function to check agains a date:

EXPIRE_MILISECONDS = EXPIRE_DAYS * 24 * 60 * 60 * 1000

def isOlderThanExpireDays(execution_date, today):
    if ((today - execution_date) > EXPIRE_MILISECONDS):
        return True
    return False

where execution date is the timestamp of a specific execution and today is today's timestamp:

today = int(round(time.time() * 1000))

Note: I chose to pass today as a parameter because the script can take days to execute if you have e.g. 300,000+ executions.

function below implements the described logic:

BATCH_SIZE=20

def checkDeletion(server, port, api_key, execid_dates):
    deleted = 0
    count = 1
    arr = []
    for exec_id in execid_dates:
        today = int(round(time.time() * 1000))
        exec_date = execid_dates[exec_id]
        if isOlderThanExpireDays(int(exec_date), today):
            arr.append(exec_id)
            deleted += 1
        count += 1

    log.info('\t we have %d removable jobs' % len(arr))
    count = 0
    while len(arr):
        batch = []
        start = datetime.datetime.now()
        while len(batch) < BATCH_SIZE and len(arr):
            batch.append(arr.pop())
        deleteExecutions(server, port, api_key, batch)
        end = datetime.datetime.now()
        delta = end-start
        count += len(batch)
        log.info("\t Removed %7d of %7d @ %3.3f execs/s" %
            (count, deleted, (BATCH_SIZE * 1.0 /
                ( delta.seconds + delta.microseconds/1E6))))

    return deleted

To be more precise, the first loop builds the list ov delete-able executions and the second one remives one BATCH_SIZE at a time.

HTH,