Dynamically Extract Hostnames from Ansible Inventory for Jenkins Active Choices Plugin

54 views Asked by At

I'm working on integrating Ansible with Jenkins and need to dynamically extract hostnames from an Ansible inventory file to populate choices in a Jenkins job using the Active Choices plugin.

I have a script that functions correctly when executed as a separate job in Jenkins. However, when I attempt to run it within a plugin, it doesn't work. I'm unsure how to debug this issue or understand why it's failing in the plugin context. Could someone provide insights on debugging scripts within Jenkins plugins or suggest why it might not be executing as expected?

Here's what I've tried:

properties([
    parameters([
        activeChoice(
            choiceType: 'PT_SINGLE_SELECT', 
            filterLength: 1, 
            filterable: false, 
            name: 'cloud', 
            randomName: 'choice-parameter-291132897896439385', 
            script: groovyScript(
                fallbackScript: [
                    classpath: [], 
                    oldScript: '', 
                    sandbox: false, 
                    script: ''
                ], 
            script: [
               classpath: [], 
               oldScript: '', 
               sandbox: false, 
               script: 
                  '''def getHostList(String envir, String envGroup) {
    node("slave-python") {
        
        def hosts = []
        checkout scmGit(branches: 
           [[name: \'*/master\']], 
           extensions: [
               cloneOption(
                   depth: 1, 
                   noTags: false, 
                   reference: \'\', 
                   shallow: true
                )
            ], 
            userRemoteConfigs: [
                [
                    credentialsId: \'jhgfghghf\', 
                    url: \'[email protected]:ansible/env.git\'
                ]
            ]
        )
        
        withCredentials([file(credentialsId: \'vault_file\', variable: \'ansibleVaultKeyFile\')]) {
            sh \'ls -lha\'
            
            String output = sh returnStdout: true, script: "ansible-inventory -i inventories/inventory.yml --list --vault-password-file ${ansibleVaultKeyFile}| jq -r \\\'.[] | select(.hosts != null) | .hosts | .[]\\\' | sort -u |grep \\\'${envGroup}\\\'"
            
            hosts = output.trim().split(\'\\n\').collect { "\'${it}\'" }.join(\', \')
        }
        println("[$hosts]")
    
        return hosts.tokenize()
        
    }
}
return getHostList(\'env-test\', \'kuber\')''']))])])

My goals are:

  1. Dynamically extract hostnames from an Ansible inventory.
  2. Use these hostnames to populate an Active Choices parameter in a Jenkins job.

Questions:

  1. How can I modify my script to correctly extract hostnames from the Ansible inventory?
  2. Is there a better approach or plugin for integrating Ansible inventory hostnames into Jenkins Active Choices?
1

There are 1 answers

0
Alexander Pletnev On

Questions: How can I modify my script to correctly extract hostnames from the Ansible inventory? Is there a better approach or plugin for integrating Ansible inventory hostnames into Jenkins Active Choices?

I'm not going to call it a better approach, but I use a different one - I find it a bit simpler, more reliable, and safer as it helps to deal with one of the major Jenkins issues of not being able to process the parameters before the build launch. You might want to apply all of the suggestions or only part of them, depending on your situation.

First, since ansible-inventory can return a valid JSON or YAML, it might be simpler and safer to just redirect the stdout into a file and load it using readJson or readYaml, and extract the data directly from a JSON object, skipping jq that might be even not installed. Note I'm not saying anything about parsing the inventory file because one can use multiple inventories.

Next, if I correctly understand the usage of grep in your command, you could avoid that by applying --limit that is also supported by ansible-inventory. Be careful with it, though - if it's a free text parameter, it's possible to make an injection and steal the vault password, for example. That's the reason why I configure everything using the environment variables instead of CLI arguments wherever possible.

Finally, and that's my favorite Jenkins trick that I've picked up from the endless comments to Jenkins Jira issues, I define the parameters and their values dynamically and stop the build right after that using currentBuild.result = 'NOT_BUILT' and error with a meaningful message and a link to Build with parameters page. I run this as a separate stage enabled when the build number equals 1 or some dedicated boolean build parameter is true.

This allows to:

  • populate the parameters dynamically without use of the external (probably buggy or vulnerable) plugins;
  • do the same without approving the custom Groovy scripts in Jenkins admin panel - likely, Jenkins may require that for Active Choice scripts as they don't run in a Groovy sandbox as far as I know, and that actually might be the cause of your issues;
  • overcome one of the most annoying Jenkins issues, especially when working with multibranch pipelines because every new branch starts with a build #1 there.