Skip to content
This repository has been archived by the owner on Sep 18, 2023. It is now read-only.

Commit

Permalink
Print pod logs for failed scenarios.
Browse files Browse the repository at this point in the history
  • Loading branch information
Sgitario committed Nov 3, 2020
1 parent 0aca538 commit 656a348
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 18 deletions.
50 changes: 42 additions & 8 deletions common/src/main/java/io/quarkus/ts/openshift/common/Command.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,23 @@

import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.List;
import java.util.function.BiConsumer;

import static org.fusesource.jansi.Ansi.ansi;

public class Command {
private final String description;
private final List<String> command;

private BiConsumer<String, InputStream> outputConsumer = consoleOutput();

public Command(String... command) {
this(Arrays.asList(command));
}
Expand All @@ -22,6 +28,16 @@ public Command(List<String> command) {
this.command = command;
}

public Command outputToFile(File file) {
outputConsumer = fileOutput(file);
return this;
}

public Command outputToConsole() {
outputConsumer = consoleOutput();
return this;
}

private static String descriptionOfProgram(String program) {
if (program.contains(File.separator)) {
return program.substring(program.lastIndexOf(File.separator) + 1);
Expand All @@ -31,26 +47,44 @@ private static String descriptionOfProgram(String program) {

public void runAndWait() throws IOException, InterruptedException {
System.out.println(ansi().a("running ").fgYellow().a(String.join(" ", command)).reset());

Process process = new ProcessBuilder()
.redirectErrorStream(true)
.command(command)
.directory(new File(".").getAbsoluteFile())
.start();

new Thread(() -> {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(ansi().fgCyan().a(description).reset().a("> ").a(line));
}
} catch (IOException ignored) {
}
outputConsumer.accept(description, process.getInputStream());
}, "stdout consumer for command " + description).start();

int result = process.waitFor();
if (result != 0) {
throw new RuntimeException(description + " failed (executed " + command + ", return code " + result + ")");
}
}

private static final BiConsumer<String, InputStream> fileOutput(File targetFile) {
return (description, is) -> {
try (OutputStream outStream = new FileOutputStream(targetFile)) {
byte[] buffer = new byte[8 * 1024];
int bytesRead;
while ((bytesRead = is.read(buffer)) != -1) {
outStream.write(buffer, 0, bytesRead);
}
} catch (IOException ignored) {
}
};
}

private static final BiConsumer<String, InputStream> consoleOutput() {
return (description, is) -> {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(is))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(ansi().fgCyan().a(description).reset().a("> ").a(line));
}
} catch (IOException ignored) {
}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import io.fabric8.openshift.api.model.Route;
import io.fabric8.openshift.client.OpenShiftClient;
import io.quarkus.ts.openshift.app.metadata.AppMetadata;
import io.quarkus.ts.openshift.common.actions.OnOpenShiftFailureAction;
import io.quarkus.ts.openshift.common.config.Config;
import io.quarkus.ts.openshift.common.injection.InjectionPoint;
import io.quarkus.ts.openshift.common.injection.TestResource;
Expand Down Expand Up @@ -38,16 +39,20 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Optional;
import java.util.ServiceLoader;
import java.util.stream.Stream;

import static org.fusesource.jansi.Ansi.ansi;
import static org.junit.platform.commons.util.AnnotationUtils.findAnnotatedFields;

// TODO at this point, this class is close to becoming unreadable, and could use some refactoring
// TODO at this point, this class is close to becoming unreadable, and could use some refactoring.
// Raised https://github.com/quarkus-qe/quarkus-openshift-test-suite/issues/108 for the refactoring.
final class OpenShiftTestExtension implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback,
TestInstancePostProcessor, ParameterResolver,
LifecycleMethodExecutionExceptionHandler, TestExecutionExceptionHandler {

private final ServiceLoader<OnOpenShiftFailureAction> onFailureActions = ServiceLoader.load(OnOpenShiftFailureAction.class);

private Store getStore(ExtensionContext context) {
return context.getStore(Namespace.create(getClass()));
}
Expand Down Expand Up @@ -249,7 +254,8 @@ public void afterAll(ExtensionContext context) throws Exception {
System.out.println("---------- OpenShiftTest failure ----------");
System.out.println(ansi().a("test ").fgYellow().a(context.getDisplayName()).reset()
.a(" failed, showing current namespace status"));
new Command("oc", "status", "--suggest").runAndWait();

onFailureActions.forEach(action -> this.runOnFailureAction(context, action));
}

System.out.println("---------- OpenShiftTest tear down ----------");
Expand Down Expand Up @@ -344,13 +350,7 @@ public void beforeEach(ExtensionContext context) {

@Override
public void postProcessTestInstance(Object testInstance, ExtensionContext context) throws Exception {
for (Field field : findAnnotatedFields(testInstance.getClass(), TestResource.class, ignored -> true)) {
InjectionPoint injectionPoint = InjectionPoint.forField(field);
if (!field.isAccessible()) {
field.setAccessible(true);
}
field.set(testInstance, valueFor(injectionPoint, context));
}
injectDependencies(testInstance, context);
}

@Override
Expand Down Expand Up @@ -450,6 +450,25 @@ public void handleTestExecutionException(ExtensionContext context, Throwable thr
throw throwable;
}

private void runOnFailureAction(ExtensionContext context, OnOpenShiftFailureAction action) {
try {
injectDependencies(action, context);
action.execute();
} catch (Exception ex) {
System.out.println(ansi().a("Error running post failure action. Caused by: " + ex).reset());
}
}

private void injectDependencies(Object instance, ExtensionContext context) throws Exception {
for (Field field : findAnnotatedFields(instance.getClass(), TestResource.class, ignored -> true)) {
InjectionPoint injectionPoint = InjectionPoint.forField(field);
if (!field.isAccessible()) {
field.setAccessible(true);
}
field.set(instance, valueFor(injectionPoint, context));
}
}

private void failureOccured(ExtensionContext context) {
getTestsStatus(context).failed = true;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package io.quarkus.ts.openshift.common.actions;

import io.fabric8.kubernetes.api.model.Pod;
import io.quarkus.ts.openshift.common.Command;
import io.quarkus.ts.openshift.common.config.Config;
import io.quarkus.ts.openshift.common.injection.TestResource;
import io.quarkus.ts.openshift.common.util.OpenShiftUtil;

import java.io.File;

public class CopyLogsOnOpenShiftFailureActionImpl implements OnOpenShiftFailureAction {

private static final String INSTANCES_LOGS_OUTPUT_DIRECTORY = "instance-logs";
private static final String DEFAULT_LOG_OUTPUT_DIRECTORY = "target/logs";
private static final String LOG_SUFFIX = ".log";

@TestResource
private OpenShiftUtil openShiftUtil;

@TestResource
private Config config;

@Override
public void execute() throws Exception {
String namespace = openShiftUtil.getNamespace();
for (Pod pod : openShiftUtil.getPods()) {
String podName = pod.getMetadata().getName();
new Command("oc", "logs", podName).outputToFile(getOutputFile(podName, namespace)).runAndWait();
}
}

private String getOutputFolder() {
return config.getAsString(INSTANCES_LOGS_OUTPUT_DIRECTORY, DEFAULT_LOG_OUTPUT_DIRECTORY);
}

private File getOutputFile(String name, String customLogFolderName) {
File outputDirectory = new File(getOutputFolder(), customLogFolderName);
outputDirectory.mkdirs();
return new File(outputDirectory, name + LOG_SUFFIX);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package io.quarkus.ts.openshift.common.actions;

/**
* Interface to perform an action after an OpenShift failure.
*/
public interface OnOpenShiftFailureAction {

/**
* Action to perform
*
* @throws Exception to be logged
*/
void execute() throws Exception;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package io.quarkus.ts.openshift.common.actions;

import io.quarkus.ts.openshift.common.Command;

public class PrintStatusOnOpenShiftFailureActionImpl implements OnOpenShiftFailureAction {

@Override
public void execute() throws Exception {
new Command("oc", "status", "--suggest").runAndWait();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,15 @@ public OpenShiftUtil(OpenShiftClient oc, AwaitUtil await) {
this.await = await;
}

private List<Pod> listPodsForDeploymentConfig(String deploymentConfigName) {
public String getNamespace() {
return oc.getNamespace();
}

public List<Pod> getPods() {
return oc.pods().list().getItems();
}

public List<Pod> listPodsForDeploymentConfig(String deploymentConfigName) {
return oc.pods()
.inNamespace(oc.getNamespace())
.withLabel("deploymentconfig", deploymentConfigName)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
io.quarkus.ts.openshift.common.actions.PrintStatusOnOpenShiftFailureActionImpl
io.quarkus.ts.openshift.common.actions.CopyLogsOnOpenShiftFailureActionImpl

0 comments on commit 656a348

Please sign in to comment.