From d4dda68b09e33c60abd0654f81d9017f7399d549 Mon Sep 17 00:00:00 2001 From: Peter Paul Bakker Date: Tue, 6 Feb 2024 20:51:58 +0100 Subject: [PATCH] version 3.0.2-SNAPSHOT, added onScheduledEvent --- README.md | 14 ++++ pom.xml | 2 +- .../commandrunner/CommandRunnerEvent.java | 79 ++++++++++++++++++- .../CommandRunnerEventConfig.java | 16 +++- .../CommandRunnerEventContext.java | 9 ++- 5 files changed, 110 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index e16d841..8fd69b1 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,12 @@ remote running load test process. Use `onAfterTest` for a command that is called on an `after-test` event. For instance, clean up artifacts from a another command. +Use `onScheduledEvent` for a command that is called in an event scheduler script. +Use `runcommand` event. Use `name` to match one specific command runner event to trigger. +Use key=value parameters with `;` separated key=value pairs. +In the command surround the keys underscores to be replaced, like `__key__`. +Use `__testRunId__` to be replaced by the test run id from the current test context. + Only when _all_ polling commands that are indicated as `continueOnKeepAliveParticipant` request a stop, the test-run is actually stopped. @@ -62,6 +68,10 @@ commands end, e.g. by running `rm -v /tmp/test-run-*.busy`. ${CIBuildResultsUrl} ${rampupTimeInSeconds} ${constantLoadTimeInSeconds} + + PT30S|run-command(scale to 3)|name=k8sCommand;app=myapp;namespace=mynamespace;replicas=3 + PT1M|run-command(scale to 1)|name=k8sCommand;app=myapp;namespace=mynamespace;replicas=1 + ${annotations} ${tags} @@ -86,6 +96,10 @@ commands end, e.g. by running `rm -v /tmp/test-run-*.busy`. echo abort K6 runner 2 rm /tmp/test-run-2.busy; echo end ${testRunId} + + k8sCommand + kubectl -n __namespace__ scale --replicas=__replicas__ --timeout=1m deployment __app__ + diff --git a/pom.xml b/pom.xml index dc0a814..a51c13a 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ io.perfana test-events-command-runner - 3.0.1 + 3.0.2-SNAPSHOT Run a command via the command process runner. Abort when needed. jar diff --git a/src/main/java/io/perfana/events/commandrunner/CommandRunnerEvent.java b/src/main/java/io/perfana/events/commandrunner/CommandRunnerEvent.java index e213b09..170b917 100644 --- a/src/main/java/io/perfana/events/commandrunner/CommandRunnerEvent.java +++ b/src/main/java/io/perfana/events/commandrunner/CommandRunnerEvent.java @@ -15,6 +15,7 @@ */ package io.perfana.events.commandrunner; +import io.perfana.eventscheduler.api.CustomEvent; import io.perfana.eventscheduler.api.EventAdapter; import io.perfana.eventscheduler.api.EventLogger; import io.perfana.eventscheduler.api.config.TestContext; @@ -33,6 +34,10 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static io.perfana.events.commandrunner.CommandRunnerEvent.AllowedCustomEvents.runcommand; +import static io.perfana.events.commandrunner.CommandRunnerEvent.AllowedCustomEvents.stream; public class CommandRunnerEvent extends EventAdapter { @@ -40,6 +45,37 @@ public class CommandRunnerEvent extends EventAdapter private final boolean isWindows; + enum AllowedCustomEvents { + runcommand("run-command"); + + private final String eventName; + + AllowedCustomEvents(String eventName) { + this.eventName = eventName; + } + + public String getEventName() { + return eventName; + } + + public static Stream stream() { + return Stream.of(values()); + } + + public boolean hasEventName(String name) { + return this.eventName.equals(name); + } + } + + private final Set allowedCustomEvents = setOf(stream() + .map(AllowedCustomEvents::getEventName) + .toArray(String[]::new)); + + @Override + public Collection allowedCustomEvents() { + return allowedCustomEvents; + } + public CommandRunnerEvent(CommandRunnerEventContext eventContext, TestContext testContext, EventMessageBus messageBus, EventLogger logger) { super(eventContext, testContext, messageBus, logger); isWindows = systemGetPropertyNullSafe("os.name", logger).startsWith("Windows"); @@ -54,13 +90,38 @@ private static String systemGetPropertyNullSafe(String property, EventLogger log return prop == null ? "" : prop; } + @Override + public void customEvent(CustomEvent scheduleEvent) { + String eventName = scheduleEvent.getName(); + try { + if (runcommand.hasEventName(eventName)) { + Map parsedSettings = parseSettings(scheduleEvent.getSettings()); + + // if name is set, only run the command if the name matches + String name = parsedSettings.get("name"); + if (name != null && !eventContext.getName().equals(name)) { + logger.info("Ignoring event [" + eventName + "] for [" + name + "], this is [" + eventContext.getName() + "]"); + return; + } + + String command = eventContext.getOnScheduledEvent(); + command = parsedSettings.entrySet().stream() + .reduce(command, (k, v) -> k.replaceAll("__" + v.getKey() + "__", v.getValue()), String::concat); + + runCommand(command, "scheduledEvent"); + } else { + logger.warn("ignoring unknown event [" + eventName + "]"); + } + } catch (Exception e) { + logger.error("Failed to run custom event: " + eventName, e); + } + } + @Override public void beforeTest() { String pluginName = CommandRunnerEvent.class.getSimpleName() + "-" + eventContext.getName(); - String newTestRunId = testContext.getTestRunId(); - // default sending of command is disabled: might contain secrets if (eventContext.isSendTestRunConfig()) { String tags = "command-runner"; @@ -70,8 +131,6 @@ public void beforeTest() { String command = eventContext.getOnBeforeTest(); - command = command.replace("__testRunId__", newTestRunId); - Future beforeTestCommandFuture = runCommand(command, "beforeTest"); if (beforeTestCommandFuture == null) { @@ -177,6 +236,9 @@ private Future runCommand(String command, String commandType) { } logger.info("About to run " + commandType + " [" + command + "]"); + String newTestRunId = testContext.getTestRunId(); + command = command.replace("__testRunId__", newTestRunId); + List commandList; if (isWindows) { @@ -272,4 +334,13 @@ private static int getExitCode(Future future) { } return processResult.getExitValue(); } + + static Map parseSettings(String eventSettings) { + if (eventSettings == null || eventSettings.trim().isEmpty()) { + return Collections.emptyMap(); + } + return Arrays.stream(eventSettings.split(";")) + .map(s -> s.split("=")) + .collect(Collectors.toMap(k -> k[0], v -> v.length == 2 ? v[1] : "")); + } } diff --git a/src/main/java/io/perfana/events/commandrunner/CommandRunnerEventConfig.java b/src/main/java/io/perfana/events/commandrunner/CommandRunnerEventConfig.java index 2ff0955..d59b280 100644 --- a/src/main/java/io/perfana/events/commandrunner/CommandRunnerEventConfig.java +++ b/src/main/java/io/perfana/events/commandrunner/CommandRunnerEventConfig.java @@ -22,19 +22,18 @@ public class CommandRunnerEventConfig extends EventConfig { private String onStartTest = ""; - private String onBeforeTest = ""; private String onKeepAlive = ""; - private String onAbort = ""; - private String onAfterTest = ""; + private String onScheduledEvent = ""; + private boolean sendTestRunConfig = false; @Override public CommandRunnerEventContext toContext() { - return new CommandRunnerEventContext(super.toContext(), onBeforeTest, onStartTest, onKeepAlive, onAbort, onAfterTest, sendTestRunConfig); + return new CommandRunnerEventContext(super.toContext(), onBeforeTest, onStartTest, onKeepAlive, onAbort, onAfterTest, onScheduledEvent, sendTestRunConfig); } public String getOnBeforeTest() { @@ -85,6 +84,14 @@ public void setOnAfterTest(String onAfterTest) { this.onAfterTest = onAfterTest; } + public String getOnScheduledEvent() { + return onScheduledEvent; + } + + public void setOnScheduledEvent(String onScheduledEvent) { + this.onScheduledEvent = onScheduledEvent; + } + @Override public String toString() { return "CommandRunnerEventConfig{" + @@ -93,6 +100,7 @@ public String toString() { ", onKeepAlive='" + onKeepAlive + '\'' + ", onAbort='" + onAbort + '\'' + ", onAfterTest='" + onAfterTest + '\'' + + ", onCustomEvent='" + onScheduledEvent + '\'' + ", sendTestRunConfig=" + sendTestRunConfig + '}'; } diff --git a/src/main/java/io/perfana/events/commandrunner/CommandRunnerEventContext.java b/src/main/java/io/perfana/events/commandrunner/CommandRunnerEventContext.java index 81f0cf0..f9c6c6f 100644 --- a/src/main/java/io/perfana/events/commandrunner/CommandRunnerEventContext.java +++ b/src/main/java/io/perfana/events/commandrunner/CommandRunnerEventContext.java @@ -28,15 +28,17 @@ public class CommandRunnerEventContext extends EventContext { private final String onKeepAlive; private final String onAbort; private final String onAfterTest; + private final String onScheduledEvent; private final boolean sendTestRunConfig; - protected CommandRunnerEventContext(EventContext context, String onBeforeTest, String onStartTest, String onKeepAlive, String onAbort, String onAfterTest, boolean sendTestRunConfig) { + protected CommandRunnerEventContext(EventContext context, String onBeforeTest, String onStartTest, String onKeepAlive, String onAbort, String onAfterTest, String onCustomEvent, boolean sendTestRunConfig) { super(context, CommandRunnerEventFactory.class.getName()); this.onStartTest = onStartTest; this.onBeforeTest = onBeforeTest; this.onKeepAlive = onKeepAlive; this.onAbort = onAbort; this.onAfterTest = onAfterTest; + this.onScheduledEvent = onCustomEvent; this.sendTestRunConfig = sendTestRunConfig; } @@ -60,6 +62,10 @@ public String getOnBeforeTest() { return onBeforeTest; } + public String getOnScheduledEvent() { + return onScheduledEvent; + } + public boolean isSendTestRunConfig() { return sendTestRunConfig; } @@ -72,6 +78,7 @@ public String toString() { ", onKeepAlive='" + onKeepAlive + '\'' + ", onAbort='" + onAbort + '\'' + ", onAfterTest='" + onAfterTest + '\'' + + ", onScheduledEvent='" + onScheduledEvent + '\'' + ", sendTestRunConfig=" + sendTestRunConfig + '}'; }