Skip to content

Commit

Permalink
openshift log output on exception
Browse files Browse the repository at this point in the history
  • Loading branch information
laDok8 committed Jul 7, 2022
1 parent 2a172f2 commit e43ece0
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 5 deletions.
1 change: 1 addition & 0 deletions junit5/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ Extensions enable better test management.

#### @OpenShiftRecorder
Record OpenShift state when a test throws an exception or use `xtf.record.always` to record on success. Specify app names (which will be turned into regexes) to filter resources by name. When not specified, everything in test and build namespace will be recorded (regex - `.*`). Use `xtf.record.dir` to set the directory.
Use `xtf.record.print_on_exception` to append logs to error messages.

#### @CleanBeforeAll/@CleanBeforeEach
Cleans namespace specified by `xtf.openshift.namespace` property. Either before all tests or each test execution.
Expand Down
6 changes: 6 additions & 0 deletions junit5/src/main/java/cz/xtf/junit5/config/JUnitConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public class JUnitConfig {
private static final String RECORD_DIR = "xtf.record.dir";
private static final String RECORD_ALWAYS = "xtf.record.always";
private static final String RECORD_BEFORE = "xtf.record.before";
private static final String PRINT_EXCEPTION = "xtf.record.print_on_exception";

public static String recordDir() {
return XTFConfig.get(RECORD_DIR);
Expand Down Expand Up @@ -59,4 +60,9 @@ public static String jenkinsRerun() {
public static boolean prebuilderSynchronized() {
return Boolean.parseBoolean(XTFConfig.get(PREBUILDER_SYNCHRONIZED, "false"));
}

public static boolean printWithException() {
return XTFConfig.get(PRINT_EXCEPTION) != null
&& (XTFConfig.get(PRINT_EXCEPTION).equals("") || XTFConfig.get(PRINT_EXCEPTION).equals("true"));
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package cz.xtf.junit5.extensions;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Constructor;
import java.nio.charset.StandardCharsets;

import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.BeforeEachCallback;
Expand Down Expand Up @@ -59,12 +63,25 @@ public void beforeEach(ExtensionContext context) {

@Override
public void handleTestExecutionException(ExtensionContext context, Throwable throwable) throws Throwable {
Throwable newThrowable = throwable;
final ByteArrayOutputStream messageStream = new ByteArrayOutputStream();
try {
openShiftRecorderService.recordState(context);

try (PrintStream ps = new PrintStream(messageStream, true, StandardCharsets.UTF_8.name())) {
openShiftRecorderService.printPodLogs(context, ps);
openShiftRecorderService.printEvents(context, ps);
openShiftRecorderService.printBuildLogs(context, ps);
}
if (messageStream.size() != 0) {
Constructor<? extends Throwable> constructor = throwable.getClass().getConstructor(String.class,
Throwable.class);
newThrowable = constructor.newInstance(throwable.getMessage() + messageStream, throwable.getCause());
}
} catch (Throwable t) {
log.error("Throwable: ", t);
} finally {
throw throwable;
throw newThrowable;
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
package cz.xtf.junit5.extensions;

import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiConsumer;

Expand Down Expand Up @@ -475,6 +480,43 @@ protected void savePodLogs(ExtensionContext context, ResourcesFilterBuilder<Pod>
}
}

/**
* openshift log may be multiline, thus messages are separated with ESCAPE char
*/
private static final String delimiter = "\u001B\\[.*?m";

protected void printPodLogs(ExtensionContext context, PrintStream ps) {
if (!JUnitConfig.printWithException()) {
return;
}
ResourcesFilterBuilder<Pod> masterFilter = getFilter(context, POD_FILTER_MASTER);
ResourcesFilterBuilder<Pod> buildsFilter = !isMasterAndBuildNamespaceSame() ? getFilter(context, POD_FILTER_BUILDS)
: null;

ps.println("\nAvailable pod logs:");
BiConsumer<OpenShift, ResourcesFilterBuilder<Pod>> podPrinter = (openShift, filter) -> openShift.getPods()
.stream()
.filter(filter.build())
.filter(
// filter un-initialized pods out: those pods do not provide any log
pod -> pod.getStatus().getInitContainerStatuses().stream().allMatch(
containerStatus -> containerStatus.getState().getTerminated() != null
&&
"Completed".equalsIgnoreCase(
containerStatus.getState().getTerminated().getReason())))
.forEach(pod -> {
String message = openShift.getPodLog(pod);
ps.println("POD " + pod.getMetadata().getName() + ":");
List<String> podLogCols = new ArrayList<>(Arrays.asList(message.split(delimiter)));
podLogCols.stream().forEach(ps::print);
});

podPrinter.accept(OpenShifts.master(), masterFilter);
if (!isMasterAndBuildNamespaceSame()) {
podPrinter.accept(BuildManagers.get().openShift(), buildsFilter);
}
}

protected void saveEvents(ExtensionContext context, ResourcesFilterBuilder<Event> masterFilter,
ResourcesFilterBuilder<Event> buildsFilter) throws IOException {
// master namespace
Expand All @@ -499,6 +541,32 @@ protected void saveEvents(ExtensionContext context, ResourcesFilterBuilder<Event
}
}

protected void printEvents(ExtensionContext context, PrintStream ps) throws IOException {
if (!JUnitConfig.printWithException()) {
return;
}
ResourcesFilterBuilder<Event> masterFilter = getFilter(context, EVENT_FILTER_MASTER);
ResourcesFilterBuilder<Event> buildsFilter = !isMasterAndBuildNamespaceSame() ? getFilter(context, EVENT_FILTER_BUILDS)
: null;

ps.println("\nAvailable Openshift events:");
try (final ResourcesPrinterHelper<Event> printer = ResourcesPrinterHelper
.forEvents(new OutputStreamWriter(ps))) {
// master namespace
OpenShifts.master().getEvents()
.stream()
.filter(masterFilter.build())
.forEach(printer::row);
// builds namespace (if not same)
if (!isMasterAndBuildNamespaceSame()) {
BuildManagers.get().openShift().getEvents()
.stream()
.filter(buildsFilter.build())
.forEach(printer::row);
}
}
}

protected void saveBuildLogs(ExtensionContext context, ResourcesFilterBuilder<Build> masterFilter,
ResourcesFilterBuilder<Build> buildsFilter) {
BiConsumer<OpenShift, ResourcesFilterBuilder<Build>> buildPrinter = (openShift, filter) -> openShift.getBuilds()
Expand All @@ -521,6 +589,28 @@ protected void saveBuildLogs(ExtensionContext context, ResourcesFilterBuilder<Bu
}
}

protected void printBuildLogs(ExtensionContext context, PrintStream ps) {
if (!JUnitConfig.printWithException()) {
return;
}

ResourcesFilterBuilder<Build> masterFilter = getFilter(context, BUILD_FILTER_MASTER);
ResourcesFilterBuilder<Build> buildsFilter = !isMasterAndBuildNamespaceSame() ? getFilter(context, BUILD_FILTER_MASTER)
: null;

ps.println("\nAvailable pod build logs:");
BiConsumer<OpenShift, ResourcesFilterBuilder<Build>> buildPrinter = (openShift, filter) -> openShift.getBuilds()
.stream()
.filter(filter.build())
.forEach(build -> ps
.println("POD: " + build.getMetadata().getName() + ":\n" + openShift.getBuildLog(build)));

buildPrinter.accept(OpenShifts.master(), masterFilter);
if (!isMasterAndBuildNamespaceSame()) {
buildPrinter.accept(BuildManagers.get().openShift(), buildsFilter);
}
}

private String attachmentsDir() {
return JUnitConfig.recordDir() != null ? JUnitConfig.recordDir() : System.getProperty("user.dir");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package cz.xtf.junit5.extensions.helpers;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.LinkedHashMap;
Expand All @@ -25,9 +25,10 @@
import io.fabric8.openshift.api.model.Route;

public class ResourcesPrinterHelper<X> implements AutoCloseable {
private final Path file;
private Path file = null;
private final Function<X, LinkedHashMap<String, String>> resourceToCols;
private final List<String[]> rows;
private OutputStreamWriter outputStream = null;
private int[] maxLengths;
private String[] headers = null;

Expand All @@ -37,10 +38,20 @@ private ResourcesPrinterHelper(Path file, Function<X, LinkedHashMap<String, Stri
rows = new ArrayList<>();
}

private ResourcesPrinterHelper(OutputStreamWriter outputStream, Function<X, LinkedHashMap<String, String>> resourceToCols) {
this.outputStream = outputStream;
this.resourceToCols = resourceToCols;
rows = new ArrayList<>();
}

public static ResourcesPrinterHelper<Event> forEvents(Path filePath) {
return new ResourcesPrinterHelper<>(filePath, ResourcesPrinterHelper::getEventCols);
}

public static ResourcesPrinterHelper<Event> forEvents(OutputStreamWriter outputStream) {
return new ResourcesPrinterHelper<>(outputStream, ResourcesPrinterHelper::getEventCols);
}

public static ResourcesPrinterHelper<Pod> forPods(Path filePath) {
return new ResourcesPrinterHelper<>(filePath, ResourcesPrinterHelper::getPodCols);
}
Expand Down Expand Up @@ -222,9 +233,14 @@ public void close() throws IOException {
}

public void flush() throws IOException {
file.getParent().toFile().mkdirs();
//mutually exclusive options file OR outputStream
OutputStreamWriter streamWriter = outputStream;
if (file != null) {
file.getParent().toFile().mkdirs();
streamWriter = new OutputStreamWriter(Files.newOutputStream(file.toFile().toPath()), StandardCharsets.UTF_8);
}

try (final Writer writer = new OutputStreamWriter(new FileOutputStream(file.toFile()), StandardCharsets.UTF_8)) {
try (final Writer writer = streamWriter) {
if (!rows.isEmpty()) {
StringBuilder formatBuilder = new StringBuilder();
for (int maxLength : maxLengths) {
Expand Down

0 comments on commit e43ece0

Please sign in to comment.