├── .gitattributes
├── .github
├── CODEOWNERS
├── dependabot.yml
└── workflows
│ ├── cd.yaml
│ └── jenkins-security-scan.yml
├── .mvn
├── maven.config
└── extensions.xml
├── .git-blame-ignore-revs
├── src
├── main
│ ├── resources
│ │ ├── org
│ │ │ └── jenkinsci
│ │ │ │ └── plugins
│ │ │ │ └── ansible
│ │ │ │ ├── AnsibleVaultBuilder
│ │ │ │ ├── help-action.html
│ │ │ │ ├── help-content.html
│ │ │ │ ├── help-input.html
│ │ │ │ ├── help-output.html
│ │ │ │ ├── help-vaultCredentialsId.html
│ │ │ │ ├── help-vaultTmpPath.html
│ │ │ │ ├── help-newVaultCredentialsId.html
│ │ │ │ ├── config.properties
│ │ │ │ └── config.jelly
│ │ │ │ ├── AnsibleAdHocCommandBuilder
│ │ │ │ ├── help-command.html
│ │ │ │ ├── help-module.html
│ │ │ │ ├── help-becomeUser.html
│ │ │ │ ├── help-forks.html
│ │ │ │ ├── help-credentialsId.html
│ │ │ │ ├── help-become.html
│ │ │ │ ├── help-disableHostKeyChecking.html
│ │ │ │ ├── help-vaultCredentialsId.html
│ │ │ │ ├── help-vaultTmpPath.html
│ │ │ │ ├── config.properties
│ │ │ │ ├── help-sudoUser.html
│ │ │ │ ├── help-additionalParameters.html
│ │ │ │ ├── help-sudo.html
│ │ │ │ ├── help-unbufferedOutput.html
│ │ │ │ ├── help-hostPattern.html
│ │ │ │ ├── help-colorizedOutput.html
│ │ │ │ └── config.jelly
│ │ │ │ ├── AnsiblePlaybookBuilder
│ │ │ │ ├── help-tags.html
│ │ │ │ ├── help-limit.html
│ │ │ │ ├── help-startAtTask.html
│ │ │ │ ├── help-becomeUser.html
│ │ │ │ ├── help-skippedTags.html
│ │ │ │ ├── help-forks.html
│ │ │ │ ├── help-playbook.html
│ │ │ │ ├── help-credentialsId.html
│ │ │ │ ├── help-become.html
│ │ │ │ ├── help-checkMode.html
│ │ │ │ ├── help-disableHostKeyChecking.html
│ │ │ │ ├── help-vaultCredentialsId.html
│ │ │ │ ├── help-vaultTmpPath.html
│ │ │ │ ├── config.properties
│ │ │ │ ├── help-sudoUser.html
│ │ │ │ ├── help-additionalParameters.html
│ │ │ │ ├── help-sudo.html
│ │ │ │ ├── help-unbufferedOutput.html
│ │ │ │ ├── help-colorizedOutput.html
│ │ │ │ └── config.jelly
│ │ │ │ ├── InventoryPath
│ │ │ │ ├── help-path.html
│ │ │ │ └── config.jelly
│ │ │ │ ├── InventoryDoNotSpecify
│ │ │ │ └── config.jelly
│ │ │ │ ├── workflow
│ │ │ │ ├── AnsiblePlaybookStep
│ │ │ │ │ ├── help.html
│ │ │ │ │ └── config.jelly
│ │ │ │ ├── AnsibleVaultStep
│ │ │ │ │ ├── help.html
│ │ │ │ │ └── config.jelly
│ │ │ │ └── AnsibleAdHocStep
│ │ │ │ │ ├── help.html
│ │ │ │ │ └── config.jelly
│ │ │ │ ├── InventoryContent
│ │ │ │ ├── help-dynamic.html
│ │ │ │ └── config.jelly
│ │ │ │ └── AnsibleInstallation
│ │ │ │ └── config.jelly
│ │ └── index.jelly
│ └── java
│ │ └── org
│ │ └── jenkinsci
│ │ └── plugins
│ │ └── ansible
│ │ ├── AnsibleInvocationException.java
│ │ ├── jobdsl
│ │ ├── context
│ │ │ ├── ExtraVarsContext.java
│ │ │ └── AnsibleContext.java
│ │ └── AnsibleJobDslExtension.java
│ │ ├── AnsibleCommand.java
│ │ ├── InventoryDoNotSpecify.java
│ │ ├── CLIRunner.java
│ │ ├── InventoryPath.java
│ │ ├── Inventory.java
│ │ ├── ExtraVar.java
│ │ ├── InventoryContent.java
│ │ ├── AnsibleAdHocCommandInvocation.java
│ │ ├── Utils.java
│ │ ├── AnsiblePlaybookInvocation.java
│ │ ├── AbstractAnsibleBuilderDescriptor.java
│ │ ├── AnsibleInstallation.java
│ │ ├── AnsibleVaultInvocation.java
│ │ ├── AnsibleVaultBuilder.java
│ │ ├── workflow
│ │ ├── AnsibleVaultStep.java
│ │ └── AnsibleAdhocStep.java
│ │ ├── AnsibleAdHocCommandBuilder.java
│ │ ├── AbstractAnsibleInvocation.java
│ │ └── AnsiblePlaybookBuilder.java
└── test
│ ├── resources
│ ├── jobdsl
│ │ ├── vault.groovy
│ │ ├── adhoc.groovy
│ │ ├── checkMode.groovy
│ │ ├── playbookBuilder.groovy
│ │ ├── expander.groovy
│ │ ├── security630.groovy
│ │ ├── playbook.groovy
│ │ └── legacyPlaybook.groovy
│ ├── pipelines
│ │ ├── minimal.groovy
│ │ ├── minimalCheckMode.groovy
│ │ ├── extraVarsHiddenString.groovy
│ │ ├── extraVarsNumeric.groovy
│ │ ├── extraVarsBoolean.groovy
│ │ ├── vaultCredentialsFile.groovy
│ │ ├── ansiblePlaybookSshPass.groovy
│ │ ├── vaultCredentialsString.groovy
│ │ ├── adhocCommand.groovy
│ │ ├── vaultTmpPath.groovy
│ │ ├── vaultCredentialsFileViaExtras.groovy
│ │ └── extraVarsMap.groovy
│ ├── docker
│ │ └── Dockerfile
│ └── org
│ │ └── jenkinsci
│ │ └── plugins
│ │ └── ansible
│ │ └── CompatibilityTest
│ │ └── jobs
│ │ └── old
│ │ └── config.xml
│ └── java
│ └── org
│ └── jenkinsci
│ └── plugins
│ └── ansible
│ ├── CompatibilityTest.java
│ ├── AnsibleVaultInvocationTest.java
│ ├── AnsibleAdHocCommandInvocationTest.java
│ └── jobdsl
│ └── JobDslIntegrationTest.java
├── .gitignore
├── docs
└── images
│ └── jenkins-deploy-ansible-console.png
├── Jenkinsfile
├── LICENSE.md
└── pom.xml
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text eol=lf
2 | *.png binary
3 |
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @jenkinsci/ansible-plugin-developers
2 |
--------------------------------------------------------------------------------
/.mvn/maven.config:
--------------------------------------------------------------------------------
1 | -Pconsume-incrementals
2 | -Pmight-produce-incrementals
3 | -Dchangelist.format=%d.v%s
--------------------------------------------------------------------------------
/.git-blame-ignore-revs:
--------------------------------------------------------------------------------
1 | # .git-blame-ignore-revs
2 | # Spotless formatting
3 | 6eb11c018eb7640cf1dbf1fd5642355e0286058e
4 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/AnsibleVaultBuilder/help-action.html:
--------------------------------------------------------------------------------
1 |
2 | Desired vault action.
3 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/AnsibleVaultBuilder/help-content.html:
--------------------------------------------------------------------------------
1 |
2 | Desired encrypted content.
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /target/
2 | *~
3 | .DS_Store
4 | /work/
5 | /.idea/
6 | *.iml
7 | .classpath
8 | .project
9 | .settings/
10 | .java-version
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/AnsibleVaultBuilder/help-input.html:
--------------------------------------------------------------------------------
1 |
2 | Desired input file to encrypt.
3 |
--------------------------------------------------------------------------------
/docs/images/jenkins-deploy-ansible-console.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/ansible-plugin/main/docs/images/jenkins-deploy-ansible-console.png
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/AnsibleVaultBuilder/help-output.html:
--------------------------------------------------------------------------------
1 |
2 | Desired output file for encrypted content.
3 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/AnsibleAdHocCommandBuilder/help-command.html:
--------------------------------------------------------------------------------
1 |
2 | Module arguments or shell command to execute
3 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/AnsiblePlaybookBuilder/help-tags.html:
--------------------------------------------------------------------------------
1 |
2 | Only run plays and tasks tagged with these values.
3 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/AnsiblePlaybookBuilder/help-limit.html:
--------------------------------------------------------------------------------
1 |
2 | Further limit selected hosts to an additional pattern.
3 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/AnsiblePlaybookBuilder/help-startAtTask.html:
--------------------------------------------------------------------------------
1 |
2 | Start the playbook at the task matching this name.
3 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/InventoryPath/help-path.html:
--------------------------------------------------------------------------------
1 |
2 | Specify the inventory host path or a comma separated host list
3 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/index.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 | Invoke
Ansible Ad-Hoc commands and playbooks.
4 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/InventoryDoNotSpecify/config.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/AnsibleAdHocCommandBuilder/help-module.html:
--------------------------------------------------------------------------------
1 |
2 | Module name to execute. The shell module is used when left empty.
3 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/AnsiblePlaybookBuilder/help-becomeUser.html:
--------------------------------------------------------------------------------
1 |
2 | Desired become user. "root" is used when this field is empty.
3 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/AnsiblePlaybookBuilder/help-skippedTags.html:
--------------------------------------------------------------------------------
1 |
2 | only run plays and tasks whose tags do not match these values.
3 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/AnsibleAdHocCommandBuilder/help-becomeUser.html:
--------------------------------------------------------------------------------
1 |
2 | Desired become user. "root" is used when this field is empty.
3 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/AnsiblePlaybookBuilder/help-forks.html:
--------------------------------------------------------------------------------
1 |
2 | Specify number of parallel processes to use. Set to 0 to use the default value.
3 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/AnsibleAdHocCommandBuilder/help-forks.html:
--------------------------------------------------------------------------------
1 |
2 | Specify number of parallel processes to use. Set to 0 to use the default value.
3 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/workflow/AnsiblePlaybookStep/help.html:
--------------------------------------------------------------------------------
1 |
2 | Execute an Ansible playbook. Only the playbook parameter is mandatory.
3 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/workflow/AnsibleVaultStep/help.html:
--------------------------------------------------------------------------------
1 |
2 | Execute Ansible vault. Only the vaultCredentialsId parameter is mandatory.
3 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/AnsiblePlaybookBuilder/help-playbook.html:
--------------------------------------------------------------------------------
1 |
2 | Path to the ansible playbook file. The path can be absolute or relative to the job workspace.
3 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/AnsiblePlaybookBuilder/help-credentialsId.html:
--------------------------------------------------------------------------------
1 |
2 | Select the credentials for the SSH connections. Only private key authentication is supported.
3 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/AnsibleAdHocCommandBuilder/help-credentialsId.html:
--------------------------------------------------------------------------------
1 |
2 | Select the credentials for the SSH connections. Only private key authentication is supported.
3 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/AnsiblePlaybookBuilder/help-become.html:
--------------------------------------------------------------------------------
1 |
2 | Run operations with become. It works only when the remote user is sudoer with nopasswd option.
3 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/AnsiblePlaybookBuilder/help-checkMode.html:
--------------------------------------------------------------------------------
1 |
2 | Run ansible with check mode enabled. This will not make any changes on the remote system (--check)
3 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/AnsibleAdHocCommandBuilder/help-become.html:
--------------------------------------------------------------------------------
1 |
2 | Run operations with become. It works only when the remote user is sudoer with nopasswd option.
3 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/AnsiblePlaybookBuilder/help-disableHostKeyChecking.html:
--------------------------------------------------------------------------------
1 |
2 | Check this box if you really want to disable the validation of the hosts SSH server keys.
3 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/AnsiblePlaybookBuilder/help-vaultCredentialsId.html:
--------------------------------------------------------------------------------
1 |
2 | Select the credentials for vault encrypted vars. Only secret file and secret text are supported.
3 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/AnsibleAdHocCommandBuilder/help-disableHostKeyChecking.html:
--------------------------------------------------------------------------------
1 |
2 | Check this box if you really want to disable the validation of the hosts SSH server keys.
3 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/AnsibleAdHocCommandBuilder/help-vaultCredentialsId.html:
--------------------------------------------------------------------------------
1 |
2 | Select the credentials for vault encrypted vars. Only secret file and secret text are supported.
3 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/AnsibleVaultBuilder/help-vaultCredentialsId.html:
--------------------------------------------------------------------------------
1 |
2 | Select the credentials for vault encrypted vars. Only Jenkins secret file and secret text are supported.
3 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/workflow/AnsibleAdHocStep/help.html:
--------------------------------------------------------------------------------
1 |
2 | Execute an Ansible Adhoc command. Only hosts and moduleArguments parameters are mandatories.
3 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/AnsiblePlaybookBuilder/help-vaultTmpPath.html:
--------------------------------------------------------------------------------
1 |
2 | Insert the path where to store temporary generated vault password files, ssh keys, etc... Default is in workspace.
3 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/AnsibleVaultBuilder/help-vaultTmpPath.html:
--------------------------------------------------------------------------------
1 |
2 | Insert the path where to store temporary generated vault password files, ssh keys, etc... Default is in workspace.
3 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/AnsibleAdHocCommandBuilder/help-vaultTmpPath.html:
--------------------------------------------------------------------------------
1 |
2 | Insert the path where to store temporary generated vault password files, ssh keys, etc... Default is in workspace.
3 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/AnsibleVaultBuilder/help-newVaultCredentialsId.html:
--------------------------------------------------------------------------------
1 |
2 | Select the credentials for rekeying vault encrypted files. Only Jenkins secret file and secret text are supported.
3 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/AnsibleVaultBuilder/config.properties:
--------------------------------------------------------------------------------
1 | AnsibleInstallation.error=Needs to know where Ansible is installed. \
2 | Please do so from the system configuration .
3 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/AnsibleAdHocCommandBuilder/config.properties:
--------------------------------------------------------------------------------
1 | AnsibleInstallation.error=Needs to know where Ansible is installed. \
2 | Please do so from the system configuration .
3 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/AnsibleAdHocCommandBuilder/help-sudoUser.html:
--------------------------------------------------------------------------------
1 |
2 | Desired sudo user. "root" is used when this field is empty. Sudo has been deprecated in favor of become and will be removed in Ansible 2.6.
3 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/AnsiblePlaybookBuilder/config.properties:
--------------------------------------------------------------------------------
1 | AnsibleInstallation.error=Needs to know where Ansible is installed. \
2 | Please do so from the system configuration .
3 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/AnsiblePlaybookBuilder/help-sudoUser.html:
--------------------------------------------------------------------------------
1 |
2 | Desired sudo user. "root" is used when this field is empty. Sudo has been deprecated in favor of become and will be removed in Ansible 2.6.
3 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/AnsiblePlaybookBuilder/help-additionalParameters.html:
--------------------------------------------------------------------------------
1 |
2 | Any additional parameters to pass to the ansible command.
3 |
Warning: The content of this textbox will be passed as is to the command line.
4 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/AnsibleAdHocCommandBuilder/help-additionalParameters.html:
--------------------------------------------------------------------------------
1 |
2 | Any additional parameters to pass to the ansible command.
3 |
Warning: The content of this textbox will be passed as is to the command line.
4 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/AnsiblePlaybookBuilder/help-sudo.html:
--------------------------------------------------------------------------------
1 |
2 | Run operations with sudo. It works only when the remote user is sudoer with nopasswd option. Sudo has been deprecated in favor of become and will be removed in Ansible 2.6.
3 |
4 |
--------------------------------------------------------------------------------
/src/test/resources/jobdsl/vault.groovy:
--------------------------------------------------------------------------------
1 | freeStyleJob('ansible') {
2 | steps {
3 | ansibleVault() {
4 | action('encrypt_string')
5 | content('my_secret')
6 | vaultCredentialsId('ansible_vault_credentials')
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/AnsibleAdHocCommandBuilder/help-sudo.html:
--------------------------------------------------------------------------------
1 |
2 | Run operations with sudo. It works only when the remote user is sudoer with nopasswd option. Sudo has been deprecated in favor of become and will be removed in Ansible 2.6.
3 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/InventoryContent/help-dynamic.html:
--------------------------------------------------------------------------------
1 |
2 | Check this box if a dynamic inventory is used. For more details see the ansible documentation for
Dynamic Inventory
4 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/AnsiblePlaybookBuilder/help-unbufferedOutput.html:
--------------------------------------------------------------------------------
1 |
2 | Skip standard output buffering for the ansible process. The ansible output is directly rendered into the Jenkins
3 | console. This option can be usefull for long running operations.
4 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/InventoryPath/config.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/AnsibleAdHocCommandBuilder/help-unbufferedOutput.html:
--------------------------------------------------------------------------------
1 |
2 | Skip standard output buffering for the ansible process. The ansible output is directly rendered into the Jenkins
3 | console. This option can be usefull for long running operations.
4 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/AnsibleAdHocCommandBuilder/help-hostPattern.html:
--------------------------------------------------------------------------------
1 |
2 | The host or set of hosts on which the command will be executed. For more details see the ansible documentation
3 | for
Patterns .
4 |
--------------------------------------------------------------------------------
/src/test/resources/jobdsl/adhoc.groovy:
--------------------------------------------------------------------------------
1 | freeStyleJob('ansible') {
2 | steps {
3 | ansibleAdHoc('module', 'command') {
4 | ansibleName('1.9.1')
5 | credentialsId('credsid')
6 | hostPattern('pattern')
7 | inventoryContent('content')
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/src/test/resources/jobdsl/checkMode.groovy:
--------------------------------------------------------------------------------
1 | freeStyleJob('ansible') {
2 | steps {
3 | ansiblePlaybook('path/playbook.yml') {
4 | inventoryPath('hosts.ini')
5 | ansibleName('1.9.4')
6 | limit('retry.limit')
7 | checkMode(true)
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/test/resources/pipelines/minimal.groovy:
--------------------------------------------------------------------------------
1 | pipeline {
2 | agent {
3 | label('test-agent')
4 | }
5 | stages {
6 | stage('Ansible playbook') {
7 | steps {
8 | ansiblePlaybook(playbook: '/ansible/playbook.yml')
9 | }
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/AnsiblePlaybookBuilder/help-colorizedOutput.html:
--------------------------------------------------------------------------------
1 |
2 | Check this box to allow ansible to render ANSI color codes in the Jenkins console.
3 | This option works well with the
Jenkins AnsiColor plugin .
4 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/AnsibleAdHocCommandBuilder/help-colorizedOutput.html:
--------------------------------------------------------------------------------
1 |
2 | Check this box to allow ansible to render ANSI color codes in the Jenkins console.
3 | This option works well with the
Jenkins AnsiColor plugin .
4 |
--------------------------------------------------------------------------------
/src/test/resources/pipelines/minimalCheckMode.groovy:
--------------------------------------------------------------------------------
1 | pipeline {
2 | agent {
3 | label('test-agent')
4 | }
5 | stages {
6 | stage('Ansible playbook') {
7 | steps {
8 | ansiblePlaybook(playbook: '/ansible/playbook.yml', checkMode: true)
9 | }
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/AnsibleInstallation/config.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
2 |
3 | version: 2
4 | updates:
5 | - package-ecosystem: "maven"
6 | directory: "/"
7 | schedule:
8 | interval: "weekly"
9 | - package-ecosystem: "github-actions"
10 | directory: "/"
11 | schedule:
12 | interval: "weekly"
13 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/InventoryContent/config.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/test/resources/pipelines/extraVarsHiddenString.groovy:
--------------------------------------------------------------------------------
1 | pipeline {
2 | agent {
3 | label('test-agent')
4 | }
5 | stages {
6 | stage('Ansible playbook') {
7 | steps {
8 | ansiblePlaybook(
9 | playbook: '/ansible/playbook.yml',
10 | extraVars: [foo: 'bar'],
11 | )
12 | }
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/test/resources/pipelines/extraVarsNumeric.groovy:
--------------------------------------------------------------------------------
1 | pipeline {
2 | agent {
3 | label('test-agent')
4 | }
5 | stages {
6 | stage('Ansible playbook') {
7 | steps {
8 | ansiblePlaybook(
9 | playbook: '/ansible/playbook.yml',
10 | extraVars: [foo1: 8],
11 | )
12 | }
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/test/resources/pipelines/extraVarsBoolean.groovy:
--------------------------------------------------------------------------------
1 | pipeline {
2 | agent {
3 | label('test-agent')
4 | }
5 | stages {
6 | stage('Ansible playbook') {
7 | steps {
8 | ansiblePlaybook(
9 | playbook: '/ansible/playbook.yml',
10 | extraVars: [foo1: true, foo2: false],
11 | )
12 | }
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/.mvn/extensions.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | io.jenkins.tools.incrementals
4 | git-changelist-maven-extension
5 | 1.13
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/test/resources/pipelines/vaultCredentialsFile.groovy:
--------------------------------------------------------------------------------
1 | pipeline {
2 | agent {
3 | label('test-agent')
4 | }
5 | stages {
6 | stage('Ansible playbook') {
7 | steps {
8 | ansiblePlaybook(
9 | playbook: '/ansible/playbook.yml',
10 | vaultCredentialsId: 'vaultCredentialsFile',
11 | )
12 | }
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/test/resources/pipelines/ansiblePlaybookSshPass.groovy:
--------------------------------------------------------------------------------
1 | pipeline {
2 | agent {
3 | label('test-agent')
4 | }
5 | stages {
6 | stage('Ansible playbook') {
7 | steps {
8 | ansiblePlaybook(
9 | playbook: '/ansible/playbook.yml',
10 | credentialsId: 'usernamePasswordCredentialsId',
11 | )
12 | }
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/test/resources/pipelines/vaultCredentialsString.groovy:
--------------------------------------------------------------------------------
1 | pipeline {
2 | agent {
3 | label('test-agent')
4 | }
5 | stages {
6 | stage('Ansible playbook') {
7 | steps {
8 | ansiblePlaybook(
9 | playbook: '/ansible/playbook.yml',
10 | vaultCredentialsId: 'vaultCredentialsString',
11 | )
12 | }
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/ansible/AnsibleInvocationException.java:
--------------------------------------------------------------------------------
1 | package org.jenkinsci.plugins.ansible;
2 |
3 | /**
4 | * Created with IntelliJ IDEA.
5 | * User: sirot
6 | * Date: 04/05/15
7 | * Time: 23:30
8 | * To change this template use File | Settings | File Templates.
9 | */
10 | public class AnsibleInvocationException extends Exception {
11 |
12 | public AnsibleInvocationException(String message) {
13 | super(message);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/test/resources/pipelines/adhocCommand.groovy:
--------------------------------------------------------------------------------
1 | pipeline {
2 | agent {
3 | label('test-agent')
4 | }
5 | stages {
6 | stage('Ansible adhoc command') {
7 | steps {
8 | ansibleAdhoc(
9 | inventory: '/ansible/inventory.yml',
10 | hosts: '127.0.0.1',
11 | moduleArguments: 'echo something',
12 | )
13 | }
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/.github/workflows/cd.yaml:
--------------------------------------------------------------------------------
1 | # Note: additional setup is required, see https://www.jenkins.io/redirect/continuous-delivery-of-plugins
2 |
3 | name: cd
4 | on:
5 | workflow_dispatch:
6 | check_run:
7 | types:
8 | - completed
9 |
10 | jobs:
11 | maven-cd:
12 | uses: jenkins-infra/github-reusable-workflows/.github/workflows/maven-cd.yml@v1
13 | secrets:
14 | MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }}
15 | MAVEN_TOKEN: ${{ secrets.MAVEN_TOKEN }}
16 |
--------------------------------------------------------------------------------
/src/test/resources/pipelines/vaultTmpPath.groovy:
--------------------------------------------------------------------------------
1 | pipeline {
2 | agent {
3 | label('test-agent')
4 | }
5 | stages {
6 | stage('Ansible playbook') {
7 | steps {
8 | ansiblePlaybook(
9 | playbook: '/ansible/playbook.yml',
10 | vaultCredentialsId: 'vaultCredentialsFile',
11 | vaultTmpPath: '/ansible/tmp',
12 | )
13 | }
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Jenkinsfile:
--------------------------------------------------------------------------------
1 | /*
2 | See the documentation for more options:
3 | https://github.com/jenkins-infra/pipeline-library/
4 | */
5 | buildPlugin(
6 | forkCount: '1C', // run this number of tests in parallel for faster feedback. If the number terminates with a 'C', the value will be multiplied by the number of available CPU cores
7 | useContainerAgent: false, // Set to `false` if you need to use Docker for containerized tests
8 | configurations: [
9 | [platform: 'linux', jdk: 21],
10 | [platform: 'windows', jdk: 17],
11 | [platform: 'linux', jdk: 25],
12 | ])
13 |
--------------------------------------------------------------------------------
/src/test/resources/jobdsl/playbookBuilder.groovy:
--------------------------------------------------------------------------------
1 | freeStyleJob('ansible') {
2 | steps {
3 | ansiblePlaybookBuilder {
4 | playbook('path/playbook.yml')
5 | inventory {
6 | inventoryDoNotSpecify()
7 | }
8 | unbufferedOutput(true)
9 | extraVars {
10 | extraVar {
11 | key('key')
12 | secretValue(hudson.util.Secret.fromString('value'))
13 | hidden(true)
14 | }
15 | }
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/test/resources/pipelines/vaultCredentialsFileViaExtras.groovy:
--------------------------------------------------------------------------------
1 | pipeline {
2 | agent {
3 | label('test-agent')
4 | }
5 | stages {
6 | stage('Ansible playbook') {
7 | steps {
8 | withCredentials([file(credentialsId: 'vaultCredentialsFileViaExtras', variable: 'VAULT_FILE')]) {
9 | ansiblePlaybook(
10 | playbook: '/ansible/playbook.yml',
11 | extras: '--vault-password-file $VAULT_FILE',
12 | )
13 | }
14 | }
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/test/resources/pipelines/extraVarsMap.groovy:
--------------------------------------------------------------------------------
1 | pipeline {
2 | agent {
3 | label('test-agent')
4 | }
5 | stages {
6 | stage('Ansible playbook') {
7 | steps {
8 | ansiblePlaybook(
9 | playbook: '/ansible/playbook.yml',
10 | extraVars: [foo1: [value: 'bar1', hidden: false]],
11 | )
12 | ansiblePlaybook(
13 | playbook: '/ansible/playbook.yml',
14 | extraVars: [foo2: [value: 'bar2', hidden: true]],
15 | )
16 | }
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/test/resources/jobdsl/expander.groovy:
--------------------------------------------------------------------------------
1 | job('ansible') {
2 | steps {
3 | shell('''cat > playbook.yml << EOL
4 | - hosts: localhost
5 | connection: local
6 | gather_facts: no
7 | tasks:
8 | - debug: msg=test
9 | EOL
10 | ''')
11 | shell('mkdir -p inventory')
12 | ansiblePlaybook('playbook.yml') {
13 | inventoryPath('${inventory_repository}/inventory.yml')
14 | vaultCredentialsId('${vault_credentials_id}')
15 | credentialsId('${credentials_id}')
16 | }
17 | }
18 | parameters {
19 | stringParam('inventory_repository')
20 | stringParam('vault_credentials_id')
21 | stringParam('credentials_id')
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/test/resources/jobdsl/security630.groovy:
--------------------------------------------------------------------------------
1 | freeStyleJob('ansible') {
2 | steps {
3 | ansiblePlaybook('path/playbook.yml') {
4 | inventoryPath('hosts.ini')
5 | ansibleName('1.9.4')
6 | limit('retry.limit')
7 | tags('one,two')
8 | skippedTags('three')
9 | startAtTask('task')
10 | credentialsId('credsid')
11 | become(true)
12 | becomeUser("user")
13 | forks(6)
14 | unbufferedOutput(false)
15 | colorizedOutput(true)
16 | additionalParameters('params')
17 | extraVars {
18 | extraVar ("key","value",true)
19 | }
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/test/resources/jobdsl/playbook.groovy:
--------------------------------------------------------------------------------
1 | freeStyleJob('ansible') {
2 | steps {
3 | ansiblePlaybook('path/playbook.yml') {
4 | inventoryPath('hosts.ini')
5 | ansibleName('1.9.4')
6 | limit('retry.limit')
7 | tags('one,two')
8 | skippedTags('three')
9 | startAtTask('task')
10 | credentialsId('credsid')
11 | become(true)
12 | becomeUser("user")
13 | forks(6)
14 | unbufferedOutput(false)
15 | colorizedOutput(true)
16 | disableHostKeyChecking(false)
17 | additionalParameters('params')
18 | extraVars {
19 | extraVar ("key","value",true)
20 | }
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/test/resources/jobdsl/legacyPlaybook.groovy:
--------------------------------------------------------------------------------
1 | freeStyleJob('ansible') {
2 | steps {
3 | ansiblePlaybook('path/playbook.yml') {
4 | inventoryPath('hosts.ini')
5 | ansibleName('1.9.4')
6 | limit('retry.limit')
7 | tags('one,two')
8 | skippedTags('three')
9 | startAtTask('task')
10 | credentialsId('credsid')
11 | sudo(true)
12 | sudoUser("user")
13 | forks(6)
14 | unbufferedOutput(false)
15 | colorizedOutput(true)
16 | hostKeyChecking(false)
17 | disableHostKeyChecking(true)
18 | additionalParameters('params')
19 | extraVars {
20 | extraVar ("key","value",true)
21 | }
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/test/java/org/jenkinsci/plugins/ansible/CompatibilityTest.java:
--------------------------------------------------------------------------------
1 | package org.jenkinsci.plugins.ansible;
2 |
3 | import static org.junit.jupiter.api.Assertions.assertEquals;
4 | import static org.junit.jupiter.api.Assertions.assertFalse;
5 |
6 | import hudson.model.FreeStyleProject;
7 | import org.junit.jupiter.api.Test;
8 | import org.jvnet.hudson.test.JenkinsRule;
9 | import org.jvnet.hudson.test.junit.jupiter.WithJenkins;
10 | import org.jvnet.hudson.test.recipes.LocalData;
11 |
12 | @WithJenkins
13 | class CompatibilityTest {
14 |
15 | @LocalData
16 | @Test
17 | void test(JenkinsRule r) {
18 | FreeStyleProject p = (FreeStyleProject) r.jenkins.getItem("old");
19 | assertEquals(2, p.getBuilders().size());
20 | assertFalse(r.jenkins.getAdministrativeMonitor("OldData").isActivated());
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/.github/workflows/jenkins-security-scan.yml:
--------------------------------------------------------------------------------
1 | # More information about the Jenkins security scan can be found at the developer docs: https://www.jenkins.io/redirect/jenkins-security-scan/
2 |
3 | name: jenkins-security-scan
4 | on:
5 | push:
6 | branches:
7 | - "main"
8 | pull_request:
9 | types: [ opened, synchronize, reopened ]
10 | workflow_dispatch:
11 |
12 | permissions:
13 | security-events: write
14 | contents: read
15 | actions: read
16 |
17 | jobs:
18 | security-scan:
19 | uses: jenkins-infra/jenkins-security-scan/.github/workflows/jenkins-security-scan.yaml@v2
20 | with:
21 | java-cache: 'maven' # Optionally enable use of a build dependency cache. Specify 'maven' or 'gradle' as appropriate.
22 | # java-version: 21 # Optionally specify what version of Java to set up for the build, or remove to use a recent default.
23 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/ansible/jobdsl/context/ExtraVarsContext.java:
--------------------------------------------------------------------------------
1 | package org.jenkinsci.plugins.ansible.jobdsl.context;
2 |
3 | import hudson.util.Secret;
4 | import java.util.ArrayList;
5 | import java.util.List;
6 | import javaposse.jobdsl.dsl.Context;
7 | import org.jenkinsci.plugins.ansible.ExtraVar;
8 |
9 | /**
10 | * @author pawbur (Pawel Burchard)
11 | */
12 | public class ExtraVarsContext implements Context {
13 | private List extraVars = new ArrayList();
14 |
15 | public void extraVar(String key, String value, boolean hidden) {
16 | ExtraVar extraVar = new ExtraVar();
17 | extraVar.setKey(key);
18 | extraVar.setSecretValue(Secret.fromString(value));
19 | extraVar.setHidden(hidden);
20 | this.extraVars.add(extraVar);
21 | }
22 |
23 | public List getExtraVars() {
24 | return extraVars;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/workflow/AnsibleVaultStep/config.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License
2 |
3 | Copyright 2023
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/ansible/AnsibleCommand.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Jean-Christophe Sirot
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.jenkinsci.plugins.ansible;
17 |
18 | /**
19 | * The command to be launched
20 | */
21 | public enum AnsibleCommand {
22 | ANSIBLE("ansible"),
23 | ANSIBLE_PLAYBOOK("ansible-playbook"),
24 | ANSIBLE_VAULT("ansible-vault");
25 |
26 | private final String name;
27 |
28 | private AnsibleCommand(String name) {
29 | this.name = name;
30 | }
31 |
32 | public String getName() {
33 | return name;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/test/resources/docker/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM jenkins/ssh-agent:5.46.0-jdk21
2 |
3 | # Define build argument for ansible-core version
4 | ARG ANSIBLE_CORE_VERSION=
5 |
6 | # sshpass used by the plugin
7 | RUN apt-get update && \
8 | apt-get install -y python3 python3-pip sshpass && \
9 | rm -rf /var/lib/apt/lists/*
10 |
11 | # Install ansible
12 | RUN pip3 install --break-system-packages ansible-core==${ANSIBLE_CORE_VERSION}
13 |
14 | ENV PATH="${PATH}:/root/.local/bin"
15 |
16 | # Create ansible files
17 | RUN mkdir -p /ansible && \
18 | mkdir -p /etc/ansible && \
19 | mkdir -p /ansible/tmp && \
20 | chmod 1777 /ansible/tmp && \
21 | echo "---\nall:\n hosts:\n local:\n ansible_connection: local" > /ansible/inventory.yml && \
22 | echo "---\n- hosts: local\n tasks:\n - debug:\n msg: 'Hello World'" > /ansible/playbook.yml && \
23 | echo "[defaults]\ninventory = /ansible/inventory.yml" > /etc/ansible/ansible.cfg
24 |
25 | # Test only. Safe to connect with user/password
26 | RUN echo "password\npassword" | passwd root
27 | RUN sed -i 's/PasswordAuthentication no/PasswordAuthentication yes/g' /etc/ssh/sshd_config
28 | RUN sed -i 's/PermitRootLogin no/PermitRootLogin yes/g' /etc/ssh/sshd_config
29 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/AnsibleVaultBuilder/config.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | ${inst.name}
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/ansible/InventoryDoNotSpecify.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 Jean-Christophe Sirot
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.jenkinsci.plugins.ansible;
17 |
18 | import hudson.EnvVars;
19 | import hudson.Extension;
20 | import hudson.FilePath;
21 | import hudson.model.TaskListener;
22 | import hudson.util.ArgumentListBuilder;
23 | import org.kohsuke.stapler.DataBoundConstructor;
24 |
25 | /**
26 | * Path to a file containing an Ansible inventory
27 | */
28 | public class InventoryDoNotSpecify extends Inventory {
29 | @DataBoundConstructor
30 | public InventoryDoNotSpecify() {}
31 |
32 | @Override
33 | protected InventoryHandler getHandler() {
34 | return new InventoryHandler() {
35 | public void addArgument(
36 | ArgumentListBuilder args, FilePath workspace, EnvVars envVars, TaskListener listener) {
37 | // Do nothing
38 | }
39 |
40 | public void tearDown(TaskListener listener) {}
41 | };
42 | }
43 |
44 | @Extension
45 | public static class DescriptorImpl extends InventoryDescriptor {
46 |
47 | @Override
48 | public String getDisplayName() {
49 | return "Do not specify Inventory";
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/ansible/CLIRunner.java:
--------------------------------------------------------------------------------
1 | package org.jenkinsci.plugins.ansible;
2 |
3 | import hudson.FilePath;
4 | import hudson.Launcher;
5 | import hudson.model.AbstractBuild;
6 | import hudson.model.BuildListener;
7 | import hudson.model.Run;
8 | import hudson.model.TaskListener;
9 | import hudson.util.ArgumentListBuilder;
10 | import java.io.IOException;
11 | import java.io.PrintStream;
12 | import java.util.Map;
13 |
14 | /**
15 | * Created with IntelliJ IDEA.
16 | * User: sirot
17 | * Date: 23/05/15
18 | * Time: 22:56
19 | * To change this template use File | Settings | File Templates.
20 | */
21 | public class CLIRunner {
22 | private final Launcher launcher;
23 | private final Run, ?> build;
24 | private final TaskListener listener;
25 | private final FilePath ws;
26 |
27 | public CLIRunner(AbstractBuild, ?> build, Launcher launcher, BuildListener listener) {
28 | this.launcher = launcher;
29 | this.build = build;
30 | this.listener = listener;
31 | this.ws = build.getWorkspace();
32 | }
33 |
34 | public CLIRunner(Run, ?> build, FilePath ws, Launcher launcher, TaskListener listener) {
35 | this.launcher = launcher;
36 | this.build = build;
37 | this.listener = listener;
38 | this.ws = ws;
39 | }
40 |
41 | public boolean execute(ArgumentListBuilder args, Map environment)
42 | throws IOException, InterruptedException {
43 | PrintStream logger = listener.getLogger();
44 | try {
45 | this.ws.mkdirs();
46 | return launcher.launch()
47 | .pwd(ws)
48 | .envs(environment)
49 | .cmds(args)
50 | .stdout(logger)
51 | .stderr(logger)
52 | .join()
53 | == 0;
54 | } finally {
55 | logger.flush();
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/ansible/InventoryPath.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Jean-Christophe Sirot
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.jenkinsci.plugins.ansible;
17 |
18 | import hudson.EnvVars;
19 | import hudson.Extension;
20 | import hudson.FilePath;
21 | import hudson.model.TaskListener;
22 | import hudson.util.ArgumentListBuilder;
23 | import org.apache.commons.lang3.StringUtils;
24 | import org.kohsuke.stapler.DataBoundConstructor;
25 |
26 | /**
27 | * Path to a file containing an Ansible inventory
28 | */
29 | public class InventoryPath extends Inventory {
30 | public final String path;
31 |
32 | @DataBoundConstructor
33 | public InventoryPath(String path) {
34 | this.path = path;
35 | }
36 |
37 | @Override
38 | protected InventoryHandler getHandler() {
39 | return new InventoryHandler() {
40 | public void addArgument(
41 | ArgumentListBuilder args, FilePath workspace, EnvVars envVars, TaskListener listener) {
42 | String expandedPath = envVars.expand(InventoryPath.this.path);
43 | if (StringUtils.isNotEmpty(expandedPath)) {
44 | args.add("-i").add(expandedPath);
45 | }
46 | }
47 |
48 | public void tearDown(TaskListener listener) {}
49 | };
50 | }
51 |
52 | @Extension
53 | public static class DescriptorImpl extends InventoryDescriptor {
54 |
55 | @Override
56 | public String getDisplayName() {
57 | return "File or host list";
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/workflow/AnsiblePlaybookStep/config.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/src/test/resources/org/jenkinsci/plugins/ansible/CompatibilityTest/jobs/old/config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | false
6 |
7 | true
8 | false
9 | false
10 | false
11 |
12 | false
13 |
14 |
15 | playbook.yml
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | false
24 |
25 | 5
26 | true
27 | false
28 | false
29 |
30 | false
31 |
32 |
33 |
34 |
35 |
36 | one.example.com
37 |
38 |
39 | false
40 |
41 | 5
42 | true
43 | false
44 | true
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/ansible/Inventory.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Jean-Christophe Sirot
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.jenkinsci.plugins.ansible;
17 |
18 | import hudson.EnvVars;
19 | import hudson.FilePath;
20 | import hudson.model.Describable;
21 | import hudson.model.Descriptor;
22 | import hudson.model.TaskListener;
23 | import hudson.util.ArgumentListBuilder;
24 | import java.io.IOException;
25 | import jenkins.model.Jenkins;
26 |
27 | /**
28 | * Common Ansible inventory.
29 | */
30 | public abstract class Inventory implements Describable {
31 | /**
32 | * @see hudson.model.Describable#getDescriptor()
33 | */
34 | @SuppressWarnings("unchecked")
35 | public Descriptor getDescriptor() {
36 | return Jenkins.getActiveInstance().getDescriptorOrDie(getClass());
37 | }
38 |
39 | protected abstract InventoryHandler getHandler();
40 |
41 | public void addArgument(ArgumentListBuilder args, FilePath workspace, EnvVars envVars, TaskListener listener)
42 | throws InterruptedException, IOException {
43 | getHandler().addArgument(args, workspace, envVars, listener);
44 | }
45 |
46 | public void tearDown(TaskListener listener) throws InterruptedException, IOException {
47 | getHandler().tearDown(listener);
48 | }
49 |
50 | public abstract static class InventoryDescriptor extends Descriptor {}
51 |
52 | protected static interface InventoryHandler {
53 |
54 | void addArgument(ArgumentListBuilder args, FilePath workspace, EnvVars envVars, TaskListener listener)
55 | throws InterruptedException, IOException;
56 |
57 | void tearDown(TaskListener listener) throws InterruptedException, IOException;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/ansible/ExtraVar.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015-2016 Jean-Christophe Sirot
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.jenkinsci.plugins.ansible;
17 |
18 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
19 | import hudson.Extension;
20 | import hudson.model.AbstractDescribableImpl;
21 | import hudson.model.Descriptor;
22 | import hudson.util.Secret;
23 | import org.kohsuke.stapler.DataBoundConstructor;
24 | import org.kohsuke.stapler.DataBoundSetter;
25 |
26 | public class ExtraVar extends AbstractDescribableImpl {
27 |
28 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility.")
29 | public String key;
30 |
31 | public transient String value;
32 |
33 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility.")
34 | public Secret secretValue;
35 |
36 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility.")
37 | public boolean hidden = true;
38 |
39 | @DataBoundConstructor
40 | public ExtraVar() {}
41 |
42 | protected Object readResolve() {
43 | if (value != null) {
44 | this.setSecretValue(Secret.fromString(value));
45 | }
46 | return this;
47 | }
48 |
49 | @DataBoundSetter
50 | public void setKey(String key) {
51 | this.key = key;
52 | }
53 |
54 | @DataBoundSetter
55 | public void setHidden(boolean hidden) {
56 | this.hidden = hidden;
57 | }
58 |
59 | @DataBoundSetter
60 | public void setSecretValue(Secret value) {
61 | this.secretValue = value;
62 | }
63 |
64 | public String getKey() {
65 | return key;
66 | }
67 |
68 | public Secret getSecretValue() {
69 | return this.secretValue;
70 | }
71 |
72 | public boolean isHidden() {
73 | return hidden;
74 | }
75 |
76 | @Extension
77 | public static class DescriptorImpl extends Descriptor {
78 |
79 | public String getDisplayName() {
80 | return "";
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/workflow/AnsibleAdHocStep/config.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/ansible/InventoryContent.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Jean-Christophe Sirot
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.jenkinsci.plugins.ansible;
17 |
18 | import hudson.EnvVars;
19 | import hudson.Extension;
20 | import hudson.FilePath;
21 | import hudson.model.TaskListener;
22 | import hudson.util.ArgumentListBuilder;
23 | import java.io.IOException;
24 | import org.kohsuke.stapler.DataBoundConstructor;
25 |
26 | /**
27 | * Inline content for Ansible inventory. Inventory may be dynamic or not.
28 | */
29 | public class InventoryContent extends Inventory {
30 | public final String content;
31 | public final boolean dynamic;
32 |
33 | private transient FilePath inventory = null;
34 |
35 | @DataBoundConstructor
36 | public InventoryContent(String content, boolean dynamic) {
37 | this.content = content;
38 | this.dynamic = dynamic;
39 | }
40 |
41 | @Override
42 | protected InventoryHandler getHandler() {
43 | return new InventoryHandler() {
44 | public void addArgument(
45 | ArgumentListBuilder args, FilePath workspace, EnvVars envVars, TaskListener listener)
46 | throws InterruptedException, IOException {
47 | inventory = createInventoryFile(inventory, workspace, envVars.expand(content));
48 | args.add("-i").add(inventory);
49 | }
50 |
51 | public void tearDown(TaskListener listener) throws InterruptedException, IOException {
52 | Utils.deleteTempFile(inventory, listener);
53 | }
54 |
55 | private FilePath createInventoryFile(FilePath inventory, FilePath workspace, String content)
56 | throws IOException, InterruptedException {
57 | inventory = workspace.createTextTempFile("inventory", ".ini", content, false);
58 | inventory.chmod(dynamic ? 0500 : 0400);
59 | return inventory;
60 | }
61 | };
62 | }
63 |
64 | @Extension
65 | public static class DescriptorImpl extends InventoryDescriptor {
66 |
67 | @Override
68 | public String getDisplayName() {
69 | return "Inline content";
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/ansible/AnsibleAdHocCommandInvocation.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Jean-Christophe Sirot
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.jenkinsci.plugins.ansible;
17 |
18 | import hudson.EnvVars;
19 | import hudson.FilePath;
20 | import hudson.model.AbstractBuild;
21 | import hudson.model.BuildListener;
22 | import hudson.model.Run;
23 | import hudson.model.TaskListener;
24 | import hudson.util.ArgumentListBuilder;
25 | import java.io.IOException;
26 |
27 | /**
28 | * Invoke the ansible command
29 | */
30 | public class AnsibleAdHocCommandInvocation extends AbstractAnsibleInvocation {
31 |
32 | private String module;
33 | private String hostPattern;
34 | private String command;
35 |
36 | protected AnsibleAdHocCommandInvocation(String exe, AbstractBuild, ?> build, BuildListener listener)
37 | throws IOException, InterruptedException, AnsibleInvocationException {
38 | super(exe, build, build.getWorkspace(), listener, build.getEnvironment(listener));
39 | }
40 |
41 | public AnsibleAdHocCommandInvocation(
42 | String exe, Run, ?> build, FilePath ws, TaskListener listener, EnvVars envVars)
43 | throws IOException, InterruptedException, AnsibleInvocationException {
44 | super(exe, build, ws, listener, envVars);
45 | }
46 |
47 | public AnsibleAdHocCommandInvocation setHostPattern(String hostPattern) {
48 | this.hostPattern = hostPattern;
49 | return this;
50 | }
51 |
52 | private ArgumentListBuilder appendHostPattern(ArgumentListBuilder args) {
53 | args.add(envVars.expand(hostPattern));
54 | return args;
55 | }
56 |
57 | public AnsibleAdHocCommandInvocation setModule(String module) {
58 | this.module = module;
59 | return this;
60 | }
61 |
62 | private ArgumentListBuilder appendModule(ArgumentListBuilder args) {
63 | addOptionAndValue(args, "-m", module);
64 | return args;
65 | }
66 |
67 | public AnsibleAdHocCommandInvocation setModuleCommand(String command) {
68 | this.command = command;
69 | return this;
70 | }
71 |
72 | public ArgumentListBuilder appendModuleCommand(ArgumentListBuilder args) {
73 | addOptionAndValue(args, "-a", command);
74 | return args;
75 | }
76 |
77 | @Override
78 | protected ArgumentListBuilder buildCommandLine()
79 | throws InterruptedException, AnsibleInvocationException, IOException {
80 | ArgumentListBuilder args = new ArgumentListBuilder();
81 | prependPasswordCredentials(args);
82 | appendExecutable(args);
83 | appendHostPattern(args);
84 | appendInventory(args);
85 | appendModule(args);
86 | appendModuleCommand(args);
87 | appendBecome(args);
88 | appendSudo(args);
89 | appendForks(args);
90 | appendCredentials(args);
91 | appendVaultPasswordFile(args);
92 | appendExtraVars(args);
93 | appendAdditionalParameters(args);
94 | return args;
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/AnsibleAdHocCommandBuilder/config.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | ${inst.name}
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/ansible/AnsiblePlaybookBuilder/config.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | ${inst.name}
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/ansible/jobdsl/AnsibleJobDslExtension.java:
--------------------------------------------------------------------------------
1 | package org.jenkinsci.plugins.ansible.jobdsl;
2 |
3 | import hudson.Extension;
4 | import javaposse.jobdsl.dsl.helpers.step.StepContext;
5 | import javaposse.jobdsl.plugin.ContextExtensionPoint;
6 | import javaposse.jobdsl.plugin.DslExtensionMethod;
7 | import org.jenkinsci.plugins.ansible.AnsibleAdHocCommandBuilder;
8 | import org.jenkinsci.plugins.ansible.AnsiblePlaybookBuilder;
9 | import org.jenkinsci.plugins.ansible.AnsibleVaultBuilder;
10 | import org.jenkinsci.plugins.ansible.jobdsl.context.AnsibleContext;
11 |
12 | /**
13 | * @author lanwen (Merkushev Kirill)
14 | */
15 | @Extension(optional = true)
16 | public class AnsibleJobDslExtension extends ContextExtensionPoint {
17 |
18 | @DslExtensionMethod(context = StepContext.class)
19 | public Object ansibleAdHoc(String module, String command, Runnable closure) {
20 | AnsibleContext context = new AnsibleContext();
21 | executeInContext(closure, context);
22 |
23 | AnsibleAdHocCommandBuilder adhoc =
24 | new AnsibleAdHocCommandBuilder(context.getHostPattern(), context.getInventory(), module, command);
25 |
26 | adhoc.setAdditionalParameters(context.getAdditionalParameters());
27 | adhoc.setAnsibleName(context.getAnsibleName());
28 | adhoc.setCredentialsId(context.getCredentialsId());
29 | adhoc.setVaultCredentialsId(context.getVaultCredentialsId());
30 | adhoc.setVaultTmpPath(context.getVaultTmpPath());
31 | adhoc.setColorizedOutput(context.isColorizedOutput());
32 | adhoc.setForks(context.getForks());
33 | adhoc.setDisableHostKeyChecking(context.isDisableHostKeyChecking());
34 | adhoc.setBecome(context.isBecome());
35 | adhoc.setBecomeUser(context.getBecomeUser());
36 | adhoc.setSudo(context.isSudo());
37 | adhoc.setSudoUser(context.getSudoUser());
38 | adhoc.setUnbufferedOutput(context.isUnbufferedOutput());
39 | adhoc.setExtraVars(context.getExtraVars());
40 |
41 | return adhoc;
42 | }
43 |
44 | @DslExtensionMethod(context = StepContext.class)
45 | public Object ansiblePlaybook(String playbook, Runnable closure) {
46 | AnsibleContext context = new AnsibleContext();
47 | executeInContext(closure, context);
48 |
49 | AnsiblePlaybookBuilder plbook = new AnsiblePlaybookBuilder(playbook, context.getInventory());
50 |
51 | plbook.setAdditionalParameters(context.getAdditionalParameters());
52 | plbook.setAnsibleName(context.getAnsibleName());
53 | plbook.setCredentialsId(context.getCredentialsId());
54 | plbook.setVaultCredentialsId(context.getVaultCredentialsId());
55 | plbook.setVaultTmpPath(context.getVaultTmpPath());
56 | plbook.setColorizedOutput(context.isColorizedOutput());
57 | plbook.setForks(context.getForks());
58 | plbook.setDisableHostKeyChecking(context.isDisableHostKeyChecking());
59 | plbook.setBecome(context.isBecome());
60 | plbook.setBecomeUser(context.getBecomeUser());
61 | plbook.setCheckMode(context.isCheckMode());
62 | plbook.setSudo(context.isSudo());
63 | plbook.setSudoUser(context.getSudoUser());
64 | plbook.setUnbufferedOutput(context.isUnbufferedOutput());
65 | plbook.setLimit(context.getLimit());
66 | plbook.setTags(context.getTags());
67 | plbook.setSkippedTags(context.getSkippedTags());
68 | plbook.setStartAtTask(context.getStartAtTask());
69 | plbook.setExtraVars(context.getExtraVars());
70 |
71 | return plbook;
72 | }
73 |
74 | @DslExtensionMethod(context = StepContext.class)
75 | public Object ansibleVault(Runnable closure) {
76 | AnsibleContext context = new AnsibleContext();
77 | executeInContext(closure, context);
78 |
79 | AnsibleVaultBuilder vault = new AnsibleVaultBuilder();
80 |
81 | vault.setAnsibleName(context.getAnsibleName());
82 | vault.setAction(context.getAction());
83 | vault.setVaultCredentialsId(context.getVaultCredentialsId());
84 | vault.setNewVaultCredentialsId(context.getNewVaultCredentialsId());
85 | vault.setVaultTmpPath(context.getVaultTmpPath());
86 | vault.setContent(context.getContent());
87 | vault.setInput(context.getInput());
88 | vault.setOutput(context.getOutput());
89 |
90 | return vault;
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/ansible/Utils.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Jean-Christophe Sirot
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.jenkinsci.plugins.ansible;
17 |
18 | import com.cloudbees.jenkins.plugins.sshcredentials.SSHUserPrivateKey;
19 | import hudson.FilePath;
20 | import hudson.model.TaskListener;
21 | import hudson.util.Secret;
22 | import java.io.IOException;
23 | import java.io.InputStream;
24 | import java.util.List;
25 | import org.jenkinsci.plugins.plaincredentials.FileCredentials;
26 | import org.jenkinsci.plugins.plaincredentials.StringCredentials;
27 |
28 | class Utils {
29 | /**
30 | * Copy the SSH private key into a temporary file.
31 | *
32 | * @param key the destination file
33 | * @param credentials the SSH key
34 | * @return the file
35 | * @throws IOException
36 | * @throws InterruptedException
37 | */
38 | static FilePath createSshKeyFile(FilePath key, FilePath tmpPath, SSHUserPrivateKey credentials, boolean inThisDir)
39 | throws IOException, InterruptedException {
40 | StringBuilder sb = new StringBuilder();
41 | List privateKeys = credentials.getPrivateKeys();
42 | for (String s : privateKeys) {
43 | sb.append(s);
44 | }
45 | key = tmpPath.createTextTempFile("ssh", ".key", sb.toString(), inThisDir);
46 | key.chmod(0400);
47 | return key;
48 | }
49 |
50 | static FilePath createSshAskPassFile(
51 | FilePath script, FilePath tmpPath, SSHUserPrivateKey credentials, boolean inThisDir)
52 | throws IOException, InterruptedException {
53 | tmpPath.mkdirs();
54 | StringBuilder sb = new StringBuilder();
55 | sb.append("#! /bin/sh\n").append("/bin/echo \"" + Secret.toString(credentials.getPassphrase()) + "\"");
56 | script = tmpPath.createTextTempFile("ssh", ".sh", sb.toString(), inThisDir);
57 | script.chmod(0700);
58 | return script;
59 | }
60 |
61 | /**
62 | * Copy the Vault password into a temporary file.
63 | *
64 | * @param key the destination file
65 | * @param credentials the SSH key
66 | * @return the file
67 | * @throws IOException
68 | * @throws InterruptedException
69 | */
70 | static FilePath createVaultPasswordFile(FilePath key, FilePath tmpPath, FileCredentials credentials)
71 | throws IOException, InterruptedException {
72 | try (InputStream content = credentials.getContent()) {
73 | tmpPath.mkdirs();
74 | key = tmpPath.createTempFile("vault", ".password");
75 | key.copyFrom(content);
76 | key.chmod(0400);
77 | }
78 | return key;
79 | }
80 |
81 | /**
82 | * Copy the Vault password into a temporary file.
83 | *
84 | * @param key the destination file
85 | * @param credentials the SSH key
86 | * @return the file
87 | * @throws IOException
88 | * @throws InterruptedException
89 | */
90 | static FilePath createVaultPasswordFile(FilePath key, FilePath tmpPath, StringCredentials credentials)
91 | throws IOException, InterruptedException {
92 | tmpPath.mkdirs();
93 | key = tmpPath.createTextTempFile(
94 | "vault", ".password", credentials.getSecret().getPlainText(), true);
95 | key.chmod(0400);
96 | return key;
97 | }
98 |
99 | /**
100 | * Delete a temporary file. Print a warning in the log when deletion fails.
101 | *
102 | * @param tempFile the file to be removed
103 | * @param listener the build listener
104 | */
105 | static void deleteTempFile(FilePath tempFile, TaskListener listener) throws IOException, InterruptedException {
106 | if (tempFile != null) {
107 | try {
108 | tempFile.delete();
109 | } catch (IOException ioe) {
110 | if (tempFile.exists()) {
111 | listener.getLogger().println("[WARNING] temp file " + tempFile + " not deleted");
112 | }
113 | }
114 | }
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/ansible/AnsiblePlaybookInvocation.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015-2016 Jean-Christophe Sirot
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.jenkinsci.plugins.ansible;
17 |
18 | import hudson.EnvVars;
19 | import hudson.FilePath;
20 | import hudson.model.AbstractBuild;
21 | import hudson.model.BuildListener;
22 | import hudson.model.Run;
23 | import hudson.model.TaskListener;
24 | import hudson.util.ArgumentListBuilder;
25 | import java.io.IOException;
26 |
27 | /**
28 | * Invoke the ansible-playbook command
29 | */
30 | public class AnsiblePlaybookInvocation extends AbstractAnsibleInvocation {
31 |
32 | private String playbook;
33 | private String limit;
34 | private String tags;
35 | private String skippedTags;
36 | private String startAtTask;
37 | private boolean checkMode;
38 |
39 | protected AnsiblePlaybookInvocation(String exe, AbstractBuild, ?> build, BuildListener listener, EnvVars envVars)
40 | throws IOException, InterruptedException, AnsibleInvocationException {
41 | this(exe, build, build.getWorkspace(), listener, envVars);
42 | }
43 |
44 | public AnsiblePlaybookInvocation(String exe, Run, ?> build, FilePath ws, TaskListener listener, EnvVars envVars)
45 | throws IOException, InterruptedException, AnsibleInvocationException {
46 | super(exe, build, ws, listener, envVars);
47 | }
48 |
49 | public AnsiblePlaybookInvocation setPlaybook(String playbook) {
50 | this.playbook = playbook;
51 | return this;
52 | }
53 |
54 | public AnsiblePlaybookInvocation setCheckMode(boolean checkMode) {
55 | this.checkMode = checkMode;
56 | return this;
57 | }
58 |
59 | private ArgumentListBuilder appendPlaybook(ArgumentListBuilder args) {
60 | args.add(envVars.expand(playbook));
61 | return args;
62 | }
63 |
64 | public AnsiblePlaybookInvocation setLimit(String limit) {
65 | this.limit = limit;
66 | return this;
67 | }
68 |
69 | private ArgumentListBuilder appendLimit(ArgumentListBuilder args) {
70 | addOptionAndValue(args, "-l", limit);
71 | return args;
72 | }
73 |
74 | public AnsiblePlaybookInvocation setTags(String tags) {
75 | this.tags = tags;
76 | return this;
77 | }
78 |
79 | private ArgumentListBuilder appendTags(ArgumentListBuilder args) {
80 | addOptionAndValue(args, "-t", tags);
81 | return args;
82 | }
83 |
84 | public AnsiblePlaybookInvocation setSkippedTags(String skippedTags) {
85 | this.skippedTags = skippedTags;
86 | return this;
87 | }
88 |
89 | private ArgumentListBuilder appendSkippedTags(ArgumentListBuilder args) {
90 | addKeyValuePair(args, "--skip-tags", skippedTags);
91 | return args;
92 | }
93 |
94 | public AnsiblePlaybookInvocation setStartTask(String startAtTask) {
95 | this.startAtTask = startAtTask;
96 | return this;
97 | }
98 |
99 | private ArgumentListBuilder appendStartTask(ArgumentListBuilder args) {
100 | addKeyValuePair(args, "--start-at-task", startAtTask);
101 | return args;
102 | }
103 |
104 | protected ArgumentListBuilder appendCheckMode(ArgumentListBuilder args) {
105 | if (checkMode) {
106 | args.add("--check");
107 | }
108 | return args;
109 | }
110 |
111 | @Override
112 | protected ArgumentListBuilder buildCommandLine()
113 | throws InterruptedException, AnsibleInvocationException, IOException {
114 | ArgumentListBuilder args = new ArgumentListBuilder();
115 | prependPasswordCredentials(args);
116 | appendExecutable(args);
117 | appendPlaybook(args);
118 | appendInventory(args);
119 | appendLimit(args);
120 | appendTags(args);
121 | appendSkippedTags(args);
122 | appendStartTask(args);
123 | appendBecome(args);
124 | appendCheckMode(args);
125 | appendSudo(args);
126 | appendForks(args);
127 | appendCredentials(args);
128 | appendVaultPasswordFile(args);
129 | appendExtraVars(args);
130 | appendAdditionalParameters(args);
131 | return args;
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/ansible/AbstractAnsibleBuilderDescriptor.java:
--------------------------------------------------------------------------------
1 | package org.jenkinsci.plugins.ansible;
2 |
3 | import static com.cloudbees.plugins.credentials.CredentialsMatchers.anyOf;
4 | import static com.cloudbees.plugins.credentials.CredentialsMatchers.instanceOf;
5 |
6 | import com.cloudbees.jenkins.plugins.sshcredentials.SSHUserPrivateKey;
7 | import com.cloudbees.plugins.credentials.CredentialsProvider;
8 | import com.cloudbees.plugins.credentials.common.StandardCredentials;
9 | import com.cloudbees.plugins.credentials.common.StandardListBoxModel;
10 | import com.cloudbees.plugins.credentials.common.StandardUsernameCredentials;
11 | import com.cloudbees.plugins.credentials.common.UsernamePasswordCredentials;
12 | import hudson.model.AbstractProject;
13 | import hudson.model.Item;
14 | import hudson.tasks.BuildStepDescriptor;
15 | import hudson.tasks.Builder;
16 | import hudson.util.FormValidation;
17 | import hudson.util.ListBoxModel;
18 | import java.util.List;
19 | import jenkins.model.Jenkins;
20 | import org.apache.commons.lang3.StringUtils;
21 | import org.jenkinsci.plugins.ansible.Inventory.InventoryDescriptor;
22 | import org.jenkinsci.plugins.plaincredentials.FileCredentials;
23 | import org.jenkinsci.plugins.plaincredentials.StringCredentials;
24 | import org.kohsuke.stapler.AncestorInPath;
25 | import org.kohsuke.stapler.QueryParameter;
26 |
27 | /**
28 | * Common descriptor for Ansible build steps
29 | */
30 | public abstract class AbstractAnsibleBuilderDescriptor extends BuildStepDescriptor {
31 | private final String displayName;
32 |
33 | protected AbstractAnsibleBuilderDescriptor(String displayName) {
34 | this.displayName = displayName;
35 | load();
36 | }
37 |
38 | protected FormValidation checkNotNullOrEmpty(String parameter, String errorMessage) {
39 | if (StringUtils.isNotBlank(parameter)) {
40 | return FormValidation.ok();
41 | } else {
42 | return FormValidation.error(errorMessage);
43 | }
44 | }
45 |
46 | public ListBoxModel doFillCredentialsIdItems(@AncestorInPath Item item, @QueryParameter String credentialsId) {
47 |
48 | StandardListBoxModel result = new StandardListBoxModel();
49 | if (item == null) {
50 | if (!Jenkins.getActiveInstance().hasPermission(Jenkins.ADMINISTER)) {
51 | return result.includeCurrentValue(credentialsId);
52 | }
53 | } else {
54 | if (!item.hasPermission(Item.EXTENDED_READ) && !item.hasPermission(CredentialsProvider.USE_ITEM)) {
55 | return result.includeCurrentValue(credentialsId);
56 | }
57 | }
58 |
59 | return result.includeEmptyValue()
60 | .withMatching(
61 | anyOf(instanceOf(SSHUserPrivateKey.class), instanceOf(UsernamePasswordCredentials.class)),
62 | CredentialsProvider.lookupCredentials(StandardUsernameCredentials.class, item))
63 | .includeCurrentValue(credentialsId);
64 | }
65 |
66 | public ListBoxModel doFillVaultCredentialsIdItems(
67 | @AncestorInPath Item item, @QueryParameter String vaultCredentialsId) {
68 | return fillVaultCredentials(item, vaultCredentialsId);
69 | }
70 |
71 | public ListBoxModel doFillNewVaultCredentialsIdItems(
72 | @AncestorInPath Item item, @QueryParameter String newVaultCredentialsId) {
73 | return fillVaultCredentials(item, newVaultCredentialsId);
74 | }
75 |
76 | private ListBoxModel fillVaultCredentials(Item item, String credentialsId) {
77 | StandardListBoxModel result = new StandardListBoxModel();
78 | if (item == null) {
79 | if (!Jenkins.getActiveInstance().hasPermission(Jenkins.ADMINISTER)) {
80 | return result.includeCurrentValue(credentialsId);
81 | }
82 | } else {
83 | if (!item.hasPermission(Item.EXTENDED_READ) && !item.hasPermission(CredentialsProvider.USE_ITEM)) {
84 | return result.includeCurrentValue(credentialsId);
85 | }
86 | }
87 |
88 | return result.includeEmptyValue()
89 | .withMatching(
90 | anyOf(instanceOf(FileCredentials.class), instanceOf(StringCredentials.class)),
91 | CredentialsProvider.lookupCredentials(StandardCredentials.class, item))
92 | .includeCurrentValue(credentialsId);
93 | }
94 |
95 | public List getInventories() {
96 | return Jenkins.getActiveInstance().getDescriptorList(Inventory.class);
97 | }
98 |
99 | @Override
100 | public boolean isApplicable(Class extends AbstractProject> klass) {
101 | return true;
102 | }
103 |
104 | @Override
105 | public String getDisplayName() {
106 | return displayName;
107 | }
108 |
109 | public AnsibleInstallation[] getInstallations() {
110 | return AnsibleInstallation.allInstallations();
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/src/test/java/org/jenkinsci/plugins/ansible/AnsibleVaultInvocationTest.java:
--------------------------------------------------------------------------------
1 | package org.jenkinsci.plugins.ansible;
2 |
3 | import static org.hamcrest.CoreMatchers.is;
4 | import static org.hamcrest.MatcherAssert.assertThat;
5 | import static org.junit.jupiter.api.Assertions.assertThrows;
6 | import static org.mockito.Mockito.*;
7 |
8 | import hudson.AbortException;
9 | import hudson.EnvVars;
10 | import hudson.model.AbstractBuild;
11 | import hudson.model.BuildListener;
12 | import hudson.model.TaskListener;
13 | import hudson.util.ArgumentListBuilder;
14 | import org.junit.jupiter.api.Test;
15 | import org.mockito.ArgumentCaptor;
16 |
17 | /**
18 | * Test Vault invocations
19 | *
20 | * @author Michael Cresswell
21 | */
22 | class AnsibleVaultInvocationTest {
23 |
24 | private static final String EXE = "ansible-vault";
25 |
26 | @Test
27 | void shouldGenerateEncryptString() throws Exception {
28 | CLIRunner runner = mock(CLIRunner.class);
29 | AnsibleVaultInvocation invocation = getInvocation();
30 |
31 | invocation.setAction("encrypt_string");
32 | invocation.setContent("Test Content");
33 | // When
34 | invocation.execute(runner);
35 | // Then
36 | ArgumentCaptor argument = ArgumentCaptor.forClass(ArgumentListBuilder.class);
37 | verify(runner).execute(argument.capture(), anyMap());
38 |
39 | assertThat(argument.getValue().toString(), is("ansible-vault encrypt_string ******"));
40 | }
41 |
42 | @Test
43 | void shouldGenerateEncrypt() throws Exception {
44 | CLIRunner runner = mock(CLIRunner.class);
45 | AnsibleVaultInvocation invocation = getInvocation();
46 |
47 | invocation.setAction("encrypt");
48 | invocation.setInput("/tmp/my_var_file.yml");
49 | // When
50 | invocation.execute(runner);
51 | // Then
52 | ArgumentCaptor argument = ArgumentCaptor.forClass(ArgumentListBuilder.class);
53 | verify(runner).execute(argument.capture(), anyMap());
54 | assertThat(argument.getValue().toString(), is("ansible-vault encrypt /tmp/my_var_file.yml"));
55 | }
56 |
57 | @Test
58 | void shouldGenerateDecrypt() throws Exception {
59 | CLIRunner runner = mock(CLIRunner.class);
60 | AnsibleVaultInvocation invocation = getInvocation();
61 |
62 | invocation.setAction("decrypt");
63 | invocation.setInput("/tmp/my_var_file.yml");
64 | // When
65 | invocation.execute(runner);
66 | // Then
67 | ArgumentCaptor argument = ArgumentCaptor.forClass(ArgumentListBuilder.class);
68 | verify(runner).execute(argument.capture(), anyMap());
69 | assertThat(argument.getValue().toString(), is("ansible-vault decrypt /tmp/my_var_file.yml"));
70 | }
71 |
72 | @Test
73 | void shouldGenerateRekey() throws Exception {
74 | CLIRunner runner = mock(CLIRunner.class);
75 | AnsibleVaultInvocation invocation = getInvocation();
76 |
77 | invocation.setAction("rekey");
78 | invocation.setInput("/tmp/my_var_file.yml");
79 | // When
80 | invocation.execute(runner);
81 | // Then
82 | ArgumentCaptor argument = ArgumentCaptor.forClass(ArgumentListBuilder.class);
83 | verify(runner).execute(argument.capture(), anyMap());
84 | assertThat(argument.getValue().toString(), is("ansible-vault rekey /tmp/my_var_file.yml"));
85 | }
86 |
87 | @Test
88 | void shouldNotGenerateView() throws Exception {
89 | CLIRunner runner = mock(CLIRunner.class);
90 | AnsibleVaultInvocation invocation = getInvocation();
91 |
92 | invocation.setAction("view");
93 | invocation.setInput("/tmp/my_var_file.yml");
94 | // When
95 | assertThrows(AbortException.class, () -> invocation.execute(runner));
96 | // Then
97 | verifyNoInteractions(runner);
98 | }
99 |
100 | @Test
101 | void shouldNotGenerateEdit() throws Exception {
102 | CLIRunner runner = mock(CLIRunner.class);
103 | AnsibleVaultInvocation invocation = getInvocation();
104 |
105 | invocation.setAction("edit");
106 | invocation.setInput("/tmp/my_var_file.yml");
107 | // When
108 | assertThrows(AbortException.class, () -> invocation.execute(runner));
109 | // Then
110 | verifyNoInteractions(runner);
111 | }
112 |
113 | @Test
114 | void shouldNotGenerateCreate() throws Exception {
115 | CLIRunner runner = mock(CLIRunner.class);
116 | AnsibleVaultInvocation invocation = getInvocation();
117 |
118 | invocation.setAction("edit");
119 | invocation.setInput("/tmp/my_var_file.yml");
120 | // When
121 | assertThrows(AbortException.class, () -> invocation.execute(runner));
122 | // Then
123 | verifyNoInteractions(runner);
124 | }
125 |
126 | private AnsibleVaultInvocation getInvocation() throws Exception {
127 | // Given
128 | BuildListener listener = mock(BuildListener.class);
129 | AbstractBuild, ?> build = mock(AbstractBuild.class);
130 | when(build.getEnvironment(any(TaskListener.class))).thenReturn(new EnvVars());
131 | return new AnsibleVaultInvocation(EXE, build, listener, new EnvVars());
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/ansible/AnsibleInstallation.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Jean-Christophe Sirot
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.jenkinsci.plugins.ansible;
17 |
18 | import hudson.EnvVars;
19 | import hudson.Extension;
20 | import hudson.FilePath;
21 | import hudson.Util;
22 | import hudson.model.EnvironmentSpecific;
23 | import hudson.model.Node;
24 | import hudson.model.TaskListener;
25 | import hudson.slaves.NodeSpecific;
26 | import hudson.tools.ToolDescriptor;
27 | import hudson.tools.ToolInstallation;
28 | import hudson.tools.ToolProperty;
29 | import java.io.IOException;
30 | import java.io.Serializable;
31 | import java.util.List;
32 | import jenkins.model.Jenkins;
33 | import net.sf.json.JSONObject;
34 | import org.jenkinsci.Symbol;
35 | import org.kohsuke.stapler.DataBoundConstructor;
36 | import org.kohsuke.stapler.StaplerRequest2;
37 |
38 | /**
39 | * {@code ToolInstallation} for Ansible
40 | */
41 | public class AnsibleInstallation extends ToolInstallation
42 | implements EnvironmentSpecific, NodeSpecific, Serializable {
43 |
44 | @DataBoundConstructor
45 | public AnsibleInstallation(String name, String home, List extends ToolProperty>> properties) {
46 | super(name, home, properties);
47 | }
48 |
49 | public AnsibleInstallation forEnvironment(EnvVars environment) {
50 | return new AnsibleInstallation(
51 | getName(), environment.expand(getHome()), getProperties().toList());
52 | }
53 |
54 | public AnsibleInstallation forNode(Node node, TaskListener log) throws IOException, InterruptedException {
55 | return new AnsibleInstallation(
56 | getName(), translateFor(node, log), getProperties().toList());
57 | }
58 |
59 | public static String getExecutable(
60 | String name, AnsibleCommand command, Node node, TaskListener listener, EnvVars env)
61 | throws IOException, InterruptedException {
62 | if (name != null) {
63 | Jenkins j = Jenkins.getInstance();
64 | if (j != null) {
65 | for (AnsibleInstallation tool :
66 | j.getDescriptorByType(DescriptorImpl.class).getInstallations()) {
67 | if (tool.getName().equals(name)) {
68 | if (node != null) {
69 | tool = tool.forNode(node, listener);
70 | }
71 | if (env != null) {
72 | tool = tool.forEnvironment(env);
73 | }
74 | String home = Util.fixEmpty(tool.getHome());
75 | if (home != null) {
76 | if (node != null) {
77 | FilePath homePath = node.createPath(home);
78 | if (homePath != null) {
79 | return homePath.child(command.getName()).getRemote();
80 | }
81 | }
82 | return home + "/" + command.getName();
83 | }
84 | }
85 | }
86 | }
87 | }
88 | return command.getName();
89 | }
90 |
91 | public static AnsibleInstallation[] allInstallations() {
92 | AnsibleInstallation.DescriptorImpl ansibleDescriptor =
93 | Jenkins.getActiveInstance().getDescriptorByType(AnsibleInstallation.DescriptorImpl.class);
94 | return ansibleDescriptor.getInstallations();
95 | }
96 |
97 | public static AnsibleInstallation getInstallation(String ansibleInstallation) throws IOException {
98 | AnsibleInstallation[] installations = allInstallations();
99 | if (ansibleInstallation == null) {
100 | if (installations.length == 0) {
101 | throw new IOException("Ansible not found");
102 | }
103 | return installations[0];
104 | } else {
105 | for (AnsibleInstallation installation : installations) {
106 | if (ansibleInstallation.equals(installation.getName())) {
107 | return installation;
108 | }
109 | }
110 | }
111 | throw new IOException("Ansible not found");
112 | }
113 |
114 | @Override
115 | public void buildEnvVars(EnvVars env) {
116 | String home = Util.fixEmpty(getHome());
117 | if (home != null) {
118 | env.put("PATH+ANSIBLE", home);
119 | }
120 | }
121 |
122 | @Extension
123 | @Symbol("ansible")
124 | public static class DescriptorImpl extends ToolDescriptor {
125 |
126 | public DescriptorImpl() {
127 | load();
128 | }
129 |
130 | @Override
131 | public boolean configure(StaplerRequest2 req, JSONObject json) throws FormException {
132 | super.configure(req, json);
133 | save();
134 | return true;
135 | }
136 |
137 | @Override
138 | public String getDisplayName() {
139 | return "Ansible";
140 | }
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/ansible/AnsibleVaultInvocation.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed under the Apache License, Version 2.0 (the "License");
3 | * you may not use this file except in compliance with the License.
4 | * You may obtain a copy of the License at
5 | *
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | *
8 | * Unless required by applicable law or agreed to in writing, software
9 | * distributed under the License is distributed on an "AS IS" BASIS,
10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 | * See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 | package org.jenkinsci.plugins.ansible;
15 |
16 | import com.cloudbees.plugins.credentials.common.StandardCredentials;
17 | import hudson.AbortException;
18 | import hudson.EnvVars;
19 | import hudson.FilePath;
20 | import hudson.model.AbstractBuild;
21 | import hudson.model.BuildListener;
22 | import hudson.model.Run;
23 | import hudson.model.TaskListener;
24 | import hudson.util.ArgumentListBuilder;
25 | import java.io.IOException;
26 | import org.jenkinsci.plugins.plaincredentials.FileCredentials;
27 | import org.jenkinsci.plugins.plaincredentials.StringCredentials;
28 |
29 | /**
30 | * Invoke the ansible-vault command
31 | *
32 | * @author Michael Cresswell
33 | */
34 | public class AnsibleVaultInvocation extends AbstractAnsibleInvocation {
35 |
36 | private String action;
37 | private String content;
38 | private String input;
39 | private String output;
40 | private StandardCredentials newVaultCredentials;
41 |
42 | private FilePath newVaultPassword = null;
43 |
44 | private FilePath ws = null;
45 |
46 | protected AnsibleVaultInvocation(String exe, AbstractBuild, ?> build, BuildListener listener, EnvVars envVars)
47 | throws IOException, InterruptedException, AnsibleInvocationException {
48 | this(exe, build, build.getWorkspace(), listener, envVars);
49 | }
50 |
51 | public AnsibleVaultInvocation(String exe, Run, ?> build, FilePath ws, TaskListener listener, EnvVars envVars)
52 | throws IOException, InterruptedException, AnsibleInvocationException {
53 | super(exe, build, ws, listener, envVars);
54 | this.ws = ws;
55 | }
56 |
57 | public AnsibleVaultInvocation setAction(String action) {
58 | this.action = action;
59 | return this;
60 | }
61 |
62 | private ArgumentListBuilder appendAction(ArgumentListBuilder args) throws AbortException {
63 | if (!"edit".equals(action) && !"create".equals(action) && !"view".equals(action)) {
64 | args.add(action);
65 | } else {
66 | throw new AbortException(action
67 | + ": ansible-plugin does not support interactive vault actions such as create, edit, or view.");
68 | }
69 | return args;
70 | }
71 |
72 | public AnsibleVaultInvocation setContent(String content) {
73 | this.content = content;
74 | return this;
75 | }
76 |
77 | private ArgumentListBuilder appendContent(ArgumentListBuilder args) {
78 | if (content != null && !content.isEmpty()) {
79 | args.addMasked(content);
80 | }
81 | return args;
82 | }
83 |
84 | public AnsibleVaultInvocation setInput(String input) {
85 | this.input = input;
86 | return this;
87 | }
88 |
89 | private ArgumentListBuilder appendInput(ArgumentListBuilder args) {
90 | if (input != null && !input.isEmpty()) {
91 | args.add(input);
92 | }
93 | return args;
94 | }
95 |
96 | public AnsibleVaultInvocation setNewVaultCredentials(StandardCredentials newVaultCredentials) {
97 | this.newVaultCredentials = newVaultCredentials;
98 | return this;
99 | }
100 |
101 | protected ArgumentListBuilder appendNewVaultPasswordFile(ArgumentListBuilder args)
102 | throws IOException, InterruptedException {
103 | if (newVaultCredentials != null) {
104 | FilePath tmpPath = vaultTmpPath != null ? vaultTmpPath : ws;
105 | if (newVaultCredentials instanceof FileCredentials) {
106 | FileCredentials secretFile = (FileCredentials) newVaultCredentials;
107 | newVaultPassword = Utils.createVaultPasswordFile(newVaultPassword, tmpPath, secretFile);
108 | args.add("--new-vault-password-file")
109 | .add(newVaultPassword.getRemote().replace("%", "%%"));
110 | } else if (newVaultCredentials instanceof StringCredentials) {
111 | StringCredentials secretText = (StringCredentials) newVaultCredentials;
112 | newVaultPassword = Utils.createVaultPasswordFile(newVaultPassword, tmpPath, secretText);
113 | args.add("--new-vault-password-file")
114 | .add(newVaultPassword.getRemote().replace("%", "%%"));
115 | }
116 | }
117 | return args;
118 | }
119 |
120 | public AnsibleVaultInvocation setOutput(String output) {
121 | this.output = output;
122 | return this;
123 | }
124 |
125 | private ArgumentListBuilder appendOutput(ArgumentListBuilder args) {
126 | if (output != null && !output.isEmpty()) {
127 | args.add(output);
128 | }
129 | return args;
130 | }
131 |
132 | @Override
133 | protected ArgumentListBuilder buildCommandLine()
134 | throws InterruptedException, AnsibleInvocationException, IOException {
135 | ArgumentListBuilder args = new ArgumentListBuilder();
136 | prependPasswordCredentials(args);
137 | appendExecutable(args);
138 | appendAction(args);
139 | appendVaultPasswordFile(args);
140 | appendNewVaultPasswordFile(args);
141 | appendOutput(args);
142 | appendContent(args);
143 | appendInput(args);
144 | return args;
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 | org.jenkins-ci.plugins
6 | plugin
7 | 5.28
8 |
9 |
10 | org.jenkins-ci.plugins
11 | ansible
12 | ${changelist}
13 | hpi
14 | Jenkins Ansible plugin
15 | Ansible support in Jenkins
16 |
17 |
18 | MIT License
19 | https://opensource.org/license/mit/
20 |
21 |
22 |
23 | scm:git:https://github.com/${gitHubRepo}.git
24 | scm:git:https@github.com:${gitHubRepo}.git
25 | ${scmTag}
26 | https://github.com/${gitHubRepo}
27 |
28 |
29 | 999999-SNAPSHOT
30 | jenkinsci/ansible-plugin
31 |
32 | 2.492
33 | ${jenkins.baseline}.3
34 | false
35 | false
36 |
37 |
38 | 1.21.3
39 |
40 |
41 |
42 |
43 |
44 | io.jenkins.tools.bom
45 | bom-${jenkins.baseline}.x
46 | 5473.vb_9533d9e5d88
47 | pom
48 | import
49 |
50 |
51 |
52 |
53 |
54 | io.jenkins.plugins
55 | commons-lang3-api
56 |
57 |
58 | org.jenkins-ci
59 | symbol-annotation
60 | true
61 |
62 |
63 | org.jenkins-ci.plugins
64 | credentials
65 |
66 |
67 | org.jenkins-ci.plugins
68 | job-dsl
69 | true
70 |
71 |
72 | org.jenkins-ci.plugins
73 | plain-credentials
74 |
75 |
76 | org.jenkins-ci.plugins
77 | ssh-credentials
78 |
79 |
80 |
81 | org.jenkins-ci.plugins.workflow
82 | workflow-step-api
83 | true
84 |
85 |
86 | junit
87 | junit
88 | test
89 |
90 |
91 | org.jenkins-ci.plugins
92 | pipeline-utility-steps
93 | test
94 |
95 |
96 | org.jenkins-ci.plugins
97 | ssh-slaves
98 | test
99 |
100 |
101 | org.jenkins-ci.plugins.workflow
102 | workflow-cps
103 | test
104 |
105 |
106 | org.jenkins-ci.plugins.workflow
107 | workflow-job
108 | test
109 |
110 |
111 | org.jenkinsci.plugins
112 | pipeline-model-definition
113 | test
114 |
115 |
116 | org.mockito
117 | mockito-core
118 | test
119 |
120 |
121 | org.testcontainers
122 | junit-jupiter
123 | ${testcontainer.version}
124 | test
125 |
126 |
127 | org.testcontainers
128 | testcontainers
129 | ${testcontainer.version}
130 | test
131 |
132 |
133 | org.apache.commons
134 | commons-compress
135 |
136 |
137 | org.jetbrains
138 | annotations
139 |
140 |
141 | org.slf4j
142 | slf4j-api
143 |
144 |
145 |
146 |
147 |
148 |
149 | repo.jenkins-ci.org
150 | https://repo.jenkins-ci.org/public/
151 |
152 |
153 |
154 |
155 | repo.jenkins-ci.org
156 | https://repo.jenkins-ci.org/public/
157 |
158 |
159 |
160 |
161 |
162 | org.apache.maven.plugins
163 | maven-surefire-plugin
164 |
165 | all
166 | true
167 | 1C
168 |
169 |
170 |
171 |
172 |
173 |
--------------------------------------------------------------------------------
/src/test/java/org/jenkinsci/plugins/ansible/AnsibleAdHocCommandInvocationTest.java:
--------------------------------------------------------------------------------
1 | package org.jenkinsci.plugins.ansible;
2 |
3 | import static org.hamcrest.CoreMatchers.is;
4 | import static org.hamcrest.MatcherAssert.assertThat;
5 | import static org.hamcrest.Matchers.aMapWithSize;
6 | import static org.hamcrest.Matchers.hasEntry;
7 | import static org.mockito.Mockito.*;
8 |
9 | import hudson.EnvVars;
10 | import hudson.model.AbstractBuild;
11 | import hudson.model.BuildListener;
12 | import hudson.model.TaskListener;
13 | import hudson.util.ArgumentListBuilder;
14 | import java.util.Map;
15 | import org.junit.jupiter.api.Test;
16 | import org.mockito.ArgumentCaptor;
17 |
18 | /**
19 | * Created with IntelliJ IDEA.
20 | * User: jcsirot
21 | * Date: 22/05/15
22 | * Time: 19:30
23 | * To change this template use File | Settings | File Templates.
24 | */
25 | class AnsibleAdHocCommandInvocationTest {
26 |
27 | @Test
28 | void should_generate_simple_invocation() throws Exception {
29 | // Given
30 | Inventory inventory = new InventoryPath("/tmp/hosts");
31 | BuildListener listener = mock(BuildListener.class);
32 | CLIRunner runner = mock(CLIRunner.class);
33 | AbstractBuild, ?> build = mock(AbstractBuild.class);
34 | when(build.getEnvironment(any(TaskListener.class))).thenReturn(new EnvVars());
35 | AnsibleAdHocCommandInvocation invocation =
36 | new AnsibleAdHocCommandInvocation("/usr/local/bin/ansible", build, listener);
37 | invocation.setHostPattern("localhost");
38 | invocation.setInventory(inventory);
39 | invocation.setModule("ping");
40 | invocation.setForks(5);
41 | // When
42 | invocation.execute(runner);
43 | // Then
44 | ArgumentCaptor argument = ArgumentCaptor.forClass(ArgumentListBuilder.class);
45 | verify(runner).execute(argument.capture(), anyMap());
46 | assertThat(argument.getValue().toString(), is("/usr/local/bin/ansible localhost -i /tmp/hosts -m ping -f 5"));
47 | }
48 |
49 | @Test
50 | void should_generate_no_forks() throws Exception {
51 | // Given
52 | Inventory inventory = new InventoryPath("/tmp/hosts");
53 | BuildListener listener = mock(BuildListener.class);
54 | CLIRunner runner = mock(CLIRunner.class);
55 | AbstractBuild, ?> build = mock(AbstractBuild.class);
56 | when(build.getEnvironment(any(TaskListener.class))).thenReturn(new EnvVars());
57 | AnsibleAdHocCommandInvocation invocation =
58 | new AnsibleAdHocCommandInvocation("/usr/local/bin/ansible", build, listener);
59 | invocation.setHostPattern("localhost");
60 | invocation.setInventory(inventory);
61 | invocation.setModule("ping");
62 | invocation.setForks(0);
63 | // When
64 | invocation.execute(runner);
65 | // Then
66 | ArgumentCaptor argument = ArgumentCaptor.forClass(ArgumentListBuilder.class);
67 | verify(runner).execute(argument.capture(), anyMap());
68 | assertThat(argument.getValue().toString(), is("/usr/local/bin/ansible localhost -i /tmp/hosts -m ping"));
69 | }
70 |
71 | @Test
72 | void should_generate_simple_invocation_with_env() throws Exception {
73 | // Given
74 | Inventory inventory = new InventoryPath("/tmp/hosts");
75 | BuildListener listener = mock(BuildListener.class);
76 | CLIRunner runner = mock(CLIRunner.class);
77 | AbstractBuild, ?> build = mock(AbstractBuild.class);
78 | when(build.getEnvironment(any(TaskListener.class))).thenReturn(new EnvVars());
79 | AnsibleAdHocCommandInvocation invocation =
80 | new AnsibleAdHocCommandInvocation("/usr/local/bin/ansible", build, listener);
81 | invocation.setHostPattern("localhost");
82 | invocation.setInventory(inventory);
83 | invocation.setModule("ping");
84 | invocation.setForks(5);
85 | invocation.setColorizedOutput(true);
86 | invocation.setDisableHostKeyCheck(true);
87 | invocation.setUnbufferedOutput(true);
88 | // When
89 | invocation.execute(runner);
90 | // Then
91 | ArgumentCaptor> argument = ArgumentCaptor.forClass(Map.class);
92 | verify(runner).execute(any(ArgumentListBuilder.class), argument.capture());
93 |
94 | assertThat(argument.getValue(), hasEntry("PYTHONUNBUFFERED", "1"));
95 | assertThat(argument.getValue(), hasEntry("ANSIBLE_FORCE_COLOR", "true"));
96 | assertThat(argument.getValue(), hasEntry("ANSIBLE_HOST_KEY_CHECKING", "False"));
97 | }
98 |
99 | @Test
100 | void secure_by_default_SEC_630() throws Exception {
101 | // Given
102 | Inventory inventory = new InventoryPath("/tmp/hosts");
103 | BuildListener listener = mock(BuildListener.class);
104 | CLIRunner runner = mock(CLIRunner.class);
105 | AbstractBuild, ?> build = mock(AbstractBuild.class);
106 | when(build.getEnvironment(any(TaskListener.class))).thenReturn(new EnvVars());
107 | AnsibleAdHocCommandInvocation invocation =
108 | new AnsibleAdHocCommandInvocation("/usr/local/bin/ansible", build, listener);
109 | invocation.setHostPattern("localhost");
110 | invocation.setInventory(inventory);
111 | invocation.setModule("ping");
112 | invocation.setForks(5);
113 | invocation.setColorizedOutput(true);
114 | // invocation.setDisableHostKeyCheck(true);
115 | invocation.setUnbufferedOutput(true);
116 | // When
117 | invocation.execute(runner);
118 | // Then
119 | ArgumentCaptor> argument = ArgumentCaptor.forClass(Map.class);
120 | verify(runner).execute(any(ArgumentListBuilder.class), argument.capture());
121 |
122 | assertThat(argument.getValue(), aMapWithSize(2));
123 | assertThat(argument.getValue(), hasEntry("PYTHONUNBUFFERED", "1"));
124 | assertThat(argument.getValue(), hasEntry("ANSIBLE_FORCE_COLOR", "true"));
125 | }
126 |
127 | @Test
128 | void should_handle_variables() throws Exception {
129 | // Given
130 | Inventory inventory = new InventoryPath("/tmp/hosts");
131 | BuildListener listener = mock(BuildListener.class);
132 | CLIRunner runner = mock(CLIRunner.class);
133 | AbstractBuild, ?> build = mock(AbstractBuild.class);
134 | EnvVars vars = new EnvVars();
135 | vars.put("MODULE", "ping");
136 | when(build.getEnvironment(any(TaskListener.class))).thenReturn(vars);
137 | AnsibleAdHocCommandInvocation invocation =
138 | new AnsibleAdHocCommandInvocation("/usr/local/bin/ansible", build, listener);
139 | invocation.setHostPattern("localhost");
140 | invocation.setInventory(inventory);
141 | invocation.setModule("${MODULE}");
142 | invocation.setForks(5);
143 | // When
144 | invocation.execute(runner);
145 | // Then
146 | ArgumentCaptor argument = ArgumentCaptor.forClass(ArgumentListBuilder.class);
147 | verify(runner).execute(argument.capture(), anyMap());
148 |
149 | assertThat(argument.getValue().toString(), is("/usr/local/bin/ansible localhost -i /tmp/hosts -m ping -f 5"));
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/ansible/AnsibleVaultBuilder.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed under the Apache License, Version 2.0 (the "License");
3 | * you may not use this file except in compliance with the License.
4 | * You may obtain a copy of the License at
5 | *
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | *
8 | * Unless required by applicable law or agreed to in writing, software
9 | * distributed under the License is distributed on an "AS IS" BASIS,
10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 | * See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 | package org.jenkinsci.plugins.ansible;
15 |
16 | import com.cloudbees.plugins.credentials.CredentialsProvider;
17 | import com.cloudbees.plugins.credentials.common.StandardCredentials;
18 | import edu.umd.cs.findbugs.annotations.NonNull;
19 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
20 | import hudson.AbortException;
21 | import hudson.EnvVars;
22 | import hudson.Extension;
23 | import hudson.FilePath;
24 | import hudson.Launcher;
25 | import hudson.Util;
26 | import hudson.model.Computer;
27 | import hudson.model.Node;
28 | import hudson.model.Run;
29 | import hudson.model.TaskListener;
30 | import hudson.tasks.BuildStepMonitor;
31 | import hudson.tasks.Builder;
32 | import hudson.util.FormValidation;
33 | import java.io.File;
34 | import java.io.IOException;
35 | import jenkins.tasks.SimpleBuildStep;
36 | import org.apache.commons.lang3.StringUtils;
37 | import org.kohsuke.stapler.DataBoundConstructor;
38 | import org.kohsuke.stapler.DataBoundSetter;
39 | import org.kohsuke.stapler.QueryParameter;
40 |
41 | /**
42 | * A builder which wraps an Ansible vault invocation.
43 | *
44 | * @author Michael Cresswell
45 | */
46 | public class AnsibleVaultBuilder extends Builder implements SimpleBuildStep {
47 |
48 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility.")
49 | public String ansibleName = null;
50 |
51 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility.")
52 | public String action = "encrypt_string";
53 |
54 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility.")
55 | public String vaultCredentialsId = null;
56 |
57 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility.")
58 | public String newVaultCredentialsId = null;
59 |
60 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility.")
61 | public String vaultTmpPath = null;
62 |
63 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility.")
64 | public String content = null;
65 |
66 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility.")
67 | public String input = null;
68 |
69 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility.")
70 | public String output = null;
71 |
72 | @DataBoundConstructor
73 | public AnsibleVaultBuilder() {}
74 |
75 | @DataBoundSetter
76 | public void setAnsibleName(String ansibleName) {
77 | this.ansibleName = ansibleName;
78 | }
79 |
80 | @DataBoundSetter
81 | public void setAction(String action) {
82 | this.action = action;
83 | }
84 |
85 | @DataBoundSetter
86 | public void setVaultCredentialsId(String vaultCredentialsId) {
87 | this.vaultCredentialsId = vaultCredentialsId;
88 | }
89 |
90 | @DataBoundSetter
91 | public void setNewVaultCredentialsId(String newVaultCredentialsId) {
92 | this.newVaultCredentialsId = newVaultCredentialsId;
93 | }
94 |
95 | @DataBoundSetter
96 | public void setVaultTmpPath(String vaultTmpPath) {
97 | this.vaultTmpPath = vaultTmpPath;
98 | }
99 |
100 | @DataBoundSetter
101 | public void setContent(String content) {
102 | this.content = content;
103 | }
104 |
105 | @DataBoundSetter
106 | public void setInput(String input) {
107 | this.input = input;
108 | }
109 |
110 | @DataBoundSetter
111 | public void setOutput(String output) {
112 | this.output = output;
113 | }
114 |
115 | @Override
116 | public void perform(
117 | @NonNull Run, ?> run, @NonNull FilePath ws, @NonNull Launcher launcher, @NonNull TaskListener listener)
118 | throws InterruptedException, IOException {
119 | Computer computer = ws.toComputer();
120 | Node node;
121 | if (computer == null || (node = computer.getNode()) == null) {
122 | throw new AbortException("The ansible vault build step requires to be launched on a node");
123 | }
124 | perform(run, node, ws, launcher, listener, run.getEnvironment(listener));
125 | }
126 |
127 | public void perform(
128 | @NonNull Run, ?> run,
129 | @NonNull Node node,
130 | @NonNull FilePath ws,
131 | @NonNull Launcher launcher,
132 | @NonNull TaskListener listener,
133 | EnvVars envVars)
134 | throws InterruptedException, IOException {
135 | try {
136 | CLIRunner runner = new CLIRunner(run, ws, launcher, listener);
137 | Computer computer = node.toComputer();
138 | String exe = AnsibleInstallation.getExecutable(
139 | ansibleName, AnsibleCommand.ANSIBLE_VAULT, node, listener, envVars);
140 | AnsibleVaultInvocation invocation = new AnsibleVaultInvocation(exe, run, ws, listener, envVars);
141 | invocation.setAction(action);
142 | invocation.setVaultCredentials(
143 | StringUtils.isNotBlank(vaultCredentialsId)
144 | ? CredentialsProvider.findCredentialById(
145 | run.getEnvironment(listener).expand(vaultCredentialsId),
146 | StandardCredentials.class,
147 | run)
148 | : null);
149 | invocation.setNewVaultCredentials(
150 | StringUtils.isNotBlank(newVaultCredentialsId)
151 | ? CredentialsProvider.findCredentialById(
152 | run.getEnvironment(listener).expand(newVaultCredentialsId),
153 | StandardCredentials.class,
154 | run)
155 | : null);
156 | invocation.setVaultTmpPath(
157 | StringUtils.isNotBlank(vaultTmpPath)
158 | ? new FilePath(computer.getChannel(), new File(vaultTmpPath).getAbsolutePath())
159 | : null);
160 | invocation.setContent(content);
161 | invocation.setInput(input);
162 | invocation.setOutput(output);
163 | if (!invocation.execute(runner)) {
164 | throw new AbortException("Ansible vault execution failed");
165 | }
166 | } catch (IOException ioe) {
167 | Util.displayIOException(ioe, listener);
168 | ioe.printStackTrace(listener.fatalError("command execution failed"));
169 | throw ioe;
170 | } catch (AnsibleInvocationException aie) {
171 | listener.fatalError(aie.getMessage());
172 | throw new AbortException(aie.getMessage());
173 | }
174 | }
175 |
176 | @Override
177 | public BuildStepMonitor getRequiredMonitorService() {
178 | return BuildStepMonitor.NONE;
179 | }
180 |
181 | @Extension
182 | public static final class DescriptorImpl extends AbstractAnsibleBuilderDescriptor {
183 | public DescriptorImpl() {
184 | super("Invoke Ansible Vault");
185 | }
186 |
187 | public FormValidation doCheckVaultCredentialsId(@QueryParameter String vaultCredentialsId) {
188 | return checkNotNullOrEmpty(vaultCredentialsId, "Vault credentials must not be empty");
189 | }
190 | }
191 | }
192 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/ansible/jobdsl/context/AnsibleContext.java:
--------------------------------------------------------------------------------
1 | package org.jenkinsci.plugins.ansible.jobdsl.context;
2 |
3 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
4 | import java.util.List;
5 | import javaposse.jobdsl.dsl.Context;
6 | import javaposse.jobdsl.plugin.ContextExtensionPoint;
7 | import org.jenkinsci.plugins.ansible.ExtraVar;
8 | import org.jenkinsci.plugins.ansible.Inventory;
9 | import org.jenkinsci.plugins.ansible.InventoryContent;
10 | import org.jenkinsci.plugins.ansible.InventoryPath;
11 |
12 | /**
13 | * @author lanwen (Merkushev Kirill)
14 | */
15 | public class AnsibleContext implements Context {
16 | private Inventory inventory;
17 | private String ansibleName;
18 | private String action;
19 | private String credentialsId;
20 | private String vaultCredentialsId;
21 | private String newVaultCredentialsId;
22 | private String vaultTmpPath = null;
23 | private String content;
24 | private String input;
25 | private String output;
26 | private boolean become = false;
27 | private String becomeUser = "root";
28 | private boolean sudo = false;
29 | private String sudoUser = "root";
30 | private int forks = 5;
31 | private boolean checkMode = false;
32 | private boolean unbufferedOutput = true;
33 | private boolean colorizedOutput = false;
34 | private boolean disableHostKeyChecking = false;
35 |
36 | @Deprecated
37 | @SuppressWarnings("unused")
38 | @SuppressFBWarnings("URF_UNREAD_FIELD")
39 | private transient boolean hostKeyChecking = true;
40 |
41 | private String additionalParameters;
42 | ExtraVarsContext extraVarsContext = new ExtraVarsContext();
43 |
44 | /* adhoc-only */
45 |
46 | private String hostPattern;
47 |
48 | /* playbook-only */
49 |
50 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility.")
51 | public String limit;
52 |
53 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility.")
54 | public String tags;
55 |
56 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility.")
57 | public String skippedTags;
58 |
59 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility.")
60 | public String startAtTask;
61 |
62 | public void inventoryContent(String content, boolean dynamic) {
63 | this.inventory = new InventoryContent(content, dynamic);
64 | }
65 |
66 | public void inventoryContent(String content) {
67 | this.inventory = new InventoryContent(content, false);
68 | }
69 |
70 | public void inventoryPath(String path) {
71 | this.inventory = new InventoryPath(path);
72 | }
73 |
74 | public void ansibleName(String ansibleName) {
75 | this.ansibleName = ansibleName;
76 | }
77 |
78 | public void action(String action) {
79 | this.action = action;
80 | }
81 |
82 | public void credentialsId(String credentialsId) {
83 | this.credentialsId = credentialsId;
84 | }
85 |
86 | public void vaultCredentialsId(String vaultCredentialsId) {
87 | this.vaultCredentialsId = vaultCredentialsId;
88 | }
89 |
90 | public void newVaultCredentialsId(String newVaultCredentialsId) {
91 | this.newVaultCredentialsId = newVaultCredentialsId;
92 | }
93 |
94 | public void setVaultTmpPath(String vaultTmpPath) {
95 | this.vaultTmpPath = vaultTmpPath;
96 | }
97 |
98 | public void content(String content) {
99 | this.content = content;
100 | }
101 |
102 | public void input(String input) {
103 | this.input = input;
104 | }
105 |
106 | public void output(String output) {
107 | this.output = output;
108 | }
109 |
110 | public void become(boolean become) {
111 | this.become = become;
112 | }
113 |
114 | public void becomeUser(String becomeUser) {
115 | this.becomeUser = becomeUser;
116 | }
117 |
118 | public void checkMode(boolean checkMode) {
119 | this.checkMode = checkMode;
120 | }
121 |
122 | public void sudo(boolean sudo) {
123 | this.sudo = sudo;
124 | }
125 |
126 | public void sudoUser(String sudoUser) {
127 | this.sudoUser = sudoUser;
128 | }
129 |
130 | public void forks(int forks) {
131 | this.forks = forks;
132 | }
133 |
134 | public void unbufferedOutput(boolean unbufferedOutput) {
135 | this.unbufferedOutput = unbufferedOutput;
136 | }
137 |
138 | public void colorizedOutput(boolean colorizedOutput) {
139 | this.colorizedOutput = colorizedOutput;
140 | }
141 |
142 | public void disableHostKeyChecking(boolean disableHostKeyChecking) {
143 | this.disableHostKeyChecking = disableHostKeyChecking;
144 | }
145 |
146 | public void additionalParameters(String additionalParameters) {
147 | this.additionalParameters = additionalParameters;
148 | }
149 |
150 | public void hostPattern(String hostPattern) {
151 | this.hostPattern = hostPattern;
152 | }
153 |
154 | public void limit(String limit) {
155 | this.limit = limit;
156 | }
157 |
158 | public void tags(String tags) {
159 | this.tags = tags;
160 | }
161 |
162 | public void skippedTags(String skippedTags) {
163 | this.skippedTags = skippedTags;
164 | }
165 |
166 | public void startAtTask(String startAtTask) {
167 | this.startAtTask = startAtTask;
168 | }
169 |
170 | public void extraVars(Runnable closure) {
171 | ContextExtensionPoint.executeInContext(closure, extraVarsContext);
172 | }
173 |
174 | public String getAction() {
175 | return action;
176 | }
177 |
178 | public String getAnsibleName() {
179 | return ansibleName;
180 | }
181 |
182 | public String getCredentialsId() {
183 | return credentialsId;
184 | }
185 |
186 | public String getVaultCredentialsId() {
187 | return vaultCredentialsId;
188 | }
189 |
190 | public String getNewVaultCredentialsId() {
191 | return newVaultCredentialsId;
192 | }
193 |
194 | public String getVaultTmpPath() {
195 | return vaultTmpPath;
196 | }
197 |
198 | public String getContent() {
199 | return content;
200 | }
201 |
202 | public String getInput() {
203 | return input;
204 | }
205 |
206 | public String getOutput() {
207 | return output;
208 | }
209 |
210 | public Inventory getInventory() {
211 | return inventory;
212 | }
213 |
214 | public boolean isBecome() {
215 | return become;
216 | }
217 |
218 | public String getBecomeUser() {
219 | return becomeUser;
220 | }
221 |
222 | public boolean isSudo() {
223 | return sudo;
224 | }
225 |
226 | public String getSudoUser() {
227 | return sudoUser;
228 | }
229 |
230 | public int getForks() {
231 | return forks;
232 | }
233 |
234 | public boolean isUnbufferedOutput() {
235 | return unbufferedOutput;
236 | }
237 |
238 | public boolean isColorizedOutput() {
239 | return colorizedOutput;
240 | }
241 |
242 | public boolean isCheckMode() {
243 | return checkMode;
244 | }
245 |
246 | public boolean isDisableHostKeyChecking() {
247 | return disableHostKeyChecking;
248 | }
249 |
250 | public String getAdditionalParameters() {
251 | return additionalParameters;
252 | }
253 |
254 | public String getHostPattern() {
255 | return hostPattern;
256 | }
257 |
258 | public String getLimit() {
259 | return limit;
260 | }
261 |
262 | public String getTags() {
263 | return tags;
264 | }
265 |
266 | public String getSkippedTags() {
267 | return skippedTags;
268 | }
269 |
270 | public String getStartAtTask() {
271 | return startAtTask;
272 | }
273 |
274 | public List getExtraVars() {
275 | return extraVarsContext.getExtraVars();
276 | }
277 |
278 | @Deprecated
279 | public void hostKeyChecking(boolean hostKeyChecking) {}
280 |
281 | @Deprecated
282 | public boolean isHostKeyChecking() {
283 | return true;
284 | }
285 | }
286 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/ansible/workflow/AnsibleVaultStep.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015-2016 Jean-Christophe Sirot
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.jenkinsci.plugins.ansible.workflow;
17 |
18 | import static com.cloudbees.plugins.credentials.CredentialsMatchers.anyOf;
19 | import static com.cloudbees.plugins.credentials.CredentialsMatchers.instanceOf;
20 |
21 | import com.cloudbees.plugins.credentials.CredentialsProvider;
22 | import com.cloudbees.plugins.credentials.common.StandardCredentials;
23 | import com.cloudbees.plugins.credentials.common.StandardListBoxModel;
24 | import com.google.inject.Inject;
25 | import hudson.*;
26 | import hudson.model.Computer;
27 | import hudson.model.Item;
28 | import hudson.model.Node;
29 | import hudson.model.Run;
30 | import hudson.model.TaskListener;
31 | import hudson.util.ListBoxModel;
32 | import jenkins.model.Jenkins;
33 | import org.jenkinsci.plugins.ansible.AnsibleInstallation;
34 | import org.jenkinsci.plugins.ansible.AnsibleVaultBuilder;
35 | import org.jenkinsci.plugins.plaincredentials.FileCredentials;
36 | import org.jenkinsci.plugins.plaincredentials.StringCredentials;
37 | import org.jenkinsci.plugins.workflow.steps.AbstractStepDescriptorImpl;
38 | import org.jenkinsci.plugins.workflow.steps.AbstractStepImpl;
39 | import org.jenkinsci.plugins.workflow.steps.AbstractSynchronousNonBlockingStepExecution;
40 | import org.jenkinsci.plugins.workflow.steps.StepContextParameter;
41 | import org.kohsuke.stapler.AncestorInPath;
42 | import org.kohsuke.stapler.DataBoundConstructor;
43 | import org.kohsuke.stapler.DataBoundSetter;
44 | import org.kohsuke.stapler.QueryParameter;
45 |
46 | /**
47 | * The Ansible vault invocation step for the Jenkins workflow plugin.
48 | */
49 | public class AnsibleVaultStep extends AbstractStepImpl {
50 |
51 | private String installation;
52 | private String action;
53 | private String vaultCredentialsId;
54 | private String vaultTmpPath;
55 | private String newVaultCredentialsId;
56 | private String content = null;
57 | private String input = null;
58 | private String output = null;
59 |
60 | @DataBoundConstructor
61 | public AnsibleVaultStep() {}
62 |
63 | @DataBoundSetter
64 | public void setAction(String action) {
65 | this.action = action;
66 | }
67 |
68 | @DataBoundSetter
69 | public void setVaultCredentialsId(String vaultCredentialsId) {
70 | this.vaultCredentialsId = Util.fixEmptyAndTrim(vaultCredentialsId);
71 | }
72 |
73 | @DataBoundSetter
74 | public void setNewVaultCredentialsId(String newVaultCredentialsId) {
75 | this.newVaultCredentialsId = Util.fixEmptyAndTrim(newVaultCredentialsId);
76 | }
77 |
78 | @DataBoundSetter
79 | public void setVaultTmpPath(String vaultTmpPath) {
80 | this.vaultTmpPath = vaultTmpPath;
81 | }
82 |
83 | @DataBoundSetter
84 | public void setContent(String content) {
85 | this.content = content;
86 | }
87 |
88 | @DataBoundSetter
89 | public void setInput(String input) {
90 | this.input = input;
91 | }
92 |
93 | @DataBoundSetter
94 | public void setOutput(String output) {
95 | this.output = output;
96 | }
97 |
98 | @DataBoundSetter
99 | public void setInstallation(String installation) {
100 | this.installation = Util.fixEmptyAndTrim(installation);
101 | }
102 |
103 | public String getInstallation() {
104 | return installation;
105 | }
106 |
107 | public String getAction() {
108 | return action;
109 | }
110 |
111 | public String getVaultCredentialsId() {
112 | return vaultCredentialsId;
113 | }
114 |
115 | public String getNewVaultCredentialsId() {
116 | return newVaultCredentialsId;
117 | }
118 |
119 | public String getVaultTmpPath() {
120 | return vaultTmpPath;
121 | }
122 |
123 | public String getContent() {
124 | return content;
125 | }
126 |
127 | public String getInput() {
128 | return input;
129 | }
130 |
131 | public String getOutput() {
132 | return output;
133 | }
134 |
135 | @Extension
136 | public static final class DescriptorImpl extends AbstractStepDescriptorImpl {
137 |
138 | public DescriptorImpl() {
139 | super(AnsibleVaultExecution.class);
140 | }
141 |
142 | @Override
143 | public String getFunctionName() {
144 | return "ansibleVault";
145 | }
146 |
147 | @Override
148 | public String getDisplayName() {
149 | return "Invoke ansible vault";
150 | }
151 |
152 | public ListBoxModel doFillVaultCredentialsIdItems(
153 | @AncestorInPath Item item, @QueryParameter String vaultCredentialsId) {
154 | return fillVaultCredentials(item, vaultCredentialsId);
155 | }
156 |
157 | public ListBoxModel doFillNewVaultCredentialsIdItems(
158 | @AncestorInPath Item item, @QueryParameter String newVaultCredentialsId) {
159 | return fillVaultCredentials(item, newVaultCredentialsId);
160 | }
161 |
162 | public ListBoxModel doFillInstallationItems() {
163 | ListBoxModel model = new ListBoxModel();
164 | for (AnsibleInstallation tool : AnsibleInstallation.allInstallations()) {
165 | model.add(tool.getName());
166 | }
167 | return model;
168 | }
169 |
170 | private ListBoxModel fillVaultCredentials(Item item, String credentialsId) {
171 | StandardListBoxModel result = new StandardListBoxModel();
172 | if (item == null) {
173 | if (!Jenkins.getActiveInstance().hasPermission(Jenkins.ADMINISTER)) {
174 | return result.includeCurrentValue(credentialsId);
175 | }
176 | } else {
177 | if (!item.hasPermission(Item.EXTENDED_READ) && !item.hasPermission(CredentialsProvider.USE_ITEM)) {
178 | return result.includeCurrentValue(credentialsId);
179 | }
180 | }
181 |
182 | return result.includeEmptyValue()
183 | .withMatching(
184 | anyOf(instanceOf(FileCredentials.class), instanceOf(StringCredentials.class)),
185 | CredentialsProvider.lookupCredentials(StandardCredentials.class, item))
186 | .includeCurrentValue(credentialsId);
187 | }
188 | }
189 |
190 | public static final class AnsibleVaultExecution extends AbstractSynchronousNonBlockingStepExecution {
191 |
192 | private static final long serialVersionUID = 1;
193 |
194 | @Inject
195 | private transient AnsibleVaultStep step;
196 |
197 | @StepContextParameter
198 | private transient TaskListener listener;
199 |
200 | @StepContextParameter
201 | private transient Launcher launcher;
202 |
203 | @StepContextParameter
204 | private transient Run, ?> run;
205 |
206 | @StepContextParameter
207 | private transient FilePath ws;
208 |
209 | @StepContextParameter
210 | private transient EnvVars envVars;
211 |
212 | @StepContextParameter
213 | private transient Computer computer;
214 |
215 | @Override
216 | protected Void run() throws Exception {
217 | AnsibleVaultBuilder builder = new AnsibleVaultBuilder();
218 | builder.setAnsibleName(step.getInstallation());
219 | builder.setAction(step.getAction());
220 | builder.setVaultCredentialsId(step.getVaultCredentialsId());
221 | builder.setNewVaultCredentialsId(step.getNewVaultCredentialsId());
222 | builder.setVaultTmpPath(step.getVaultTmpPath());
223 | builder.setContent(step.getContent());
224 | builder.setInput(step.getInput());
225 | builder.setOutput(step.getOutput());
226 | Node node;
227 | if (computer == null || (node = computer.getNode()) == null) {
228 | throw new AbortException("The ansible vault build step requires to be launched on a node");
229 | }
230 | builder.perform(run, node, ws, launcher, listener, envVars);
231 | return null;
232 | }
233 | }
234 | }
235 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/ansible/workflow/AnsibleAdhocStep.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015-2016 Jean-Christophe Sirot
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.jenkinsci.plugins.ansible.workflow;
17 |
18 | import static com.cloudbees.plugins.credentials.CredentialsMatchers.anyOf;
19 | import static com.cloudbees.plugins.credentials.CredentialsMatchers.instanceOf;
20 |
21 | import com.cloudbees.jenkins.plugins.sshcredentials.SSHUserPrivateKey;
22 | import com.cloudbees.plugins.credentials.CredentialsProvider;
23 | import com.cloudbees.plugins.credentials.common.StandardCredentials;
24 | import com.cloudbees.plugins.credentials.common.StandardListBoxModel;
25 | import com.cloudbees.plugins.credentials.common.StandardUsernameCredentials;
26 | import com.cloudbees.plugins.credentials.common.UsernamePasswordCredentials;
27 | import com.google.inject.Inject;
28 | import hudson.*;
29 | import hudson.model.Computer;
30 | import hudson.model.Project;
31 | import hudson.model.Run;
32 | import hudson.model.TaskListener;
33 | import hudson.util.ListBoxModel;
34 | import java.util.List;
35 | import org.apache.commons.lang3.StringUtils;
36 | import org.jenkinsci.plugins.ansible.AnsibleAdHocCommandBuilder;
37 | import org.jenkinsci.plugins.ansible.AnsibleInstallation;
38 | import org.jenkinsci.plugins.ansible.ExtraVar;
39 | import org.jenkinsci.plugins.ansible.Inventory;
40 | import org.jenkinsci.plugins.ansible.InventoryContent;
41 | import org.jenkinsci.plugins.ansible.InventoryDoNotSpecify;
42 | import org.jenkinsci.plugins.ansible.InventoryPath;
43 | import org.jenkinsci.plugins.plaincredentials.FileCredentials;
44 | import org.jenkinsci.plugins.plaincredentials.StringCredentials;
45 | import org.jenkinsci.plugins.workflow.steps.AbstractStepDescriptorImpl;
46 | import org.jenkinsci.plugins.workflow.steps.AbstractStepImpl;
47 | import org.jenkinsci.plugins.workflow.steps.AbstractSynchronousNonBlockingStepExecution;
48 | import org.jenkinsci.plugins.workflow.steps.StepContextParameter;
49 | import org.kohsuke.stapler.AncestorInPath;
50 | import org.kohsuke.stapler.DataBoundConstructor;
51 | import org.kohsuke.stapler.DataBoundSetter;
52 |
53 | /**
54 | * The Ansible adhoc invocation step for the Jenkins workflow plugin.
55 | */
56 | public class AnsibleAdhocStep extends AbstractStepImpl {
57 |
58 | private String hosts;
59 | private String module;
60 | private String moduleArguments;
61 | private String inventory;
62 | private String inventoryContent;
63 | private boolean dynamicInventory = false;
64 | private String installation;
65 | private String credentialsId;
66 | private String vaultCredentialsId;
67 | private String vaultTmpPath = null;
68 | private boolean become = false;
69 | private String becomeUser = "root";
70 | private List extraVars = null;
71 | private String extras = null;
72 | private boolean colorized = false;
73 | private int forks = 0;
74 | private boolean hostKeyChecking = false;
75 |
76 | @DataBoundConstructor
77 | public AnsibleAdhocStep(String hosts) {
78 | this.hosts = hosts;
79 | }
80 |
81 | @DataBoundSetter
82 | public void setModule(String module) {
83 | this.module = Util.fixEmptyAndTrim(module);
84 | }
85 |
86 | @DataBoundSetter
87 | public void setModuleArguments(String moduleArguments) {
88 | this.moduleArguments = Util.fixEmptyAndTrim(moduleArguments);
89 | }
90 |
91 | @DataBoundSetter
92 | public void setInventory(String inventory) {
93 | this.inventory = Util.fixEmptyAndTrim(inventory);
94 | }
95 |
96 | @DataBoundSetter
97 | public void setInventoryContent(String inventoryContent) {
98 | this.inventoryContent = Util.fixEmptyAndTrim(inventoryContent);
99 | }
100 |
101 | @DataBoundSetter
102 | public void setDynamicInventory(boolean dynamicInventory) {
103 | this.dynamicInventory = dynamicInventory;
104 | }
105 |
106 | @DataBoundSetter
107 | public void setCredentialsId(String credentialsId) {
108 | this.credentialsId = Util.fixEmptyAndTrim(credentialsId);
109 | }
110 |
111 | @DataBoundSetter
112 | public void setVaultCredentialsId(String vaultCredentialsId) {
113 | this.vaultCredentialsId = Util.fixEmptyAndTrim(vaultCredentialsId);
114 | }
115 |
116 | @DataBoundSetter
117 | public void setVaultTmpPath(String vaultTmpPath) {
118 | this.vaultTmpPath = vaultTmpPath;
119 | }
120 |
121 | @DataBoundSetter
122 | public void setBecome(boolean become) {
123 | this.become = become;
124 | }
125 |
126 | @DataBoundSetter
127 | public void setBecomeUser(String becomeUser) {
128 | this.becomeUser = Util.fixEmptyAndTrim(becomeUser);
129 | }
130 |
131 | @DataBoundSetter
132 | public void setInstallation(String installation) {
133 | this.installation = Util.fixEmptyAndTrim(installation);
134 | }
135 |
136 | @DataBoundSetter
137 | public void setExtraVars(List extraVars) {
138 | this.extraVars = extraVars;
139 | }
140 |
141 | @DataBoundSetter
142 | public void setExtras(String extras) {
143 | this.extras = Util.fixEmptyAndTrim(extras);
144 | }
145 |
146 | @DataBoundSetter
147 | public void setColorized(boolean colorized) {
148 | this.colorized = colorized;
149 | }
150 |
151 | @DataBoundSetter
152 | public void setForks(int forks) {
153 | this.forks = forks;
154 | }
155 |
156 | @DataBoundSetter
157 | public void setHostKeyChecking(boolean hostKeyChecking) {
158 | this.hostKeyChecking = hostKeyChecking;
159 | }
160 |
161 | public String getInstallation() {
162 | return installation;
163 | }
164 |
165 | public String getHosts() {
166 | return hosts;
167 | }
168 |
169 | public String getModule() {
170 | return module;
171 | }
172 |
173 | public String getModuleArguments() {
174 | return moduleArguments;
175 | }
176 |
177 | public String getInventory() {
178 | return inventory;
179 | }
180 |
181 | public String getInventoryContent() {
182 | return inventoryContent;
183 | }
184 |
185 | public boolean isDynamicInventory() {
186 | return dynamicInventory;
187 | }
188 |
189 | public String getCredentialsId() {
190 | return credentialsId;
191 | }
192 |
193 | public String getVaultCredentialsId() {
194 | return vaultCredentialsId;
195 | }
196 |
197 | public String getVaultTmpPath() {
198 | return vaultTmpPath;
199 | }
200 |
201 | public boolean isBecome() {
202 | return become;
203 | }
204 |
205 | public String getBecomeUser() {
206 | return becomeUser;
207 | }
208 |
209 | public List getExtraVars() {
210 | return extraVars;
211 | }
212 |
213 | public String getExtras() {
214 | return extras;
215 | }
216 |
217 | public boolean isHostKeyChecking() {
218 | return hostKeyChecking;
219 | }
220 |
221 | public int getForks() {
222 | return forks;
223 | }
224 |
225 | public boolean isColorized() {
226 | return colorized;
227 | }
228 |
229 | @Extension
230 | public static final class DescriptorImpl extends AbstractStepDescriptorImpl {
231 |
232 | public DescriptorImpl() {
233 | super(AnsibleAdhocExecution.class);
234 | }
235 |
236 | @Override
237 | public String getFunctionName() {
238 | return "ansibleAdhoc";
239 | }
240 |
241 | @Override
242 | public String getDisplayName() {
243 | return "Invoke an ansible adhoc command";
244 | }
245 |
246 | public ListBoxModel doFillCredentialsIdItems(@AncestorInPath Project project) {
247 | return new StandardListBoxModel()
248 | .withEmptySelection()
249 | .withMatching(
250 | anyOf(instanceOf(SSHUserPrivateKey.class), instanceOf(UsernamePasswordCredentials.class)),
251 | CredentialsProvider.lookupCredentials(StandardUsernameCredentials.class, project));
252 | }
253 |
254 | public ListBoxModel doFillVaultCredentialsIdItems(@AncestorInPath Project project) {
255 | return new StandardListBoxModel()
256 | .withEmptySelection()
257 | .withMatching(
258 | anyOf(instanceOf(FileCredentials.class), instanceOf(StringCredentials.class)),
259 | CredentialsProvider.lookupCredentials(StandardCredentials.class, project));
260 | }
261 |
262 | public ListBoxModel doFillInstallationItems() {
263 | ListBoxModel model = new ListBoxModel();
264 | for (AnsibleInstallation tool : AnsibleInstallation.allInstallations()) {
265 | model.add(tool.getName());
266 | }
267 | return model;
268 | }
269 | }
270 |
271 | public static final class AnsibleAdhocExecution extends AbstractSynchronousNonBlockingStepExecution {
272 |
273 | private static final long serialVersionUID = 1;
274 |
275 | @Inject
276 | private transient AnsibleAdhocStep step;
277 |
278 | @StepContextParameter
279 | private transient TaskListener listener;
280 |
281 | @StepContextParameter
282 | private transient Launcher launcher;
283 |
284 | @StepContextParameter
285 | private transient Run, ?> run;
286 |
287 | @StepContextParameter
288 | private transient FilePath ws;
289 |
290 | @StepContextParameter
291 | private transient EnvVars envVars;
292 |
293 | @StepContextParameter
294 | private transient Computer computer;
295 |
296 | @Override
297 | protected Void run() throws Exception {
298 | Inventory inventory = null;
299 | if (StringUtils.isNotBlank(step.getInventory())) {
300 | inventory = new InventoryPath(step.getInventory());
301 | } else if (StringUtils.isNotBlank(step.getInventoryContent())) {
302 | inventory = new InventoryContent(step.getInventoryContent(), step.isDynamicInventory());
303 | } else {
304 | inventory = new InventoryDoNotSpecify();
305 | }
306 | AnsibleAdHocCommandBuilder builder = new AnsibleAdHocCommandBuilder(
307 | step.getHosts(), inventory, step.getModule(), step.getModuleArguments());
308 | builder.setAnsibleName(step.getInstallation());
309 | builder.setBecome(step.isBecome());
310 | builder.setBecomeUser(step.getBecomeUser());
311 | builder.setCredentialsId(step.getCredentialsId());
312 | builder.setVaultCredentialsId(step.getVaultCredentialsId());
313 | builder.setVaultTmpPath(step.getVaultTmpPath());
314 | builder.setForks(step.getForks());
315 | builder.setExtraVars(step.getExtraVars());
316 | builder.setAdditionalParameters(step.getExtras());
317 | builder.setHostKeyChecking(step.isHostKeyChecking());
318 | builder.setColorizedOutput(step.isColorized());
319 | builder.perform(run, ws, launcher, listener);
320 | return null;
321 | }
322 | }
323 | }
324 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/ansible/AnsibleAdHocCommandBuilder.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Jean-Christophe Sirot
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.jenkinsci.plugins.ansible;
17 |
18 | import com.cloudbees.plugins.credentials.CredentialsProvider;
19 | import com.cloudbees.plugins.credentials.common.StandardCredentials;
20 | import com.cloudbees.plugins.credentials.common.StandardUsernameCredentials;
21 | import edu.umd.cs.findbugs.annotations.NonNull;
22 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
23 | import hudson.AbortException;
24 | import hudson.EnvVars;
25 | import hudson.Extension;
26 | import hudson.FilePath;
27 | import hudson.Launcher;
28 | import hudson.Util;
29 | import hudson.model.Computer;
30 | import hudson.model.Run;
31 | import hudson.model.TaskListener;
32 | import hudson.tasks.Builder;
33 | import hudson.util.FormValidation;
34 | import java.io.File;
35 | import java.io.IOException;
36 | import java.util.List;
37 | import jenkins.tasks.SimpleBuildStep;
38 | import org.apache.commons.lang3.StringUtils;
39 | import org.kohsuke.stapler.DataBoundConstructor;
40 | import org.kohsuke.stapler.DataBoundSetter;
41 | import org.kohsuke.stapler.QueryParameter;
42 |
43 | /**
44 | * A builder which wraps an Ansible Ad-Hoc command invocation.
45 | */
46 | public class AnsibleAdHocCommandBuilder extends Builder implements SimpleBuildStep {
47 |
48 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility.")
49 | public String ansibleName;
50 |
51 | // SSH settings
52 | /**
53 | * The id of the credentials to use.
54 | */
55 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility.")
56 | public String credentialsId = null;
57 |
58 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility.")
59 | public String vaultCredentialsId = null;
60 |
61 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility.")
62 | public String vaultTmpPath = null;
63 |
64 | public final String hostPattern;
65 |
66 | /**
67 | * Path to the inventory file.
68 | */
69 | public final Inventory inventory;
70 |
71 | public final String module;
72 |
73 | public final String command;
74 |
75 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility.")
76 | public boolean become = false;
77 |
78 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility.")
79 | public String becomeUser = "root";
80 |
81 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility.")
82 | public boolean sudo = false;
83 |
84 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility.")
85 | public String sudoUser = "root";
86 |
87 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility.")
88 | public int forks = 0;
89 |
90 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility.")
91 | public boolean unbufferedOutput = true;
92 |
93 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility.")
94 | public boolean colorizedOutput = false;
95 |
96 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility.")
97 | public boolean disableHostKeyChecking = false;
98 |
99 | @Deprecated
100 | @SuppressWarnings("unused")
101 | @SuppressFBWarnings(value = {"URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD", "PA_PUBLIC_PRIMITIVE_ATTRIBUTE"})
102 | public transient boolean hostKeyChecking = true;
103 |
104 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility.")
105 | public String additionalParameters = null;
106 |
107 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility.")
108 | public List extraVars;
109 |
110 | @Deprecated
111 | public AnsibleAdHocCommandBuilder(
112 | String ansibleName,
113 | String hostPattern,
114 | Inventory inventory,
115 | String module,
116 | String command,
117 | String credentialsId,
118 | boolean sudo,
119 | String sudoUser,
120 | int forks,
121 | boolean unbufferedOutput,
122 | boolean colorizedOutput,
123 | boolean hostKeyChecking,
124 | String additionalParameters) {
125 | this.ansibleName = ansibleName;
126 | this.hostPattern = hostPattern;
127 | this.inventory = inventory;
128 | this.module = module;
129 | this.command = command;
130 | this.credentialsId = credentialsId;
131 | this.sudo = sudo;
132 | this.sudoUser = sudoUser;
133 | this.forks = forks;
134 | this.unbufferedOutput = unbufferedOutput;
135 | this.colorizedOutput = colorizedOutput;
136 | // ignored because of SECURITY-630
137 | // this.hostKeyChecking = hostKeyChecking;
138 | this.additionalParameters = additionalParameters;
139 | }
140 |
141 | @DataBoundConstructor
142 | public AnsibleAdHocCommandBuilder(String hostPattern, Inventory inventory, String module, String command) {
143 | this.hostPattern = hostPattern;
144 | this.inventory = inventory;
145 | this.module = module;
146 | this.command = command;
147 | }
148 |
149 | @DataBoundSetter
150 | public void setAnsibleName(String ansibleName) {
151 | this.ansibleName = ansibleName;
152 | }
153 |
154 | @DataBoundSetter
155 | public void setCredentialsId(String credentialsId) {
156 | this.credentialsId = credentialsId;
157 | }
158 |
159 | @DataBoundSetter
160 | public void setVaultCredentialsId(String vaultCredentialsId) {
161 | this.vaultCredentialsId = vaultCredentialsId;
162 | }
163 |
164 | @DataBoundSetter
165 | public void setVaultTmpPath(String vaultTmpPath) {
166 | this.vaultTmpPath = vaultTmpPath;
167 | }
168 |
169 | public void setBecome(boolean become) {
170 | this.become = become;
171 | }
172 |
173 | @DataBoundSetter
174 | public void setBecomeUser(String becomeUser) {
175 | this.becomeUser = becomeUser;
176 | }
177 |
178 | @DataBoundSetter
179 | public void setSudo(boolean sudo) {
180 | this.sudo = sudo;
181 | }
182 |
183 | @DataBoundSetter
184 | public void setSudoUser(String sudoUser) {
185 | this.sudoUser = sudoUser;
186 | }
187 |
188 | @DataBoundSetter
189 | public void setForks(int forks) {
190 | this.forks = forks;
191 | }
192 |
193 | @DataBoundSetter
194 | public void setUnbufferedOutput(boolean unbufferedOutput) {
195 | this.unbufferedOutput = unbufferedOutput;
196 | }
197 |
198 | @DataBoundSetter
199 | public void setColorizedOutput(boolean colorizedOutput) {
200 | this.colorizedOutput = colorizedOutput;
201 | }
202 |
203 | @DataBoundSetter
204 | public void setDisableHostKeyChecking(boolean disableHostKeyChecking) {
205 | this.disableHostKeyChecking = disableHostKeyChecking;
206 | }
207 |
208 | @DataBoundSetter
209 | @Deprecated
210 | public void setHostKeyChecking(boolean hostKeyChecking) {
211 | this.hostKeyChecking = true;
212 | }
213 |
214 | @DataBoundSetter
215 | public void setAdditionalParameters(String additionalParameters) {
216 | this.additionalParameters = additionalParameters;
217 | }
218 |
219 | @DataBoundSetter
220 | public void setExtraVars(List extraVars) {
221 | this.extraVars = extraVars;
222 | }
223 |
224 | @Override
225 | public void perform(
226 | @NonNull Run, ?> run, @NonNull FilePath ws, @NonNull Launcher launcher, @NonNull TaskListener listener)
227 | throws InterruptedException, IOException {
228 | try {
229 | CLIRunner runner = new CLIRunner(run, ws, launcher, listener);
230 | Computer computer = ws.toComputer();
231 | if (computer == null) {
232 | throw new AbortException("The ansible ad-hoc command build step requires to be launched on a node");
233 | }
234 | EnvVars envVars = run.getEnvironment(listener);
235 | String exe = AnsibleInstallation.getExecutable(
236 | ansibleName, AnsibleCommand.ANSIBLE, computer.getNode(), listener, envVars);
237 | AnsibleAdHocCommandInvocation invocation =
238 | new AnsibleAdHocCommandInvocation(exe, run, ws, listener, envVars);
239 | invocation.setHostPattern(hostPattern);
240 | invocation.setInventory(inventory);
241 | invocation.setModule(module);
242 | invocation.setModuleCommand(command);
243 | invocation.setBecome(become, becomeUser);
244 | invocation.setSudo(sudo, sudoUser);
245 | invocation.setForks(forks);
246 | invocation.setCredentials(
247 | StringUtils.isNotBlank(credentialsId)
248 | ? CredentialsProvider.findCredentialById(
249 | credentialsId, StandardUsernameCredentials.class, run)
250 | : null);
251 | invocation.setVaultCredentials(
252 | StringUtils.isNotBlank(vaultCredentialsId)
253 | ? CredentialsProvider.findCredentialById(vaultCredentialsId, StandardCredentials.class, run)
254 | : null);
255 | invocation.setVaultTmpPath(
256 | StringUtils.isNotBlank(vaultTmpPath)
257 | ? new FilePath(computer.getChannel(), new File(vaultTmpPath).getAbsolutePath())
258 | : null);
259 | invocation.setExtraVars(extraVars);
260 | invocation.setAdditionalParameters(additionalParameters);
261 | invocation.setDisableHostKeyCheck(disableHostKeyChecking);
262 | invocation.setUnbufferedOutput(unbufferedOutput);
263 | invocation.setColorizedOutput(colorizedOutput);
264 | if (!invocation.execute(runner)) {
265 | throw new AbortException("Ansible Ad-Hoc command execution failed");
266 | }
267 | } catch (IOException ioe) {
268 | Util.displayIOException(ioe, listener);
269 | ioe.printStackTrace(listener.fatalError("command execution failed"));
270 | throw ioe;
271 | } catch (AnsibleInvocationException aie) {
272 | listener.fatalError(aie.getMessage());
273 | throw new AbortException(aie.getMessage());
274 | }
275 | }
276 |
277 | @Extension
278 | public static final class DescriptorImpl extends AbstractAnsibleBuilderDescriptor {
279 |
280 | public DescriptorImpl() {
281 | super("Invoke Ansible Ad-Hoc Command");
282 | }
283 |
284 | public FormValidation doCheckHostPattern(@QueryParameter String hostPattern) {
285 | return checkNotNullOrEmpty(hostPattern, "Host pattern must not be empty");
286 | }
287 | }
288 | }
289 |
--------------------------------------------------------------------------------
/src/test/java/org/jenkinsci/plugins/ansible/jobdsl/JobDslIntegrationTest.java:
--------------------------------------------------------------------------------
1 | package org.jenkinsci.plugins.ansible.jobdsl;
2 |
3 | import static org.hamcrest.MatcherAssert.assertThat;
4 | import static org.hamcrest.Matchers.allOf;
5 | import static org.hamcrest.Matchers.containsString;
6 | import static org.hamcrest.Matchers.hasItem;
7 | import static org.hamcrest.Matchers.is;
8 | import static org.hamcrest.Matchers.isA;
9 | import static org.hamcrest.Matchers.notNullValue;
10 | import static org.junit.jupiter.api.Assumptions.assumeFalse;
11 |
12 | import com.cloudbees.plugins.credentials.CredentialsProvider;
13 | import com.cloudbees.plugins.credentials.CredentialsScope;
14 | import com.cloudbees.plugins.credentials.CredentialsStore;
15 | import com.cloudbees.plugins.credentials.domains.Domain;
16 | import com.google.common.io.Resources;
17 | import hudson.model.FreeStyleBuild;
18 | import hudson.model.FreeStyleProject;
19 | import hudson.model.ParameterValue;
20 | import hudson.model.ParametersAction;
21 | import hudson.model.StringParameterValue;
22 | import hudson.util.Secret;
23 | import java.nio.charset.StandardCharsets;
24 | import java.util.ArrayList;
25 | import java.util.List;
26 | import javaposse.jobdsl.plugin.ExecuteDslScripts;
27 | import javaposse.jobdsl.plugin.LookupStrategy;
28 | import javaposse.jobdsl.plugin.RemovedJobAction;
29 | import javaposse.jobdsl.plugin.RemovedViewAction;
30 | import org.apache.commons.lang3.SystemUtils;
31 | import org.jenkinsci.plugins.ansible.AnsibleAdHocCommandBuilder;
32 | import org.jenkinsci.plugins.ansible.AnsiblePlaybookBuilder;
33 | import org.jenkinsci.plugins.ansible.AnsibleVaultBuilder;
34 | import org.jenkinsci.plugins.ansible.InventoryContent;
35 | import org.jenkinsci.plugins.ansible.InventoryPath;
36 | import org.jenkinsci.plugins.plaincredentials.StringCredentials;
37 | import org.jenkinsci.plugins.plaincredentials.impl.StringCredentialsImpl;
38 | import org.junit.jupiter.api.Test;
39 | import org.jvnet.hudson.test.JenkinsRule;
40 | import org.jvnet.hudson.test.junit.jupiter.WithJenkins;
41 |
42 | /**
43 | * @author lanwen (Merkushev Kirill)
44 | */
45 | @WithJenkins
46 | class JobDslIntegrationTest {
47 | private static final String JOB_NAME_IN_DSL_SCRIPT = "ansible";
48 |
49 | private static final String ANSIBLE_DSL_GROOVY_PLAYBOOK = "jobdsl/playbook.groovy";
50 | private static final String ANSIBLE_DSL_GROOVY_EXPANDER = "jobdsl/expander.groovy";
51 | private static final String ANSIBLE_DSL_GROOVY_CHECK_MODE = "jobdsl/checkMode.groovy";
52 | private static final String ANSIBLE_DSL_GROOVY_SECURITY_630 = "jobdsl/security630.groovy";
53 | private static final String ANSIBLE_DSL_GROOVY_PLAYBOOK_LEGACY = "jobdsl/legacyPlaybook.groovy";
54 | private static final String ANSIBLE_DSL_GROOVY_ADHOC = "jobdsl/adhoc.groovy";
55 | private static final String ANSIBLE_DSL_GROOVY_VAULT = "jobdsl/vault.groovy";
56 | private static final String ANSIBLE_DSL_GROOVY_PLAYBOOK_BUILDER = "jobdsl/playbookBuilder.groovy";
57 |
58 | @Test
59 | void shouldCreateJobSecurity630Dsl(JenkinsRule r) throws Exception {
60 | AnsiblePlaybookBuilder step = generateJob(r, ANSIBLE_DSL_GROOVY_SECURITY_630)
61 | .getBuildersList()
62 | .get(AnsiblePlaybookBuilder.class);
63 | assertThat("Should add playbook builder", step, notNullValue());
64 | assertThat("disableHostKeyChecking", step.disableHostKeyChecking, is(false));
65 | }
66 |
67 | @Test
68 | void shouldCreateJobWithPlaybookDsl(JenkinsRule r) throws Exception {
69 | AnsiblePlaybookBuilder step =
70 | generateJob(r, ANSIBLE_DSL_GROOVY_PLAYBOOK).getBuildersList().get(AnsiblePlaybookBuilder.class);
71 | assertThat("Should add playbook builder", step, notNullValue());
72 |
73 | assertThat("playbook", step.playbook, is("path/playbook.yml"));
74 | assertThat("inventory", step.inventory, isA(InventoryPath.class));
75 | assertThat("ansibleName", step.ansibleName, is("1.9.4"));
76 | assertThat("limit", step.limit, is("retry.limit"));
77 | assertThat("tags", step.tags, is("one,two"));
78 | assertThat("skippedTags", step.skippedTags, is("three"));
79 | assertThat("startAtTask", step.startAtTask, is("task"));
80 | assertThat("credentialsId", step.credentialsId, is("credsid"));
81 | assertThat("become", step.become, is(true));
82 | assertThat("becomeUser", step.becomeUser, is("user"));
83 | assertThat("checkMode", step.checkMode, is(false));
84 | assertThat("sudo", step.sudo, is(false));
85 | assertThat("sudoUser", step.sudoUser, is("root"));
86 | assertThat("forks", step.forks, is(6));
87 | assertThat("unbufferedOutput", step.unbufferedOutput, is(false));
88 | assertThat("colorizedOutput", step.colorizedOutput, is(true));
89 | assertThat("disableHostKeyChecking", step.disableHostKeyChecking, is(false));
90 | assertThat("additionalParameters", step.additionalParameters, is("params"));
91 | assertThat("extraVar.key", step.extraVars.get(0).getKey(), is("key"));
92 | assertThat("extraVar.value", step.extraVars.get(0).getSecretValue().getPlainText(), is("value"));
93 | assertThat("extraVar.hidden", step.extraVars.get(0).isHidden(), is(true));
94 | }
95 |
96 | @Test
97 | void shouldCreateJobWithCheckMode(JenkinsRule r) throws Exception {
98 | AnsiblePlaybookBuilder step =
99 | generateJob(r, ANSIBLE_DSL_GROOVY_CHECK_MODE).getBuildersList().get(AnsiblePlaybookBuilder.class);
100 | assertThat("Should add playbook builder", step, notNullValue());
101 |
102 | assertThat("playbook", step.playbook, is("path/playbook.yml"));
103 | assertThat("inventory", step.inventory, isA(InventoryPath.class));
104 | assertThat("ansibleName", step.ansibleName, is("1.9.4"));
105 | assertThat("checkMode", step.checkMode, is(true));
106 | }
107 |
108 | @Test
109 | void shouldCreateJobWithVarExpander(JenkinsRule r) throws Exception {
110 |
111 | assumeFalse(SystemUtils.IS_OS_WINDOWS);
112 |
113 | // Add credentials
114 | StringCredentials vaultCredentials = new StringCredentialsImpl(
115 | CredentialsScope.GLOBAL,
116 | "vaultCredentialsString",
117 | "test username password",
118 | Secret.fromString("test-secret"));
119 | StringCredentials credentials = new StringCredentialsImpl(
120 | CredentialsScope.GLOBAL, "credentialsString", "test credentials", Secret.fromString("test"));
121 | CredentialsStore store =
122 | CredentialsProvider.lookupStores(r.jenkins).iterator().next();
123 | store.addCredentials(Domain.global(), vaultCredentials);
124 | store.addCredentials(Domain.global(), credentials);
125 |
126 | // Create job via jobdsl with var expander
127 | AnsiblePlaybookBuilder step =
128 | generateJob(r, ANSIBLE_DSL_GROOVY_EXPANDER).getBuildersList().get(AnsiblePlaybookBuilder.class);
129 | assertThat("Should add playbook builder", step, notNullValue());
130 | assertThat("playbook", step.playbook, is("playbook.yml"));
131 | assertThat("inventory", step.inventory, isA(InventoryPath.class));
132 | assertThat("vaultCredentialsId", step.vaultCredentialsId, is("${vault_credentials_id}"));
133 | assertThat("credentialsId", step.credentialsId, is("${credentials_id}"));
134 |
135 | List parameters = new ArrayList<>();
136 | parameters.add(new StringParameterValue("inventory_repository", "/ansible"));
137 | parameters.add(new StringParameterValue("vault_credentials_id", "vaultCredentialsString"));
138 | parameters.add(new StringParameterValue("credentials_id", "credentialsString"));
139 | ParametersAction parametersAction = new ParametersAction(parameters);
140 |
141 | FreeStyleProject freeStyleProject = r.getInstance().getItemByFullName("ansible", FreeStyleProject.class);
142 | FreeStyleBuild build =
143 | freeStyleProject.scheduleBuild2(0, parametersAction).get();
144 | assertThat(
145 | build.getLog(),
146 | allOf(containsString(
147 | "ansible-playbook playbook.yml -i /ansible/inventory.yml -f 5 --vault-password-file ")));
148 | }
149 |
150 | @Test
151 | void shouldCreateJobWithLegacyPlaybookDsl(JenkinsRule r) throws Exception {
152 | AnsiblePlaybookBuilder step = generateJob(r, ANSIBLE_DSL_GROOVY_PLAYBOOK_LEGACY)
153 | .getBuildersList()
154 | .get(AnsiblePlaybookBuilder.class);
155 | assertThat("Should add playbook builder", step, notNullValue());
156 |
157 | assertThat("playbook", step.playbook, is("path/playbook.yml"));
158 | assertThat("inventory", step.inventory, isA(InventoryPath.class));
159 | assertThat("ansibleName", step.ansibleName, is("1.9.4"));
160 | assertThat("limit", step.limit, is("retry.limit"));
161 | assertThat("tags", step.tags, is("one,two"));
162 | assertThat("skippedTags", step.skippedTags, is("three"));
163 | assertThat("startAtTask", step.startAtTask, is("task"));
164 | assertThat("credentialsId", step.credentialsId, is("credsid"));
165 | assertThat("become", step.become, is(false));
166 | assertThat("becomeUser", step.becomeUser, is("root"));
167 | assertThat("sudo", step.sudo, is(true));
168 | assertThat("sudoUser", step.sudoUser, is("user"));
169 | assertThat("forks", step.forks, is(6));
170 | assertThat("unbufferedOutput", step.unbufferedOutput, is(false));
171 | assertThat("colorizedOutput", step.colorizedOutput, is(true));
172 | assertThat("disableHostKeyChecking", step.disableHostKeyChecking, is(true));
173 | assertThat("additionalParameters", step.additionalParameters, is("params"));
174 | assertThat("extraVar.key", step.extraVars.get(0).getKey(), is("key"));
175 | assertThat("extraVar.value", step.extraVars.get(0).getSecretValue().getPlainText(), is("value"));
176 | assertThat("extraVar.hidden", step.extraVars.get(0).isHidden(), is(true));
177 | }
178 |
179 | @Test
180 | void shouldCreateJobAdhocDsl(JenkinsRule r) throws Exception {
181 | AnsibleAdHocCommandBuilder step =
182 | generateJob(r, ANSIBLE_DSL_GROOVY_ADHOC).getBuildersList().get(AnsibleAdHocCommandBuilder.class);
183 | assertThat("Should add adhoc builder", step, notNullValue());
184 |
185 | assertThat("module", step.module, is("module"));
186 | assertThat("inventory", step.inventory, isA(InventoryContent.class));
187 | assertThat("ansibleName", step.ansibleName, is("1.9.1"));
188 |
189 | assertThat("credentialsId", step.credentialsId, is("credsid"));
190 | assertThat("hostPattern", step.hostPattern, is("pattern"));
191 | assertThat("become", step.become, is(false));
192 | assertThat("becomeUser", step.becomeUser, is("root"));
193 | assertThat("forks", step.forks, is(5));
194 | assertThat("unbufferedOutput", step.unbufferedOutput, is(true));
195 | assertThat("colorizedOutput", step.colorizedOutput, is(false));
196 | assertThat("disableHostKeyChecking", step.disableHostKeyChecking, is(false));
197 | }
198 |
199 | @Test
200 | void shouldCreateJobWithVaultDsl(JenkinsRule r) throws Exception {
201 | AnsibleVaultBuilder step =
202 | generateJob(r, ANSIBLE_DSL_GROOVY_VAULT).getBuildersList().get(AnsibleVaultBuilder.class);
203 | assertThat("Should add playbook builder", step, notNullValue());
204 |
205 | assertThat("action", step.action, is("encrypt_string"));
206 | assertThat("content", step.content, is("my_secret"));
207 | assertThat("vaultCredentialsId", step.vaultCredentialsId, is("ansible_vault_credentials"));
208 | }
209 |
210 | @Test
211 | void shouldCreateJobWithPlaybookBuilderDsl(JenkinsRule r) throws Exception {
212 | AnsiblePlaybookBuilder step = generateJob(r, ANSIBLE_DSL_GROOVY_PLAYBOOK_BUILDER)
213 | .getBuildersList()
214 | .get(AnsiblePlaybookBuilder.class);
215 | assertThat("Should add playbook builder", step, notNullValue());
216 |
217 | assertThat("playbook", step.playbook, is("path/playbook.yml"));
218 | assertThat("extraVar.key", step.extraVars.get(0).getKey(), is("key"));
219 | assertThat("extraVar.value", step.extraVars.get(0).getSecretValue().getPlainText(), is("value"));
220 | assertThat("extraVar.hidden", step.extraVars.get(0).isHidden(), is(true));
221 | }
222 |
223 | private static FreeStyleProject generateJob(JenkinsRule rule, String script) throws Exception {
224 | FreeStyleProject job = rule.createFreeStyleProject();
225 | String scriptText = Resources.toString(Resources.getResource(script), StandardCharsets.UTF_8);
226 |
227 | ExecuteDslScripts builder = new ExecuteDslScripts();
228 | builder.setScriptText(scriptText);
229 | builder.setRemovedJobAction(RemovedJobAction.DELETE);
230 | builder.setRemovedViewAction(RemovedViewAction.DELETE);
231 | builder.setLookupStrategy(LookupStrategy.JENKINS_ROOT);
232 | job.getBuildersList().add(builder);
233 |
234 | rule.buildAndAssertSuccess(job);
235 |
236 | assertThat(rule.getInstance().getJobNames(), hasItem(is(JOB_NAME_IN_DSL_SCRIPT)));
237 |
238 | return rule.getInstance().getItemByFullName(JOB_NAME_IN_DSL_SCRIPT, FreeStyleProject.class);
239 | }
240 | }
241 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/ansible/AbstractAnsibleInvocation.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015-2016 Jean-Christophe Sirot
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.jenkinsci.plugins.ansible;
17 |
18 | import com.cloudbees.jenkins.plugins.sshcredentials.SSHUserPrivateKey;
19 | import com.cloudbees.plugins.credentials.common.StandardCredentials;
20 | import com.cloudbees.plugins.credentials.common.StandardUsernameCredentials;
21 | import com.cloudbees.plugins.credentials.common.UsernamePasswordCredentials;
22 | import hudson.EnvVars;
23 | import hudson.FilePath;
24 | import hudson.Util;
25 | import hudson.model.Run;
26 | import hudson.model.TaskListener;
27 | import hudson.util.ArgumentListBuilder;
28 | import hudson.util.Secret;
29 | import java.io.IOException;
30 | import java.util.HashMap;
31 | import java.util.List;
32 | import java.util.Map;
33 | import java.util.regex.Pattern;
34 | import org.apache.commons.lang3.StringUtils;
35 | import org.jenkinsci.plugins.plaincredentials.FileCredentials;
36 | import org.jenkinsci.plugins.plaincredentials.StringCredentials;
37 |
38 | /**
39 | * Ansible command invocation
40 | */
41 | abstract class AbstractAnsibleInvocation> {
42 |
43 | protected final EnvVars envVars;
44 | protected final TaskListener listener;
45 | protected final Run, ?> build;
46 | protected final Map environment;
47 |
48 | protected String exe;
49 | protected int forks;
50 | protected boolean become;
51 | protected String becomeUser;
52 | protected boolean sudo;
53 | protected String sudoUser;
54 | protected StandardCredentials vaultCredentials;
55 | protected FilePath vaultTmpPath = null;
56 | protected StandardUsernameCredentials credentials;
57 | protected List extraVars;
58 | protected String additionalParameters;
59 |
60 | private FilePath key = null;
61 | private FilePath script = null;
62 | private FilePath vaultPassword = null;
63 | private Inventory inventory;
64 | private boolean copyCredentialsInWorkspace = false;
65 | private final FilePath ws;
66 |
67 | protected AbstractAnsibleInvocation(
68 | String exe, Run, ?> build, FilePath ws, TaskListener listener, EnvVars envVars)
69 | throws IOException, InterruptedException, AnsibleInvocationException {
70 | this.build = build;
71 | this.ws = ws;
72 | this.envVars = envVars;
73 | this.environment = new HashMap(this.envVars);
74 | this.listener = listener;
75 | this.exe = exe;
76 | if (exe == null) {
77 | throw new AnsibleInvocationException("Ansible executable not found, check your installation.");
78 | }
79 | }
80 |
81 | protected ArgumentListBuilder appendExecutable(ArgumentListBuilder args) {
82 | args.add(exe);
83 | return args;
84 | }
85 |
86 | public T setInventory(Inventory inventory) {
87 | this.inventory = inventory;
88 | return (T) this;
89 | }
90 |
91 | protected ArgumentListBuilder appendInventory(ArgumentListBuilder args)
92 | throws IOException, InterruptedException, AnsibleInvocationException {
93 | if (inventory == null) {
94 | // throw new AnsibleInvocationException(
95 | // "The inventory of hosts and groups is not defined. Check the job configuration.");
96 | return args;
97 | }
98 | inventory.addArgument(args, ws, envVars, listener);
99 | return args;
100 | }
101 |
102 | public T setForks(int forks) {
103 | this.forks = Math.max(forks, 0);
104 | return (T) this;
105 | }
106 |
107 | public ArgumentListBuilder appendForks(ArgumentListBuilder args) {
108 | if (forks > 0) {
109 | args.add("-f").add(forks);
110 | }
111 | return args;
112 | }
113 |
114 | public T setExtraVars(List extraVars) {
115 | this.extraVars = extraVars;
116 | return (T) this;
117 | }
118 |
119 | public ArgumentListBuilder appendExtraVars(ArgumentListBuilder args) {
120 | if (extraVars != null && !extraVars.isEmpty()) {
121 | for (ExtraVar var : extraVars) {
122 | if (var.getSecretValue() == null) {
123 | listener.getLogger()
124 | .println(
125 | "[WARN] Omitting extra var " + var.getKey() + ": check value is a supported type.");
126 | continue;
127 | }
128 | args.add("-e");
129 | String value = envVars.expand(var.getSecretValue().getPlainText());
130 | if (Pattern.compile("\\s").matcher(value).find()) {
131 | value = Util.singleQuote(value);
132 | }
133 | StringBuilder sb = new StringBuilder();
134 | // assuming Groovy representation for Boolean values
135 | if (value.equals("true") || value.equals("false")) {
136 | // JSON format is required for Boolean variables
137 | sb.append("{\"")
138 | .append(envVars.expand(var.getKey()))
139 | .append("\":")
140 | .append(value)
141 | .append("}");
142 | } else {
143 | sb.append(envVars.expand(var.getKey())).append("=").append(value);
144 | }
145 | if (var.isHidden()) {
146 | args.addMasked(sb.toString());
147 | } else {
148 | args.add(sb.toString());
149 | }
150 | }
151 | }
152 | return args;
153 | }
154 |
155 | public T setAdditionalParameters(String additionalParameters) {
156 | this.additionalParameters = additionalParameters;
157 | return (T) this;
158 | }
159 |
160 | public ArgumentListBuilder appendAdditionalParameters(ArgumentListBuilder args) {
161 | args.addTokenized(envVars.expand(additionalParameters));
162 | return args;
163 | }
164 |
165 | public T setBecome(boolean become, String becomeUser) {
166 | this.become = become;
167 | this.becomeUser = becomeUser;
168 | return (T) this;
169 | }
170 |
171 | protected ArgumentListBuilder appendBecome(ArgumentListBuilder args) {
172 | if (become) {
173 | args.add("-b");
174 | addOptionAndValue(args, "--become-user", becomeUser);
175 | }
176 | return args;
177 | }
178 |
179 | public T setSudo(boolean sudo, String sudoUser) {
180 | this.sudo = sudo;
181 | this.sudoUser = sudoUser;
182 | return (T) this;
183 | }
184 |
185 | protected ArgumentListBuilder appendSudo(ArgumentListBuilder args) {
186 | if (sudo) {
187 | args.add("-s");
188 | addOptionAndValue(args, "-U", sudoUser);
189 | }
190 | return args;
191 | }
192 |
193 | protected void addOptionAndValue(final ArgumentListBuilder args, final String option, final String value) {
194 | if (StringUtils.isNotBlank(value)) {
195 | String expandedValue = envVars.expand(value);
196 | if (StringUtils.isNotBlank(expandedValue)) {
197 | args.add(option).add(expandedValue);
198 | } else {
199 | listener.getLogger()
200 | .println("[WARNING] parameter " + value + " is empty. Omitting option '" + option + "'.");
201 | }
202 | }
203 | }
204 |
205 | protected void addKeyValuePair(final ArgumentListBuilder args, final String key, final String value) {
206 | if (StringUtils.isNotBlank(value)) {
207 | String expandedValue = envVars.expand(value);
208 | if (StringUtils.isNotBlank(expandedValue)) {
209 | args.addKeyValuePair("", key, expandedValue, false);
210 | } else {
211 | listener.getLogger()
212 | .println("[WARNING] parameter " + value + " is empty. Omitting option '" + key + "'.");
213 | }
214 | }
215 | }
216 |
217 | public T setCredentials(StandardUsernameCredentials credentials) {
218 | this.credentials = credentials;
219 | return (T) this;
220 | }
221 |
222 | public T setCredentials(StandardUsernameCredentials credentials, boolean copyCredentialsInWorkspace) {
223 | this.copyCredentialsInWorkspace = copyCredentialsInWorkspace;
224 | return setCredentials(credentials);
225 | }
226 |
227 | public T setVaultCredentials(StandardCredentials vaultCredentials) {
228 | this.vaultCredentials = vaultCredentials;
229 | return (T) this;
230 | }
231 |
232 | public T setVaultTmpPath(FilePath vaultTmpPath) {
233 | this.vaultTmpPath = vaultTmpPath;
234 | return (T) this;
235 | }
236 |
237 | protected ArgumentListBuilder prependPasswordCredentials(ArgumentListBuilder args) {
238 | if (credentials instanceof UsernamePasswordCredentials) {
239 | UsernamePasswordCredentials passwordCredentials = (UsernamePasswordCredentials) credentials;
240 | args.add("sshpass").addMasked("-p" + Secret.toString(passwordCredentials.getPassword()));
241 | }
242 | return args;
243 | }
244 |
245 | protected ArgumentListBuilder appendCredentials(ArgumentListBuilder args) throws IOException, InterruptedException {
246 | if (credentials instanceof SSHUserPrivateKey) {
247 | FilePath tmpPath = vaultTmpPath != null ? vaultTmpPath : ws;
248 | SSHUserPrivateKey privateKeyCredentials = (SSHUserPrivateKey) credentials;
249 | key = Utils.createSshKeyFile(key, tmpPath, privateKeyCredentials, copyCredentialsInWorkspace);
250 | args.add("--private-key").add(key.getRemote().replace("%", "%%"));
251 | args.add("-u").add(privateKeyCredentials.getUsername());
252 | if (privateKeyCredentials.getPassphrase() != null) {
253 | script = Utils.createSshAskPassFile(script, tmpPath, privateKeyCredentials, copyCredentialsInWorkspace);
254 | environment.put("SSH_ASKPASS", script.getRemote());
255 | // inspired from https://github.com/jenkinsci/git-client-plugin/pull/168
256 | // but does not work with MacOSX
257 | if (!environment.containsKey("DISPLAY")) {
258 | environment.put("DISPLAY", ":123.456");
259 | }
260 | }
261 | } else if (credentials instanceof UsernamePasswordCredentials) {
262 | args.add("-u").add(credentials.getUsername());
263 | args.add("-k");
264 | }
265 | return args;
266 | }
267 |
268 | protected ArgumentListBuilder appendVaultPasswordFile(ArgumentListBuilder args)
269 | throws IOException, InterruptedException {
270 | if (vaultCredentials != null) {
271 | FilePath tmpPath = vaultTmpPath != null ? vaultTmpPath : ws;
272 | if (vaultCredentials instanceof FileCredentials) {
273 | FileCredentials secretFile = (FileCredentials) vaultCredentials;
274 | vaultPassword = Utils.createVaultPasswordFile(vaultPassword, tmpPath, secretFile);
275 | args.add("--vault-password-file").add(vaultPassword.getRemote().replace("%", "%%"));
276 | } else if (vaultCredentials instanceof StringCredentials) {
277 | StringCredentials secretText = (StringCredentials) vaultCredentials;
278 | vaultPassword = Utils.createVaultPasswordFile(vaultPassword, tmpPath, secretText);
279 | args.add("--vault-password-file").add(vaultPassword.getRemote().replace("%", "%%"));
280 | }
281 | }
282 | return args;
283 | }
284 |
285 | public T setUnbufferedOutput(boolean unbufferedOutput) {
286 | if (unbufferedOutput) {
287 | environment.put("PYTHONUNBUFFERED", "1");
288 | }
289 | return (T) this;
290 | }
291 |
292 | public T setColorizedOutput(boolean colorizedOutput) {
293 | if (colorizedOutput) {
294 | environment.put("ANSIBLE_FORCE_COLOR", "true");
295 | }
296 | return (T) this;
297 | }
298 |
299 | public T setDisableHostKeyCheck(boolean disableHostKeyChecking) {
300 | if (disableHostKeyChecking) {
301 | environment.put("ANSIBLE_HOST_KEY_CHECKING", "False");
302 | }
303 | return (T) this;
304 | }
305 |
306 | protected abstract ArgumentListBuilder buildCommandLine()
307 | throws InterruptedException, AnsibleInvocationException, IOException;
308 |
309 | public boolean execute(CLIRunner runner) throws IOException, InterruptedException, AnsibleInvocationException {
310 | try {
311 | return runner.execute(buildCommandLine(), environment);
312 | } finally {
313 | if (inventory != null) {
314 | inventory.tearDown(listener);
315 | }
316 | Utils.deleteTempFile(key, listener);
317 | Utils.deleteTempFile(script, listener);
318 | Utils.deleteTempFile(vaultPassword, listener);
319 | }
320 | }
321 | }
322 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/ansible/AnsiblePlaybookBuilder.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015-2016 Jean-Christophe Sirot
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.jenkinsci.plugins.ansible;
17 |
18 | import com.cloudbees.plugins.credentials.CredentialsProvider;
19 | import com.cloudbees.plugins.credentials.common.StandardCredentials;
20 | import com.cloudbees.plugins.credentials.common.StandardUsernameCredentials;
21 | import edu.umd.cs.findbugs.annotations.NonNull;
22 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
23 | import hudson.AbortException;
24 | import hudson.EnvVars;
25 | import hudson.Extension;
26 | import hudson.FilePath;
27 | import hudson.Launcher;
28 | import hudson.Util;
29 | import hudson.model.Computer;
30 | import hudson.model.Node;
31 | import hudson.model.Run;
32 | import hudson.model.TaskListener;
33 | import hudson.tasks.BuildStepMonitor;
34 | import hudson.tasks.Builder;
35 | import hudson.util.FormValidation;
36 | import java.io.File;
37 | import java.io.IOException;
38 | import java.util.List;
39 | import jenkins.tasks.SimpleBuildStep;
40 | import org.apache.commons.lang3.StringUtils;
41 | import org.kohsuke.stapler.DataBoundConstructor;
42 | import org.kohsuke.stapler.DataBoundSetter;
43 | import org.kohsuke.stapler.QueryParameter;
44 |
45 | /**
46 | * A builder which wraps an Ansible playbook invocation.
47 | */
48 | public class AnsiblePlaybookBuilder extends Builder implements SimpleBuildStep {
49 |
50 | public final String playbook;
51 |
52 | public final Inventory inventory;
53 |
54 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility.")
55 | public String ansibleName = null;
56 |
57 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility.")
58 | public String limit = null;
59 |
60 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility.")
61 | public String tags = null;
62 |
63 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility.")
64 | public String skippedTags = null;
65 |
66 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility.")
67 | public String startAtTask = null;
68 |
69 | /**
70 | * The id of the credentials to use.
71 | */
72 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility.")
73 | public String credentialsId = null;
74 |
75 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility.")
76 | public String vaultCredentialsId = null;
77 |
78 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility.")
79 | public String vaultTmpPath = null;
80 |
81 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility.")
82 | public boolean become = false;
83 |
84 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility.")
85 | public String becomeUser = "root";
86 |
87 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility.")
88 | public boolean checkMode = false;
89 |
90 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility.")
91 | public boolean sudo = false;
92 |
93 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility.")
94 | public String sudoUser = "root";
95 |
96 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility.")
97 | public int forks = 0;
98 |
99 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility.")
100 | public boolean unbufferedOutput = true;
101 |
102 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility.")
103 | public boolean colorizedOutput = false;
104 |
105 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility.")
106 | public boolean disableHostKeyChecking = false;
107 |
108 | @Deprecated
109 | @SuppressWarnings("unused")
110 | @SuppressFBWarnings(value = {"URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD", "PA_PUBLIC_PRIMITIVE_ATTRIBUTE"})
111 | public transient boolean hostKeyChecking = true;
112 |
113 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility.")
114 | public String additionalParameters = null;
115 |
116 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility.")
117 | public boolean copyCredentialsInWorkspace = false;
118 |
119 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility.")
120 | public List extraVars;
121 |
122 | @Deprecated
123 | public AnsiblePlaybookBuilder(
124 | String ansibleName,
125 | String playbook,
126 | Inventory inventory,
127 | String limit,
128 | String tags,
129 | String skippedTags,
130 | String startAtTask,
131 | String credentialsId,
132 | boolean sudo,
133 | String sudoUser,
134 | int forks,
135 | boolean unbufferedOutput,
136 | boolean colorizedOutput,
137 | boolean hostKeyChecking,
138 | String additionalParameters) {
139 | this.ansibleName = ansibleName;
140 | this.playbook = playbook;
141 | this.inventory = inventory;
142 | this.limit = limit;
143 | this.tags = tags;
144 | this.skippedTags = skippedTags;
145 | this.startAtTask = startAtTask;
146 | this.credentialsId = credentialsId;
147 | this.sudo = sudo;
148 | this.sudoUser = sudoUser;
149 | this.forks = forks;
150 | this.unbufferedOutput = unbufferedOutput;
151 | this.colorizedOutput = colorizedOutput;
152 | // this.hostKeyChecking = hostKeyChecking;
153 | this.additionalParameters = additionalParameters;
154 | }
155 |
156 | @DataBoundConstructor
157 | public AnsiblePlaybookBuilder(String playbook, Inventory inventory) {
158 | this.playbook = playbook;
159 | this.inventory = inventory;
160 | }
161 |
162 | @DataBoundSetter
163 | public void setAnsibleName(String ansibleName) {
164 | this.ansibleName = ansibleName;
165 | }
166 |
167 | @DataBoundSetter
168 | public void setLimit(String limit) {
169 | this.limit = limit;
170 | }
171 |
172 | @DataBoundSetter
173 | public void setTags(String tags) {
174 | this.tags = tags;
175 | }
176 |
177 | @DataBoundSetter
178 | public void setSkippedTags(String skippedTags) {
179 | this.skippedTags = skippedTags;
180 | }
181 |
182 | @DataBoundSetter
183 | public void setStartAtTask(String startAtTask) {
184 | this.startAtTask = startAtTask;
185 | }
186 |
187 | @DataBoundSetter
188 | public void setCredentialsId(String credentialsId) {
189 | setCredentialsId(credentialsId, false);
190 | }
191 |
192 | public void setCredentialsId(String credentialsId, boolean copyCredentialsInWorkspace) {
193 | this.credentialsId = credentialsId;
194 | this.copyCredentialsInWorkspace = copyCredentialsInWorkspace;
195 | }
196 |
197 | @DataBoundSetter
198 | public void setVaultCredentialsId(String vaultCredentialsId) {
199 | this.vaultCredentialsId = vaultCredentialsId;
200 | }
201 |
202 | @DataBoundSetter
203 | public void setVaultTmpPath(String vaultTmpPath) {
204 | this.vaultTmpPath = vaultTmpPath;
205 | }
206 |
207 | public void setBecome(boolean become) {
208 | this.become = become;
209 | }
210 |
211 | @DataBoundSetter
212 | public void setBecomeUser(String becomeUser) {
213 | this.becomeUser = becomeUser;
214 | }
215 |
216 | @DataBoundSetter
217 | public void setSudo(boolean sudo) {
218 | this.sudo = sudo;
219 | }
220 |
221 | @DataBoundSetter
222 | public void setCheckMode(boolean checkMode) {
223 | this.checkMode = checkMode;
224 | }
225 |
226 | @DataBoundSetter
227 | public void setSudoUser(String sudoUser) {
228 | this.sudoUser = sudoUser;
229 | }
230 |
231 | @DataBoundSetter
232 | public void setForks(int forks) {
233 | this.forks = forks;
234 | }
235 |
236 | @DataBoundSetter
237 | public void setUnbufferedOutput(boolean unbufferedOutput) {
238 | this.unbufferedOutput = unbufferedOutput;
239 | }
240 |
241 | @DataBoundSetter
242 | public void setColorizedOutput(boolean colorizedOutput) {
243 | this.colorizedOutput = colorizedOutput;
244 | }
245 |
246 | @DataBoundSetter
247 | public void setDisableHostKeyChecking(boolean disableHostKeyChecking) {
248 | this.disableHostKeyChecking = disableHostKeyChecking;
249 | }
250 |
251 | @DataBoundSetter
252 | @Deprecated
253 | public void setHostKeyChecking(boolean hostKeyChecking) {
254 | this.hostKeyChecking = true;
255 | }
256 |
257 | @DataBoundSetter
258 | public void setAdditionalParameters(String additionalParameters) {
259 | this.additionalParameters = additionalParameters;
260 | }
261 |
262 | @DataBoundSetter
263 | public void setExtraVars(List extraVars) {
264 | this.extraVars = extraVars;
265 | }
266 |
267 | @Override
268 | public void perform(
269 | @NonNull Run, ?> run, @NonNull FilePath ws, @NonNull Launcher launcher, @NonNull TaskListener listener)
270 | throws InterruptedException, IOException {
271 | Computer computer = ws.toComputer();
272 | Node node;
273 | if (computer == null || (node = computer.getNode()) == null) {
274 | throw new AbortException("The ansible playbook build step requires to be launched on a node");
275 | }
276 | perform(run, node, ws, launcher, listener, run.getEnvironment(listener));
277 | }
278 |
279 | public void perform(
280 | @NonNull Run, ?> run,
281 | @NonNull Node node,
282 | @NonNull FilePath ws,
283 | @NonNull Launcher launcher,
284 | @NonNull TaskListener listener,
285 | EnvVars envVars)
286 | throws InterruptedException, IOException {
287 | try {
288 | CLIRunner runner = new CLIRunner(run, ws, launcher, listener);
289 | Computer computer = node.toComputer();
290 | String exe = AnsibleInstallation.getExecutable(
291 | ansibleName, AnsibleCommand.ANSIBLE_PLAYBOOK, node, listener, envVars);
292 | AnsiblePlaybookInvocation invocation = new AnsiblePlaybookInvocation(exe, run, ws, listener, envVars);
293 | invocation.setPlaybook(playbook);
294 | invocation.setInventory(inventory);
295 | invocation.setLimit(limit);
296 | invocation.setTags(tags);
297 | invocation.setSkippedTags(skippedTags);
298 | invocation.setStartTask(startAtTask);
299 | invocation.setBecome(become, becomeUser);
300 | invocation.setCheckMode(checkMode);
301 | invocation.setSudo(sudo, sudoUser);
302 | invocation.setForks(forks);
303 | invocation.setCredentials(
304 | StringUtils.isNotBlank(credentialsId)
305 | ? CredentialsProvider.findCredentialById(
306 | run.getEnvironment(listener).expand(credentialsId),
307 | StandardUsernameCredentials.class,
308 | run)
309 | : null,
310 | copyCredentialsInWorkspace);
311 | invocation.setVaultCredentials(
312 | StringUtils.isNotBlank(vaultCredentialsId)
313 | ? CredentialsProvider.findCredentialById(
314 | run.getEnvironment(listener).expand(vaultCredentialsId),
315 | StandardCredentials.class,
316 | run)
317 | : null);
318 | invocation.setVaultTmpPath(
319 | StringUtils.isNotBlank(vaultTmpPath)
320 | ? new FilePath(computer.getChannel(), new File(vaultTmpPath).getAbsolutePath())
321 | : null);
322 | invocation.setExtraVars(extraVars);
323 | invocation.setAdditionalParameters(additionalParameters);
324 | invocation.setDisableHostKeyCheck(disableHostKeyChecking);
325 | invocation.setUnbufferedOutput(unbufferedOutput);
326 | invocation.setColorizedOutput(colorizedOutput);
327 | if (!invocation.execute(runner)) {
328 | throw new AbortException("Ansible playbook execution failed");
329 | }
330 | } catch (IOException ioe) {
331 | Util.displayIOException(ioe, listener);
332 | ioe.printStackTrace(listener.fatalError("command execution failed"));
333 | throw ioe;
334 | } catch (AnsibleInvocationException aie) {
335 | listener.fatalError(aie.getMessage());
336 | throw new AbortException(aie.getMessage());
337 | }
338 | }
339 |
340 | @Override
341 | public BuildStepMonitor getRequiredMonitorService() {
342 | return BuildStepMonitor.NONE;
343 | }
344 |
345 | @Extension
346 | public static final class DescriptorImpl extends AbstractAnsibleBuilderDescriptor {
347 | public DescriptorImpl() {
348 | super("Invoke Ansible Playbook");
349 | }
350 |
351 | public FormValidation doCheckPlaybook(@QueryParameter String playbook) {
352 | return checkNotNullOrEmpty(playbook, "Path to playbook must not be empty");
353 | }
354 | }
355 | }
356 |
--------------------------------------------------------------------------------