Skip to content

Commit c26740a

Browse files
authored
JENKINS-49056 Pipeline Support for Ansible Adhoc Commands (#89)
* JENKINS-49056 Pipeline Support for Ansible Adhoc Commands
1 parent a9176a8 commit c26740a

6 files changed

Lines changed: 449 additions & 22 deletions

File tree

README.md

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -44,35 +44,42 @@ allows for a convenient way of doing quick tasks with Ansible.
4444

4545
#### Scripted
4646

47-
Due
48-
to [JENKINS-43782](https://issues.jenkins.io/browse/JENKINS-43782)
49-
and [JENKINS-49056](https://issues.jenkins.io/browse/JENKINS-49056),
50-
adhoc commands cannot be run with a pipeline job.
47+
**Jenkinsfile**
48+
49+
``` groovy
50+
ansibleAdhoc credentialsId: 'private_key', inventory: 'inventories/a/hosts', hosts: 'hosts_pattern', moduleArguments: 'module_arguments'
51+
```
5152

5253
#### Declarative
5354

55+
**Jenkinsfile**
56+
57+
``` groovy
58+
ansibleAdhoc(credentialsId: 'private_key', inventory: 'inventories/a/hosts', hosts: 'hosts_pattern', moduleArguments: 'module_arguments')
59+
```
60+
5461
### Arguments
5562

5663
See also [jenkins.io](https://jenkins.io/doc/pipeline/steps/ansible/) documentation.
5764

58-
| Freestyle Name | Description |
59-
| -------------------------------------- | ------------------------------------------------------------- |
60-
| Ansible installation | Ansible installation to use for the playbook invocation |
61-
| Host pattern | The host pattern to manage. See Ansible Patterns for details. |
62-
| Module | CLI arg: `-m` |
63-
| Module arguments or command to execute | CLI arg: `-a` |
64-
| Inventory file or host list | CLI arg: `-i`: See the Inventory section for additional details. |
65-
| Inventory inline content | CLI arg: `-i`: See the Inventory section for additional details. |
66-
| Credentials | The Jenkins credential to use for the SSH connection. See the Authentication section for additional details. |
67-
| Vault Credentials | CLI arg: `--vault-password-file`: The Jenkins credential to use as the vault credential. See the Vault Credentials section for additional details. |
68-
| sudo | CLI arg: `-s` |
69-
| sudo user | CLI arg: `-U` |
70-
| Number of parallel processes | CLI arg: `-f` |
71-
| Check host SSH key | Toggle checking of the host key. Sets the environment variable `ANSIBLE_HOST_KEY_CHECKING`, similar to the recommendations for running with Vagrant. |
72-
| Unbuffered stdout | Toggle buffering of standard out. Sets the environment variable `PYTHONUNBUFFERED`, similar to the recommendations for running with Vagrant. |
73-
| Colorized stdout | Toggle color codes in console text. See Colorized Output section for example usage. Sets the environment variable `ANSIBLE_FORCE_COLOR`, similar to the recommendations for running with Vagrant. |
74-
| Extra Variables | CLI arg: `-e` |
75-
| Additional parameters | String passed to the Ansible Command Line invocation as-is. |
65+
| Freestyle Name | Pipeline Name | Description |
66+
| -------------------------------------- | ------------------ | ------------------------------------------------------------- |
67+
| Ansible installation | installation | Ansible installation to use for the playbook invocation |
68+
| Host pattern | hosts | The host pattern to manage. See Ansible Patterns for details. |
69+
| Module | module | CLI arg: `-m` |
70+
| Module arguments or command to execute | moduleArguments | CLI arg: `-a` |
71+
| Inventory file or host list | inventory | CLI arg: `-i`: See the Inventory section for additional details. |
72+
| Inventory inline content | inventoryContent | CLI arg: `-i`: See the Inventory section for additional details. |
73+
| Credentials | credentialsId | The Jenkins credential to use for the SSH connection. See the Authentication section for additional details. |
74+
| Vault Credentials | vaultCredentialsId | CLI arg: `--vault-password-file`: The Jenkins credential to use as the vault credential. See the Vault Credentials section for additional details. |
75+
| sudo | become | CLI arg: `-s` |
76+
| sudo user | becomeUser | CLI arg: `-U` |
77+
| Number of parallel processes | forks | CLI arg: `-f` |
78+
| Check host SSH key | hostKeyChecking | Toggle checking of the host key. Sets the environment variable `ANSIBLE_HOST_KEY_CHECKING`, similar to the recommendations for running with Vagrant. |
79+
| Unbuffered stdout | | Toggle buffering of standard out. Sets the environment variable `PYTHONUNBUFFERED`, similar to the recommendations for running with Vagrant. |
80+
| Colorized stdout | colorized | Toggle color codes in console text. See Colorized Output section for example usage. Sets the environment variable `ANSIBLE_FORCE_COLOR`, similar to the recommendations for running with Vagrant. |
81+
| Extra Variables | extraVars | CLI arg: `-e` |
82+
| Additional parameters | extras | String passed to the Ansible Command Line invocation as-is. |
7683

7784
## Playbook
7885

Lines changed: 318 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,318 @@
1+
/*
2+
* Copyright 2015-2016 Jean-Christophe Sirot <sirot@chelonix.com>
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 java.util.List;
22+
23+
import com.cloudbees.jenkins.plugins.sshcredentials.SSHUserPrivateKey;
24+
import com.cloudbees.plugins.credentials.CredentialsProvider;
25+
import com.cloudbees.plugins.credentials.common.StandardCredentials;
26+
import com.cloudbees.plugins.credentials.common.StandardListBoxModel;
27+
import com.cloudbees.plugins.credentials.common.StandardUsernameCredentials;
28+
import com.cloudbees.plugins.credentials.common.UsernamePasswordCredentials;
29+
import com.google.inject.Inject;
30+
import hudson.*;
31+
import hudson.model.Computer;
32+
import hudson.model.Project;
33+
import hudson.model.Run;
34+
import hudson.model.TaskListener;
35+
import hudson.util.ListBoxModel;
36+
import org.apache.commons.lang.StringUtils;
37+
import org.jenkinsci.plugins.ansible.AnsibleAdHocCommandBuilder;
38+
import org.jenkinsci.plugins.ansible.AnsibleInstallation;
39+
import org.jenkinsci.plugins.ansible.ExtraVar;
40+
import org.jenkinsci.plugins.ansible.Inventory;
41+
import org.jenkinsci.plugins.ansible.InventoryPath;
42+
import org.jenkinsci.plugins.ansible.InventoryContent;
43+
import org.jenkinsci.plugins.ansible.InventoryDoNotSpecify;
44+
import org.jenkinsci.plugins.plaincredentials.FileCredentials;
45+
import org.jenkinsci.plugins.plaincredentials.StringCredentials;
46+
import org.jenkinsci.plugins.workflow.steps.AbstractStepDescriptorImpl;
47+
import org.jenkinsci.plugins.workflow.steps.AbstractStepImpl;
48+
import org.jenkinsci.plugins.workflow.steps.AbstractSynchronousNonBlockingStepExecution;
49+
import org.jenkinsci.plugins.workflow.steps.StepContextParameter;
50+
import org.kohsuke.stapler.AncestorInPath;
51+
import org.kohsuke.stapler.DataBoundConstructor;
52+
import org.kohsuke.stapler.DataBoundSetter;
53+
54+
/**
55+
* The Ansible adhoc invocation step for the Jenkins workflow plugin.
56+
*/
57+
public class AnsibleAdhocStep extends AbstractStepImpl {
58+
59+
private String hosts;
60+
private String module;
61+
private String moduleArguments;
62+
private String inventory;
63+
private String inventoryContent;
64+
private boolean dynamicInventory = false;
65+
private String installation;
66+
private String credentialsId;
67+
private String vaultCredentialsId;
68+
private boolean become = false;
69+
private String becomeUser = "root";
70+
private List<ExtraVar> 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 setBecome(boolean become) {
118+
this.become = become;
119+
}
120+
121+
@DataBoundSetter
122+
public void setBecomeUser(String becomeUser) {
123+
this.becomeUser = Util.fixEmptyAndTrim(becomeUser);
124+
}
125+
126+
@DataBoundSetter
127+
public void setInstallation(String installation) {
128+
this.installation = Util.fixEmptyAndTrim(installation);
129+
}
130+
131+
@DataBoundSetter
132+
public void setExtraVars(List<ExtraVar> extraVars) {
133+
this.extraVars = extraVars;
134+
}
135+
136+
@DataBoundSetter
137+
public void setExtras(String extras) {
138+
this.extras = Util.fixEmptyAndTrim(extras);
139+
}
140+
141+
@DataBoundSetter
142+
public void setColorized(boolean colorized) {
143+
this.colorized = colorized;
144+
}
145+
146+
@DataBoundSetter
147+
public void setForks(int forks) {
148+
this.forks = forks;
149+
}
150+
151+
@DataBoundSetter
152+
public void setHostKeyChecking(boolean hostKeyChecking) {
153+
this.hostKeyChecking = hostKeyChecking;
154+
}
155+
156+
public String getInstallation() {
157+
return installation;
158+
}
159+
160+
public String getHosts() {
161+
return hosts;
162+
}
163+
164+
public String getModule() {
165+
return module;
166+
}
167+
168+
public String getModuleArguments() {
169+
return moduleArguments;
170+
}
171+
172+
public String getInventory() {
173+
return inventory;
174+
}
175+
176+
public String getInventoryContent() {
177+
return inventoryContent;
178+
}
179+
180+
public boolean isDynamicInventory() {
181+
return dynamicInventory;
182+
}
183+
184+
public String getCredentialsId() {
185+
return credentialsId;
186+
}
187+
188+
public String getVaultCredentialsId() {
189+
return vaultCredentialsId;
190+
}
191+
192+
public boolean isBecome() {
193+
return become;
194+
}
195+
196+
public String getBecomeUser() {
197+
return becomeUser;
198+
}
199+
200+
public List<ExtraVar> getExtraVars() {
201+
return extraVars;
202+
}
203+
204+
public String getExtras() {
205+
return extras;
206+
}
207+
208+
public boolean isHostKeyChecking() {
209+
return hostKeyChecking;
210+
}
211+
212+
public int getForks() {
213+
return forks;
214+
}
215+
216+
public boolean isColorized() {
217+
return colorized;
218+
}
219+
220+
@Extension
221+
public static final class DescriptorImpl extends AbstractStepDescriptorImpl {
222+
223+
public DescriptorImpl() {
224+
super(AnsibleAdhocExecution.class);
225+
}
226+
227+
@Override
228+
public String getFunctionName() {
229+
return "ansibleAdhoc";
230+
}
231+
232+
@Override
233+
public String getDisplayName() {
234+
return "Invoke an ansible adhoc command";
235+
}
236+
237+
public ListBoxModel doFillCredentialsIdItems(@AncestorInPath Project project) {
238+
return new StandardListBoxModel()
239+
.withEmptySelection()
240+
.withMatching(anyOf(
241+
instanceOf(SSHUserPrivateKey.class),
242+
instanceOf(UsernamePasswordCredentials.class)),
243+
CredentialsProvider.lookupCredentials(StandardUsernameCredentials.class, project));
244+
}
245+
246+
public ListBoxModel doFillVaultCredentialsIdItems(@AncestorInPath Project project) {
247+
return new StandardListBoxModel()
248+
.withEmptySelection()
249+
.withMatching(anyOf(
250+
instanceOf(FileCredentials.class),
251+
instanceOf(StringCredentials.class)),
252+
CredentialsProvider.lookupCredentials(StandardCredentials.class, project));
253+
}
254+
255+
public ListBoxModel doFillInstallationItems() {
256+
ListBoxModel model = new ListBoxModel();
257+
for (AnsibleInstallation tool : AnsibleInstallation.allInstallations()) {
258+
model.add(tool.getName());
259+
}
260+
return model;
261+
}
262+
}
263+
264+
public static final class AnsibleAdhocExecution extends AbstractSynchronousNonBlockingStepExecution<Void> {
265+
266+
private static final long serialVersionUID = 1;
267+
268+
@Inject
269+
private transient AnsibleAdhocStep step;
270+
271+
@StepContextParameter
272+
private transient TaskListener listener;
273+
274+
@StepContextParameter
275+
private transient Launcher launcher;
276+
277+
@StepContextParameter
278+
private transient Run<?,?> run;
279+
280+
@StepContextParameter
281+
private transient FilePath ws;
282+
283+
@StepContextParameter
284+
private transient EnvVars envVars;
285+
286+
@StepContextParameter
287+
private transient Computer computer;
288+
289+
@Override
290+
protected Void run() throws Exception {
291+
Inventory inventory = null;
292+
if (StringUtils.isNotBlank(step.getInventory())) {
293+
inventory = new InventoryPath(step.getInventory());
294+
} else if (StringUtils.isNotBlank(step.getInventoryContent())) {
295+
inventory = new InventoryContent(
296+
step.getInventoryContent(),
297+
step.isDynamicInventory()
298+
);
299+
} else {
300+
inventory = new InventoryDoNotSpecify();
301+
}
302+
AnsibleAdHocCommandBuilder builder = new AnsibleAdHocCommandBuilder(step.getHosts(), inventory, step.getModule(), step.getModuleArguments());
303+
builder.setAnsibleName(step.getInstallation());
304+
builder.setBecome(step.isBecome());
305+
builder.setBecomeUser(step.getBecomeUser());
306+
builder.setCredentialsId(step.getCredentialsId());
307+
builder.setVaultCredentialsId(step.getVaultCredentialsId());
308+
builder.setForks(step.getForks());
309+
builder.setExtraVars(step.getExtraVars());
310+
builder.setAdditionalParameters(step.getExtras());
311+
builder.setHostKeyChecking(step.isHostKeyChecking());
312+
builder.setColorizedOutput(step.isColorized());
313+
builder.perform(run, ws, launcher, listener);
314+
return null;
315+
}
316+
}
317+
318+
}

0 commit comments

Comments
 (0)