Skip to content

Commit bdf7a16

Browse files
committed
refactor: migrate CommandsWrapper commands to dynamic properties
fix: render before command with options in CommandsWrapper (#7496) refactor: return only command when no interpreter and no beforeCommands (#7452) refactor: remove rendering from Docker (#7439) rendering is done in the CommandsWrapper so no need to render again in the task runner refactor: introduce render in commands wrapper for property string (#7430) fix: enable rendering of commands properties inside CommandsWrapper (#7381) refactor: migrate commands to Property in TaskCommands and CommandsWrapper implement beforeCommand and interpreter
1 parent 592a99e commit bdf7a16

File tree

11 files changed

+165
-35
lines changed

11 files changed

+165
-35
lines changed

core/src/main/java/io/kestra/core/models/tasks/runners/ScriptService.java

+13
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.google.common.collect.ImmutableMap;
44
import io.kestra.core.exceptions.IllegalVariableEvaluationException;
5+
import io.kestra.core.models.property.Property;
56
import io.kestra.core.runners.RunContext;
67
import io.kestra.core.utils.ListUtils;
78
import io.kestra.core.utils.Slugify;
@@ -93,6 +94,18 @@ public static List<String> replaceInternalStorage(
9394

9495
}
9596

97+
public static List<String> replaceInternalStorage(
98+
RunContext runContext,
99+
Map<String, Object> additionalVars,
100+
Property<List<String>> commands,
101+
boolean replaceWithRelativePath
102+
) throws IOException, IllegalVariableEvaluationException {
103+
return commands == null ? Collections.emptyList() :
104+
runContext.render(commands).asList(String.class, additionalVars).stream()
105+
.map(throwFunction(c -> ScriptService.replaceInternalStorage(runContext, c, replaceWithRelativePath)))
106+
.toList();
107+
}
108+
96109
public static List<String> replaceInternalStorage(
97110
RunContext runContext,
98111
List<String> commands

core/src/main/java/io/kestra/core/models/tasks/runners/TaskCommands.java

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package io.kestra.core.models.tasks.runners;
22

3+
import io.kestra.core.models.property.Property;
4+
import lombok.With;
5+
36
import java.io.IOException;
47
import java.nio.file.Files;
58
import java.nio.file.Path;
@@ -19,7 +22,11 @@ public interface TaskCommands {
1922

2023
AbstractLogConsumer getLogConsumer();
2124

22-
List<String> getCommands();
25+
Property<List<String>> getInterpreter();
26+
27+
Property<List<String>> getBeforeCommands();
28+
29+
Property<List<String>> getCommands();
2330

2431
Map<String, Object> getAdditionalVars();
2532

core/src/main/java/io/kestra/core/models/tasks/runners/TaskRunner.java

+4-3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import io.kestra.core.exceptions.IllegalVariableEvaluationException;
66
import io.kestra.core.models.Plugin;
77
import io.kestra.core.models.WorkerJobLifecycle;
8+
import io.kestra.core.models.property.Property;
89
import io.kestra.core.runners.RunContext;
910
import io.kestra.plugin.core.runner.Process;
1011
import jakarta.validation.constraints.NotBlank;
@@ -16,11 +17,11 @@
1617
import lombok.experimental.SuperBuilder;
1718
import org.apache.commons.lang3.SystemUtils;
1819

19-
import java.util.HashMap;
20-
import java.util.List;
21-
import java.util.Map;
20+
import java.io.IOException;
21+
import java.util.*;
2222
import java.util.concurrent.atomic.AtomicBoolean;
2323
import java.util.concurrent.atomic.AtomicReference;
24+
import java.util.stream.Stream;
2425

2526
import static io.kestra.core.utils.WindowsUtils.windowsToUnixPath;
2627

core/src/main/java/io/kestra/plugin/core/runner/Process.java

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package io.kestra.plugin.core.runner;
22

3+
import io.kestra.core.exceptions.IllegalVariableEvaluationException;
34
import io.kestra.core.models.annotations.Example;
45
import io.kestra.core.models.annotations.Plugin;
6+
import io.kestra.core.models.property.Property;
57
import io.kestra.core.models.tasks.runners.*;
68
import io.kestra.core.runners.RunContext;
79
import io.micronaut.core.annotation.Introspected;
@@ -14,6 +16,7 @@
1416
import org.slf4j.Logger;
1517

1618
import java.io.BufferedReader;
19+
import java.io.IOException;
1720
import java.io.InputStream;
1821
import java.io.InputStreamReader;
1922
import java.nio.charset.StandardCharsets;
@@ -133,11 +136,14 @@ public TaskRunnerResult<TaskRunnerDetailResult> run(RunContext runContext, TaskC
133136
environment.putAll(this.env(runContext, taskCommands));
134137

135138
processBuilder.directory(taskCommands.getWorkingDirectory().toFile());
136-
processBuilder.command(taskCommands.getCommands());
139+
140+
List<String> renderedCommands = runContext.render(taskCommands.getCommands()).asList(String.class);
141+
142+
processBuilder.command(renderedCommands);
137143

138144
java.lang.Process process = processBuilder.start();
139145
long pid = process.pid();
140-
logger.debug("Starting command with pid {} [{}]", pid, String.join(" ", taskCommands.getCommands()));
146+
logger.debug("Starting command with pid {} [{}]", pid, String.join(" ", renderedCommands));
141147

142148
LogRunnable stdOutRunnable = new LogRunnable(process.getInputStream(), defaultLogConsumer, false);
143149
LogRunnable stdErrRunnable = new LogRunnable(process.getErrorStream(), defaultLogConsumer, true);

core/src/test/java/io/kestra/core/models/tasks/runners/TaskRunnerTest.java

+12-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.kestra.core.models.tasks.runners;
22

33
import io.kestra.core.exceptions.IllegalVariableEvaluationException;
4+
import io.kestra.core.models.property.Property;
45
import io.kestra.core.runners.RunContext;
56
import io.kestra.core.runners.RunContextFactory;
67
import io.micronaut.context.ApplicationContext;
@@ -149,7 +150,17 @@ public AbstractLogConsumer getLogConsumer() {
149150
}
150151

151152
@Override
152-
public List<String> getCommands() {
153+
public Property<List<String>> getInterpreter() {
154+
return null;
155+
}
156+
157+
@Override
158+
public Property<List<String>> getBeforeCommands() {
159+
return null;
160+
}
161+
162+
@Override
163+
public Property<List<String>> getCommands() {
153164
return null;
154165
}
155166

script/src/main/java/io/kestra/plugin/scripts/exec/AbstractExecScript.java

+8-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import io.kestra.core.models.annotations.PluginProperty;
55
import io.kestra.core.models.property.Property;
66
import io.kestra.core.models.tasks.*;
7+
import io.kestra.core.models.tasks.runners.RemoteRunnerInterface;
8+
import io.kestra.core.models.tasks.runners.ScriptService;
79
import io.kestra.core.models.tasks.runners.TargetOS;
810
import io.kestra.core.models.tasks.runners.TaskRunner;
911
import io.kestra.core.runners.RunContext;
@@ -166,9 +168,14 @@ protected CommandsWrapper commands(RunContext runContext) throws IllegalVariable
166168
.withOutputFiles(runContext.render(this.getOutputFiles()).asList(String.class))
167169
.withEnableOutputDirectory(runContext.render(this.getOutputDirectory()).as(Boolean.class).orElse(null))
168170
.withTimeout(runContext.render(this.getTimeout()).as(Duration.class).orElse(null))
169-
.withTargetOS(runContext.render(this.getTargetOS()).as(TargetOS.class).orElseThrow());
171+
.withTargetOS(runContext.render(this.getTargetOS()).as(TargetOS.class).orElseThrow())
172+
.withFailFast(runContext.render(this.getFailFast()).as(Boolean.class).orElse(false));
170173
}
171174

175+
/**
176+
* Rendering of beforeCommands will be done in the CommandsWrapper to give access to the workingDir variable
177+
*/
178+
@Deprecated(since = "0.22")
172179
protected List<String> getBeforeCommandsWithOptions(RunContext runContext) throws IllegalVariableEvaluationException {
173180
return mayAddExitOnErrorCommands(runContext.render(this.getBeforeCommands()).asList(String.class), runContext);
174181
}

script/src/main/java/io/kestra/plugin/scripts/exec/scripts/runners/CommandsWrapper.java

+76-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.kestra.plugin.scripts.exec.scripts.runners;
22

33
import io.kestra.core.exceptions.IllegalVariableEvaluationException;
4+
import io.kestra.core.models.property.Property;
45
import io.kestra.core.models.tasks.RunnableTaskException;
56
import io.kestra.core.models.tasks.runners.DefaultLogConsumer;
67
import io.kestra.core.models.tasks.runners.*;
@@ -20,15 +21,16 @@
2021
import lombok.AllArgsConstructor;
2122
import lombok.Getter;
2223
import lombok.With;
24+
import org.apache.commons.lang3.SystemUtils;
2325

2426
import java.io.IOException;
2527
import java.io.InputStream;
2628
import java.net.URI;
2729
import java.nio.file.Path;
2830
import java.time.Duration;
29-
import java.util.HashMap;
30-
import java.util.List;
31-
import java.util.Map;
31+
import java.util.*;
32+
33+
import static io.kestra.core.utils.Rethrow.throwFunction;
3234

3335
@AllArgsConstructor
3436
@Getter
@@ -42,7 +44,19 @@ public class CommandsWrapper implements TaskCommands {
4244
private Map<String, Object> additionalVars;
4345

4446
@With
45-
private List<String> commands;
47+
private Property<List<String>> interpreter;
48+
49+
@With
50+
private Property<List<String>> beforeCommands;
51+
52+
@With
53+
private Property<List<String>> commands;
54+
55+
@With
56+
private boolean beforeCommandsWithOptions;
57+
58+
@With
59+
private boolean failFast;
4660

4761
private Map<String, String> env;
4862

@@ -96,7 +110,11 @@ public CommandsWrapper withEnv(Map<String, String> envs) {
96110
workingDirectory,
97111
getOutputDirectory(),
98112
additionalVars,
113+
interpreter,
114+
beforeCommands,
99115
commands,
116+
beforeCommandsWithOptions,
117+
failFast,
100118
envs,
101119
logConsumer,
102120
runnerType,
@@ -155,7 +173,21 @@ public <T extends TaskRunnerDetailResult> ScriptOutput run() throws Exception {
155173
RunContextInitializer initializer = ((DefaultRunContext) runContext).getApplicationContext().getBean(RunContextInitializer.class);
156174

157175
RunContext taskRunnerRunContext = initializer.forPlugin(((DefaultRunContext) runContext).clone(), realTaskRunner);
158-
this.commands = this.render(runContext, commands);
176+
177+
List<String> renderedCommands = this.renderCommands(runContext, commands);
178+
List<String> renderedBeforeCommands = this.renderCommands(runContext, beforeCommands);
179+
List<String> renderedInterpreter = this.renderCommands(runContext, interpreter);
180+
181+
List<String> finalCommands = renderedBeforeCommands.isEmpty() && renderedInterpreter.isEmpty() ?
182+
renderedCommands :
183+
ScriptService.scriptCommands(
184+
renderedInterpreter,
185+
this.isBeforeCommandsWithOptions() ? getBeforeCommandsWithOptions(renderedBeforeCommands) : renderedBeforeCommands,
186+
renderedCommands,
187+
Optional.ofNullable(targetOS).orElse(TargetOS.AUTO)
188+
);
189+
190+
this.commands = Property.of(finalCommands);
159191

160192
ScriptOutput.ScriptOutputBuilder scriptOutputBuilder = ScriptOutput.builder()
161193
.warningOnStdErr(this.warningOnStdErr);
@@ -244,7 +276,18 @@ public String render(RunContext runContext, String command, List<String> interna
244276
);
245277
}
246278

247-
public List<String> render(RunContext runContext, List<String> commands) throws IllegalVariableEvaluationException, IOException {
279+
public String render(RunContext runContext, Property<String> command) throws IllegalVariableEvaluationException, IOException {
280+
TaskRunner<?> taskRunner = this.getTaskRunner();
281+
if (command == null) {
282+
return null;
283+
}
284+
285+
return runContext.render(command).as(String.class, taskRunner.additionalVars(runContext, this))
286+
.map(throwFunction(c -> ScriptService.replaceInternalStorage(runContext, c, taskRunner instanceof RemoteRunnerInterface)))
287+
.orElse(null);
288+
}
289+
290+
public List<String> renderCommands(RunContext runContext, Property<List<String>> commands) throws IllegalVariableEvaluationException, IOException {
248291
TaskRunner<?> taskRunner = this.getTaskRunner();
249292
return ScriptService.replaceInternalStorage(
250293
this.runContext,
@@ -253,4 +296,31 @@ public List<String> render(RunContext runContext, List<String> commands) throws
253296
taskRunner instanceof RemoteRunnerInterface
254297
);
255298
}
299+
300+
protected List<String> getBeforeCommandsWithOptions(List<String> beforeCommands) throws IllegalVariableEvaluationException {
301+
if (!this.isFailFast()) {
302+
return beforeCommands;
303+
}
304+
305+
if (beforeCommands == null || beforeCommands.isEmpty()) {
306+
return getExitOnErrorCommands();
307+
}
308+
309+
ArrayList<String> newCommands = new ArrayList<>(beforeCommands.size() + 1);
310+
newCommands.addAll(getExitOnErrorCommands());
311+
newCommands.addAll(beforeCommands);
312+
return newCommands;
313+
}
314+
315+
protected List<String> getExitOnErrorCommands() {
316+
TargetOS os = this.getTargetOS();
317+
318+
// If targetOS is Windows OR targetOS is AUTO && current system is windows and we use process as a runner.(TLDR will run on windows)
319+
if (os == TargetOS.WINDOWS ||
320+
(os == TargetOS.AUTO && SystemUtils.IS_OS_WINDOWS && this.getTaskRunner() instanceof Process)) {
321+
return List.of("");
322+
}
323+
// errexit option may be unsupported by non-shell interpreter.
324+
return List.of("set -e");
325+
}
256326
}

script/src/main/java/io/kestra/plugin/scripts/runner/docker/Docker.java

+5-3
Original file line numberDiff line numberDiff line change
@@ -429,11 +429,13 @@ public TaskRunnerResult<DockerTaskRunnerDetailResult> run(RunContext runContext,
429429
// start container
430430
dockerClient.startContainerCmd(exec.getId()).exec();
431431

432+
List<String> renderedCommands = runContext.render(taskCommands.getCommands()).asList(String.class);
433+
432434
if (logger.isDebugEnabled()) {
433435
logger.debug(
434436
"Starting command with container id {} [{}]",
435437
exec.getId(),
436-
String.join(" ", taskCommands.getCommands())
438+
String.join(" ", renderedCommands)
437439
);
438440
}
439441

@@ -641,7 +643,7 @@ private DockerClient dockerClient(RunContext runContext, String image, String ho
641643
return DockerService.client(dockerClientConfig);
642644
}
643645

644-
private CreateContainerCmd configure(TaskCommands taskCommands, DockerClient dockerClient, RunContext runContext, Map<String, Object> additionalVars) throws IllegalVariableEvaluationException {
646+
private CreateContainerCmd configure(TaskCommands taskCommands, DockerClient dockerClient, RunContext runContext, Map<String, Object> additionalVars) throws IllegalVariableEvaluationException, IOException {
645647
Optional<Boolean> volumeEnabledConfig = runContext.pluginConfiguration(VOLUME_ENABLED_CONFIG);
646648
if (volumeEnabledConfig.isEmpty()) {
647649
// check the legacy property and emit a warning if used
@@ -779,7 +781,7 @@ private CreateContainerCmd configure(TaskCommands taskCommands, DockerClient doc
779781

780782
return container
781783
.withHostConfig(hostConfig)
782-
.withCmd(taskCommands.getCommands())
784+
.withCmd(runContext.render(taskCommands.getCommands()).asList(String.class))
783785
.withAttachStderr(true)
784786
.withAttachStdout(true);
785787
}

0 commit comments

Comments
 (0)