-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #195 from marvinthepa/feature/JENKINS-46085-contai…
…ner-log-pipeline-step JENKINS-46085 containerLog step to get the logs of a container running in the slave pod
- Loading branch information
Showing
15 changed files
with
593 additions
and
167 deletions.
There are no files selected for viewing
121 changes: 121 additions & 0 deletions
121
src/main/java/org/csanchez/jenkins/plugins/kubernetes/pipeline/ContainerLogStep.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
/* | ||
* Copyright (C) 2017 Original Authors | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package org.csanchez.jenkins.plugins.kubernetes.pipeline; | ||
|
||
import com.google.common.collect.ImmutableSet; | ||
import hudson.Extension; | ||
import hudson.FilePath; | ||
import hudson.model.Node; | ||
import hudson.model.TaskListener; | ||
import org.jenkinsci.plugins.workflow.steps.Step; | ||
import org.jenkinsci.plugins.workflow.steps.StepContext; | ||
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 java.io.Serializable; | ||
import java.util.Set; | ||
|
||
public class ContainerLogStep extends Step implements Serializable { | ||
private static final long serialVersionUID = 5588861066775717487L; | ||
|
||
private final String name; | ||
private boolean returnLog = false; | ||
private int tailingLines = 0; | ||
private int sinceSeconds = 0; | ||
private int limitBytes = 0; | ||
|
||
@DataBoundConstructor | ||
public ContainerLogStep(String name) { | ||
this.name = name; | ||
} | ||
|
||
public String getName() { | ||
return name; | ||
} | ||
|
||
@DataBoundSetter | ||
public void setReturnLog(boolean returnLog) { | ||
this.returnLog = returnLog; | ||
} | ||
|
||
public boolean isReturnLog() { | ||
return returnLog; | ||
} | ||
|
||
@Override | ||
public StepExecution start(StepContext context) throws Exception { | ||
return new ContainerLogStepExecution(this, context); | ||
} | ||
|
||
public int getTailingLines() { | ||
return tailingLines; | ||
} | ||
|
||
@DataBoundSetter | ||
public void setTailingLines(int tailingLines) { | ||
this.tailingLines = tailingLines; | ||
} | ||
|
||
public int getSinceSeconds() { | ||
return sinceSeconds; | ||
} | ||
|
||
@DataBoundSetter | ||
public void setSinceSeconds(int sinceSeconds) { | ||
this.sinceSeconds = sinceSeconds; | ||
} | ||
|
||
public int getLimitBytes() { | ||
return limitBytes; | ||
} | ||
|
||
@DataBoundSetter | ||
public void setLimitBytes(int limitBytes) { | ||
this.limitBytes = limitBytes; | ||
} | ||
|
||
@Extension | ||
public static class DescriptorImpl extends StepDescriptor { | ||
|
||
@Override | ||
public String getFunctionName() { | ||
return "containerLog"; | ||
} | ||
|
||
@Override | ||
public String getDisplayName() { | ||
return "Get container log from Kubernetes"; | ||
} | ||
|
||
@Override | ||
public boolean takesImplicitBlockArgument() { | ||
return false; | ||
} | ||
|
||
@Override | ||
public boolean isAdvanced() { | ||
return false; | ||
} | ||
|
||
@Override | ||
public Set<? extends Class<?>> getRequiredContext() { | ||
return ImmutableSet.of(Node.class, FilePath.class, TaskListener.class); | ||
} | ||
} | ||
} |
122 changes: 122 additions & 0 deletions
122
...main/java/org/csanchez/jenkins/plugins/kubernetes/pipeline/ContainerLogStepExecution.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
/* | ||
* Copyright (C) 2017 Original Authors | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package org.csanchez.jenkins.plugins.kubernetes.pipeline; | ||
|
||
import hudson.model.TaskListener; | ||
import hudson.util.LogTaskListener; | ||
import io.fabric8.kubernetes.client.KubernetesClient; | ||
import io.fabric8.kubernetes.client.dsl.*; | ||
import org.jenkinsci.plugins.workflow.steps.StepContext; | ||
import org.jenkinsci.plugins.workflow.steps.SynchronousNonBlockingStepExecution; | ||
|
||
import java.io.*; | ||
import java.util.logging.Level; | ||
import java.util.logging.Logger; | ||
|
||
import static org.csanchez.jenkins.plugins.kubernetes.pipeline.Resources.closeQuietly; | ||
|
||
public class ContainerLogStepExecution extends SynchronousNonBlockingStepExecution<String> { | ||
private static final long serialVersionUID = 5588861066775717487L; | ||
private static final transient Logger LOGGER = Logger.getLogger(ContainerLogStepExecution.class.getName()); | ||
|
||
private final ContainerLogStep step; | ||
private transient KubernetesClient client; | ||
|
||
ContainerLogStepExecution(ContainerLogStep step, StepContext context) { | ||
super(context); | ||
this.step = step; | ||
} | ||
|
||
private PrintStream logger() { | ||
TaskListener l = null; | ||
StepContext context = getContext(); | ||
try { | ||
l = context.get(TaskListener.class); | ||
} catch (Exception x) { | ||
LOGGER.log(Level.WARNING, "Failed to find TaskListener in context"); | ||
} finally { | ||
if (l == null) { | ||
l = new LogTaskListener(LOGGER, Level.FINE); | ||
} | ||
} | ||
return l.getLogger(); | ||
} | ||
|
||
@Override | ||
protected String run() throws Exception { | ||
boolean returnLog = step.isReturnLog(); | ||
String containerName = step.getName(); | ||
int tailingLines = step.getTailingLines(); | ||
int sinceSeconds = step.getSinceSeconds(); | ||
int limitBytes = step.getLimitBytes(); | ||
|
||
try { | ||
LOGGER.log(Level.FINE, "Starting containerLog step."); | ||
|
||
KubernetesNodeContext nodeContext = new KubernetesNodeContext(getContext()); | ||
client = nodeContext.connectToCloud(); | ||
|
||
String podName = nodeContext.getPodName(); | ||
ContainerResource<String, LogWatch, InputStream, PipedOutputStream, OutputStream, PipedInputStream, | ||
String, ExecWatch> container = client.pods() | ||
.inNamespace(nodeContext.getNamespace()) | ||
.withName(podName) | ||
.inContainer(containerName); | ||
|
||
TimeTailPrettyLoggable<String, LogWatch> limited = limitBytes > 0 ? container.limitBytes(limitBytes) : container; | ||
|
||
TailPrettyLoggable<String, LogWatch> since = sinceSeconds > 0 ? limited.sinceSeconds(sinceSeconds) : limited; | ||
|
||
PrettyLoggable<String, LogWatch> tailed = tailingLines > 0 ? since.tailingLines(tailingLines) : since; | ||
String log = tailed.getLog(); | ||
|
||
if (returnLog) { | ||
return log; | ||
} else { | ||
logger().println("> start log of container '" + containerName + "' in pod '" + podName + "'"); | ||
logger().print(log); | ||
if (log.length() > 0 && log.charAt(log.length() - 1) != '\n') { | ||
logger().println(); | ||
} | ||
logger().println("> end log of container '" + containerName + "' in pod '" + podName + "'"); | ||
} | ||
|
||
return ""; | ||
} catch (InterruptedException e) { | ||
logger().println("Interrupted while getting logs of container"); | ||
LOGGER.log(Level.FINE, "interrupted while getting logs of container {1}", containerName); | ||
return ""; | ||
} catch (Exception e) { | ||
String message = "Failed to get logs for container"; | ||
logger().println(message); | ||
LOGGER.log(Level.WARNING, message, e); | ||
return ""; | ||
} finally { | ||
closeQuietly(getContext(), client); | ||
} | ||
} | ||
|
||
@Override | ||
public void stop(Throwable cause) throws Exception { | ||
LOGGER.log(Level.FINE, "Stopping container log step."); | ||
try { | ||
super.stop(cause); | ||
} finally { | ||
closeQuietly(getContext(), client); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.