Skip to content

Commit

Permalink
add the possibility to pass env variables to chain lifecycle scripts
Browse files Browse the repository at this point in the history
  • Loading branch information
derklaro committed Mar 3, 2023
1 parent 84ef9b7 commit cdd3f22
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Locale;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import org.jetbrains.annotations.NotNull;
Expand All @@ -18,6 +20,8 @@
@Singleton
public final class ScriptExecutionHandler {

public static final String LOG_DIR_NAME = ".scriptlog";

private static final Logger LOGGER = LoggerFactory.getLogger(ScriptExecutionHandler.class);

private static final Random RANDOM = new Random();
Expand All @@ -27,6 +31,7 @@ public void runScriptIfExists(
@NotNull Path directory,
@NotNull String scriptName,
@NotNull String scriptLogId,
@NotNull Map<String, String> env,
@Nullable TaskExecutionContext<?, ?> context,
@Nullable Object successfulScriptReturnValue
) throws IOException {
Expand All @@ -38,15 +43,20 @@ public void runScriptIfExists(
}

// create a temporary file that catches the log output of the script process
var logFilePath = directory.resolve(".scriptlog").resolve("%s.tmp".formatted(RANDOM.nextLong()));
var logFilePath = directory.resolve(LOG_DIR_NAME).resolve("%s.tmp".formatted(RANDOM.nextLong()));
this.createLogFile(logFilePath);

// start the script process
var process = new ProcessBuilder("bash", scriptPathName)
// build the process
var processBuilder = new ProcessBuilder("bash", scriptPathName)
.directory(directory.toFile())
.redirectErrorStream(true)
.redirectOutput(logFilePath.toFile())
.start();
.redirectOutput(logFilePath.toFile());
for (var envVarEntry : env.entrySet()) {
processBuilder.environment().put(envVarEntry.getKey().toUpperCase(Locale.ROOT), envVarEntry.getValue());
}

// start the script process
var process = processBuilder.start();

// if a context is given, ensure that we destroy the process in case the execution fails
var processHandle = process.toHandle();
Expand All @@ -57,6 +67,11 @@ public void runScriptIfExists(
}
});

// attach the log file information to the context
context.registerAdditionalInformation(
"easydep_%s_log".formatted(scriptName.replace('.', '_')),
logFilePath.toAbsolutePath().toString());

// configure the future based on the context wait method
context.waitForFutureCompletion(
processHandle.onExit(),
Expand All @@ -80,11 +95,7 @@ public void runScriptIfExists(
// print out the process log file lines to the target logger
var logLines = Files.readAllLines(logPath, StandardCharsets.UTF_8);
logLines.forEach(line -> LOGGER.info("[{}]: {}", scriptLogId, line));

// remove the log file
Files.deleteIfExists(logPath);
} catch (IOException exception) {
LOGGER.error("Unable to read log lines from file: {}", logPath.toAbsolutePath(), exception);
} catch (IOException ignored) {
}
})
.thenApply(handle -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
import io.easybill.easydeploy.task.TaskTreeLifecycle;
import io.easybill.easydeploy.task.event.TaskTreeLifecycleEvent;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import org.apache.commons.io.file.PathUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.jetbrains.annotations.NotNull;
import org.kohsuke.github.GHRelease;
Expand Down Expand Up @@ -37,19 +39,35 @@ public ChainEventScriptExecuteTask(@NotNull ScriptExecutionHandler scriptExecuti
if (lifecycle == TaskTreeLifecycle.TASK_FAILURE || lifecycle == TaskTreeLifecycle.TASK_SUCCESS) {
// include a normalized version of the task name (all lower case, spaces replaced with underscore)
var normalizedTaskName = lifecycleEvent.lastTask().displayName().toLowerCase().replace(' ', '_');
this.runScript(input.getRight(), "%s.%s".formatted(normalizedLifecycleName, normalizedTaskName));
this.runScript(context, input.getRight(), "%s.%s".formatted(normalizedLifecycleName, normalizedTaskName));
} else {
// no need to append the task name, just use the lifecycle name
this.runScript(input.getRight(), "%s".formatted(normalizedLifecycleName));
this.runScript(context, input.getRight(), "%s".formatted(normalizedLifecycleName));

// as the tree finished & all scripts ran we can now remove the script log directory
var scriptLogDirectory = input.getRight().resolve(ScriptExecutionHandler.LOG_DIR_NAME);
if (Files.exists(scriptLogDirectory)) {
PathUtils.deleteDirectory(scriptLogDirectory);
}
}
}, /* very low priority to get called first */ 0);

return input;
}

private void runScript(@NotNull Path directory, @NotNull String scriptName) throws IOException {
private void runScript(
@NotNull TaskExecutionContext<?, ?> context,
@NotNull Path directory,
@NotNull String scriptName
) throws IOException {
// append the script suffix and pass on the execution to the handler
var finalScriptName = "%s.sh".formatted(scriptName);
this.scriptExecutionHandler.runScriptIfExists(directory, finalScriptName, "Lifecycle Event", null, null);
this.scriptExecutionHandler.runScriptIfExists(
directory,
finalScriptName,
"Lifecycle Event",
context.additionalTaskInformation(),
null,
null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import io.easybill.easydeploy.task.ChainedTask;
import io.easybill.easydeploy.task.TaskExecutionContext;
import java.nio.file.Path;
import java.util.Map;
import org.apache.commons.lang3.tuple.Pair;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
Expand Down Expand Up @@ -33,6 +34,7 @@ public DeployScriptExecuteTask(@NotNull ScriptExecutionHandler scriptExecutionHa
input.getRight(),
DEPLOY_SCRIPT_NAME,
DEPLOY_LOG_FORMAT.formatted(input.getLeft().getId()),
Map.of(),
context,
input);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import io.easybill.easydeploy.task.event.TaskTreeLifecycleEvent;
import io.easybill.easydeploy.task.event.TaskTreeTaskFailureEvent;
import io.easybill.easydeploy.task.event.TaskTreeTaskFinishedEvent;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
Expand Down Expand Up @@ -38,6 +40,9 @@ public final class TaskExecutionContext<I, O> {
// our future that will be completed with the result of the task execution
private final CompletableFuture<O> ourFuture = new CompletableFuture<>();

// additional step information that can be optionally passed in by the task
private final Map<String, String> additionalTaskInformation = new HashMap<>(16);

// the current execution state
private ChainedTask<Object> currentTask;
private CompletableFuture<?> waitingFuture;
Expand Down Expand Up @@ -119,6 +124,14 @@ public void cancel() {
}
}

public void registerAdditionalInformation(@NotNull String key, @Nullable String value) {
this.additionalTaskInformation.put(key, value);
}

public @NotNull Map<String, String> additionalTaskInformation() {
return this.additionalTaskInformation;
}

private boolean inState(int expectedState) {
return this.state.get() == expectedState;
}
Expand Down Expand Up @@ -167,8 +180,9 @@ private void resumeExecutionAt(@Nullable ChainedTask<Object> nextTask, @Nullable
return;
}

// notify the event pipeline that the previous task executed successfully
// notify the event pipeline that the previous task executed successfully & remove added task information
this.eventPipeline.post(new TaskTreeTaskFinishedEvent(currentTask, previousTaskOutput));
this.additionalTaskInformation.clear();

// set the current task we're executing
this.currentTask = nextTask;
Expand Down

0 comments on commit cdd3f22

Please sign in to comment.