Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow specifying a different shell command other than /bin/sh #287

Merged
merged 2 commits into from
Mar 6, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,22 @@ There might be cases, where you need to have the agent pod run inside a differen
For example you may need the agent to run inside an `ephemeral` namespace for the sake of testing.
For those cases you can explicitly configure a namespace either using the ui or the pipeline.

#### Specifying a different shell command other than /bin/sh

By default, the shell command is /bin/sh. In some case, you would like to use another shell command like /bin/bash.

```groovy
podTemplate(label: my-label) {
node(my-label) {
stage('Run specific shell') {
container(name:'mycontainer', shell:'/bin/bash') {
sh 'echo hello world'
}
}
}
}
```

## Container Configuration
When configuring a container in a pipeline podTemplate the following options are available:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import org.apache.commons.lang.StringUtils;
import org.csanchez.jenkins.plugins.kubernetes.model.TemplateEnvVar;
Expand Down Expand Up @@ -50,6 +52,7 @@ public class ContainerTemplate extends AbstractDescribableImpl<ContainerTemplate
private String resourceLimitCpu;

private String resourceLimitMemory;
private String shell;

private final List<TemplateEnvVar> envVars = new ArrayList<>();
private List<PortMapping> ports = new ArrayList<PortMapping>();
Expand Down Expand Up @@ -214,6 +217,18 @@ public void setResourceRequestCpu(String resourceRequestCpu) {
this.resourceRequestCpu = resourceRequestCpu;
}

public Map<String,Object> getAsArgs() {
Map<String,Object> argMap = new TreeMap<>();

argMap.put("name", name);

if (!StringUtils.isEmpty(shell)) {
argMap.put("shell", shell);
}

return argMap;
}

@Extension
@Symbol("containerTemplate")
public static class DescriptorImpl extends Descriptor<ContainerTemplate> {
Expand Down Expand Up @@ -250,4 +265,13 @@ public String toString() {
(livenessProbe == null ? "" : ", livenessProbe=" + livenessProbe) +
'}';
}

public String getShell() {
return shell;
}

@DataBoundSetter
public void setShell(String shell) {
this.shell = shell;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@
import org.csanchez.jenkins.plugins.kubernetes.pipeline.proc.CachedProc;
import org.csanchez.jenkins.plugins.kubernetes.pipeline.proc.DeadProc;


import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.EnvVars;
Expand Down Expand Up @@ -77,6 +76,7 @@ public class ContainerExecDecorator extends LauncherDecorator implements Seriali
private static final String COOKIE_VAR = "JENKINS_SERVER_COOKIE";

private static final Logger LOGGER = Logger.getLogger(ContainerExecDecorator.class.getName());
private static final String DEFAULT_SHELL="/bin/sh";

private transient KubernetesClient client;

Expand All @@ -93,6 +93,7 @@ public class ContainerExecDecorator extends LauncherDecorator implements Seriali
private EnvVars globalVars;
private FilePath ws;
private EnvVars rcEnvVars;
private String shell;

public ContainerExecDecorator() {
}
Expand All @@ -105,6 +106,7 @@ public ContainerExecDecorator(KubernetesClient client, String podName, String co
this.containerName = containerName;
this.environmentExpander = environmentExpander;
this.ws = ws;
this.shell = DEFAULT_SHELL;
}

@Deprecated
Expand Down Expand Up @@ -196,6 +198,14 @@ public void setWs(FilePath ws) {
this.ws = ws;
}

public String getShell() {
return shell == null? DEFAULT_SHELL:shell;
}

public void setShell(String shell) {
this.shell = shell;
}

@Override
public Launcher decorate(final Launcher launcher, final Node node) {
return new Launcher.DecoratedLauncher(launcher) {
Expand Down Expand Up @@ -309,7 +319,7 @@ public void onClose(int i, String s) {

ExecWatch watch;
try {
watch = execable.exec("/bin/sh");
watch = execable.exec(getShell());
} catch (KubernetesClientException e) {
if (e.getCause() instanceof InterruptedException) {
throw new IOException("JENKINS-40825: interrupted while starting websocket connection", e);
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/org/csanchez/jenkins/plugins/kubernetes/pipeline/ContainerStep.java
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.jenkinsci.plugins.workflow.steps.StepDescriptor;
import org.jenkinsci.plugins.workflow.steps.StepExecution;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.DataBoundSetter;

import com.google.common.collect.ImmutableSet;

Expand All @@ -21,6 +22,7 @@ public class ContainerStep extends Step implements Serializable {
private static final long serialVersionUID = 5588861066775717487L;

private final String name;
private String shell;

@DataBoundConstructor
public ContainerStep(String name) {
Expand All @@ -31,6 +33,15 @@ public String getName() {
return name;
}

@DataBoundSetter
public void setShell(String shell){
this.shell = shell;
}

public String getShell() {
return shell;
}

@Override
public StepExecution start(StepContext context) throws Exception {
return new ContainerStepExecution(this, context);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ public void onResume() {
public boolean start() throws Exception {
LOGGER.log(Level.FINE, "Starting container step.");
String containerName = step.getName();
String shell = step.getShell();

KubernetesNodeContext nodeContext = new KubernetesNodeContext(getContext());
client = nodeContext.connectToCloud();
Expand Down Expand Up @@ -94,6 +95,7 @@ public boolean start() throws Exception {
decorator.setWs(getContext().get(FilePath.class));
decorator.setGlobalVars(globalVars);
decorator.setRunContextEnvVars(rcEnvVars);
decorator.setShell(shell);
getContext().newBodyInvoker()
.withContext(BodyInvoker
.mergeLauncherDecorators(getContext().get(LauncherDecorator.class), decorator))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public class KubernetesDeclarativeAgentScript extends DeclarativeAgentScript<Kub
script.checkout script.scm
}
}
script.container(describable.containerTemplate.name) {
script.container(describable.containerTemplate.asArgs) {
body.call()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import org.junit.rules.TemporaryFolder;
import org.jvnet.hudson.test.JenkinsRuleNonLocalhost;

import hudson.model.Result;
import io.fabric8.kubernetes.api.model.NamespaceBuilder;
import io.fabric8.kubernetes.client.KubernetesClient;

Expand Down Expand Up @@ -83,6 +84,16 @@ private boolean hasPodTemplateWithLabel(String label, List<PodTemplate> template
.anyMatch(label::equals);
}

@Test
public void runInPodWithDifferentShell() throws Exception {
WorkflowJob p = r.jenkins.createProject(WorkflowJob.class, "p");
p.setDefinition(new CpsFlowDefinition(loadPipelineScript("runInPodWithDifferentShell.groovy"), true));
WorkflowRun b = p.scheduleBuild2(0).waitForStart();
assertNotNull(b);
r.assertBuildStatus(Result.FAILURE,r.waitForCompletion(b));
r.assertLogContains("/bin/bash: no such file or directory", b);
}

@Test
public void runInPodWithMultipleContainers() throws Exception {
WorkflowJob p = r.jenkins.createProject(WorkflowJob.class, "p");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,12 @@ pipeline {
}
}
}
stage('Run maven with a different shell') {
steps {
container(name: 'maven', shell: '/bin/bash') {
sh 'mvn -version'
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
podTemplate(label: 'mypod', containers: [
containerTemplate(name: 'busybox', image: 'busybox', ttyEnabled: true, command: '/bin/cat'),
]) {

node ('mypod') {
stage('Run') {
container(name:'busybox', shell: '/bin/bash') {
sh """
echo "Run BusyBox shell"
"""
}
}
}
}