Skip to content

Commit

Permalink
Merge pull request #287 from lyra/feature/openshift_bash
Browse files Browse the repository at this point in the history
Allow specifying a different shell command other than /bin/sh
  • Loading branch information
carlossg authored Mar 6, 2018
2 parents ff824e5 + 06e3fe6 commit c156507
Show file tree
Hide file tree
Showing 9 changed files with 98 additions and 3 deletions.
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"
"""
}
}
}
}

0 comments on commit c156507

Please sign in to comment.