Repository: kestra-io/kestra (java)

Issue:
Bash Task : add a property warningOnStdErr

Pull request:
feat(jdbc): skip worker task running resubmission for skipped execution
Fixes #405


Patch:
diff --git a/core/src/main/java/io/kestra/core/runners/NamespaceFilesService.java b/core/src/main/java/io/kestra/core/runners/NamespaceFilesService.java
index 7e8d1732a..97b8b1e73 100644
--- a/core/src/main/java/io/kestra/core/runners/NamespaceFilesService.java
+++ b/core/src/main/java/io/kestra/core/runners/NamespaceFilesService.java
@@ -107,7 +107,6 @@ public class NamespaceFilesService {
     private void copy(String tenantId, String namespace, Path basePath, List<URI> files) throws IOException {
         files
             .forEach(throwConsumer(f -> {
-                InputStream inputStream = storageInterface.get(tenantId, uri(namespace, f));
                 Path destination = Paths.get(basePath.toString(), f.getPath());
 
                 if (!destination.getParent().toFile().exists()) {
@@ -115,7 +114,9 @@ public class NamespaceFilesService {
                     destination.getParent().toFile().mkdirs();
                 }
 
-                Files.copy(inputStream, destination);
+                try (InputStream inputStream = storageInterface.get(tenantId, uri(namespace, f))) {
+                    Files.copy(inputStream, destination);
+                }
             }));
     }
 }
diff --git a/core/src/main/java/io/kestra/core/runners/pebble/functions/ReadFileFunction.java b/core/src/main/java/io/kestra/core/runners/pebble/functions/ReadFileFunction.java
index d5061c045..10437811a 100644
--- a/core/src/main/java/io/kestra/core/runners/pebble/functions/ReadFileFunction.java
+++ b/core/src/main/java/io/kestra/core/runners/pebble/functions/ReadFileFunction.java
@@ -1,7 +1,6 @@
 package io.kestra.core.runners.pebble.functions;
 
 import io.kestra.core.storages.StorageInterface;
-import io.kestra.core.tenant.TenantService;
 import io.kestra.core.utils.Slugify;
 import io.pebbletemplates.pebble.error.PebbleException;
 import io.pebbletemplates.pebble.extension.Function;
@@ -11,6 +10,7 @@ import jakarta.inject.Inject;
 import jakarta.inject.Singleton;
 
 import java.io.IOException;
+import java.io.InputStream;
 import java.net.URI;
 import java.nio.charset.StandardCharsets;
 import java.util.List;
@@ -47,7 +47,9 @@ public class ReadFileFunction implements Function {
     private String readFromNamespaceFile(EvaluationContext context, String path) throws IOException {
         Map<String, String> flow = (Map<String, String>) context.getVariable("flow");
         URI namespaceFile = URI.create(storageInterface.namespaceFilePrefix(flow.get("namespace")) + "/" + path);
-        return new String(storageInterface.get(flow.get("tenantId"), namespaceFile).readAllBytes(), StandardCharsets.UTF_8);
+        try (InputStream inputStream = storageInterface.get(flow.get("tenantId"), namespaceFile)) {
+            return new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
+        }
     }
 
     private String readFromInternalStorageUri(EvaluationContext context, String path) throws IOException {
@@ -69,7 +71,9 @@ public class ReadFileFunction implements Function {
             }
         }
         URI internalStorageFile = URI.create(path);
-        return new String(storageInterface.get(flow.get("tenantId"), internalStorageFile).readAllBytes(), StandardCharsets.UTF_8);
+        try (InputStream inputStream = storageInterface.get(flow.get("tenantId"), internalStorageFile)) {
+            return new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
+        }
     }
 
     private boolean validateFileUri(String namespace, String flowId, String executionId, String path) {
diff --git a/core/src/main/java/io/kestra/core/services/StorageService.java b/core/src/main/java/io/kestra/core/services/StorageService.java
index adb3b9fe1..6ce7453eb 100644
--- a/core/src/main/java/io/kestra/core/services/StorageService.java
+++ b/core/src/main/java/io/kestra/core/services/StorageService.java
@@ -4,10 +4,7 @@ import io.kestra.core.runners.RunContext;
 import io.kestra.core.storages.StorageSplitInterface;
 import io.micronaut.core.convert.format.ReadableBytesTypeConverter;
 
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.RandomAccessFile;
+import java.io.*;
 import java.net.URI;
 import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
diff --git a/core/src/main/java/io/kestra/core/storages/StorageInterface.java b/core/src/main/java/io/kestra/core/storages/StorageInterface.java
index d19dd6e3b..42abe765f 100644
--- a/core/src/main/java/io/kestra/core/storages/StorageInterface.java
+++ b/core/src/main/java/io/kestra/core/storages/StorageInterface.java
@@ -42,8 +42,7 @@ public interface StorageInterface {
      * @return true if the uri points to a file/object that exist in the internal storage.
      */
     default boolean exists(String tenantId, URI uri) {
-        try {
-            get(tenantId, uri);
+        try (InputStream ignored = get(tenantId, uri)){
             return true;
         } catch (IOException ieo) {
             return false;
diff --git a/core/src/main/java/io/kestra/core/tasks/states/AbstractState.java b/core/src/main/java/io/kestra/core/tasks/states/AbstractState.java
index b1e48e9b6..47c2e277d 100644
--- a/core/src/main/java/io/kestra/core/tasks/states/AbstractState.java
+++ b/core/src/main/java/io/kestra/core/tasks/states/AbstractState.java
@@ -54,9 +54,9 @@ public abstract class AbstractState extends Task {
 
 
     protected Map<String, Object> get(RunContext runContext) throws IllegalVariableEvaluationException, IOException {
-        InputStream taskStateFile = runContext.getTaskStateFile("tasks-states", runContext.render(this.name), this.namespace, this.taskrunValue);
-
-        return JacksonMapper.ofJson(false).readValue(taskStateFile, TYPE_REFERENCE);
+        try (InputStream taskStateFile = runContext.getTaskStateFile("tasks-states", runContext.render(this.name), this.namespace, this.taskrunValue)) {
+            return JacksonMapper.ofJson(false).readValue(taskStateFile, TYPE_REFERENCE);
+        }
     }
 
     protected Pair<URI, Map<String, Object>> merge(RunContext runContext, Map<String, Object> map) throws IllegalVariableEvaluationException, IOException {
diff --git a/core/src/main/java/io/kestra/core/tasks/storages/Concat.java b/core/src/main/java/io/kestra/core/tasks/storages/Concat.java
index a623f2459..9cd318aa9 100644
--- a/core/src/main/java/io/kestra/core/tasks/storages/Concat.java
+++ b/core/src/main/java/io/kestra/core/tasks/storages/Concat.java
@@ -16,6 +16,7 @@ import io.kestra.core.runners.RunContext;
 import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.FileOutputStream;
+import java.io.InputStream;
 import java.net.URI;
 import java.util.List;
 
@@ -128,7 +129,9 @@ public class Concat extends Task implements RunnableTask<Concat.Output> {
 
             finalFiles.forEach(throwConsumer(s -> {
                 URI from = new URI(runContext.render(s));
-                IOUtils.copyLarge(runContext.uriToInputStream(from), fileOutputStream);
+                try (InputStream inputStream = runContext.uriToInputStream(from)) {
+                    IOUtils.copyLarge(inputStream, fileOutputStream);
+                }
 
                 if (separator != null) {
                     IOUtils.copy(new ByteArrayInputStream(this.separator.getBytes()), fileOutputStream);
diff --git a/jdbc/src/main/java/io/kestra/jdbc/runner/JdbcExecutor.java b/jdbc/src/main/java/io/kestra/jdbc/runner/JdbcExecutor.java
index a19bf27cc..13d1966f7 100644
--- a/jdbc/src/main/java/io/kestra/jdbc/runner/JdbcExecutor.java
+++ b/jdbc/src/main/java/io/kestra/jdbc/runner/JdbcExecutor.java
@@ -238,20 +238,28 @@ public class JdbcExecutor implements ExecutorInterface {
             workerJobRunningRepository.getWorkerJobWithWorkerDead(context, workersToDeleteUuids)
                 .forEach(workerJobRunning -> {
                     if (workerJobRunning instanceof WorkerTaskRunning workerTaskRunning) {
-                        workerTaskQueue.emit(WorkerTask.builder()
-                            .taskRun(workerTaskRunning.getTaskRun())
-                            .task(workerTaskRunning.getTask())
-                            .runContext(workerTaskRunning.getRunContext())
-                            .build()
-                        );
+                        if (skipExecutionService.skipExecution(workerTaskRunning.getTaskRun().getExecutionId())) {
+                            // if the execution is skipped, we remove the workerTaskRunning and skip its resubmission
+                            log.warn("Skipping execution {}", workerTaskRunning.getTaskRun().getId());
+                            workerJobRunningRepository.deleteByKey(workerTaskRunning.uid());
+                        }
+                        else {
+                            workerTaskQueue.emit(WorkerTask.builder()
+                                .taskRun(workerTaskRunning.getTaskRun())
+                                .task(workerTaskRunning.getTask())
+                                .runContext(workerTaskRunning.getRunContext())
+                                .build()
+                            );
+
+                            log.warn(
+                                "[namespace: {}] [flow: {}] [execution: {}] [taskrun: {}] WorkerTask is being resend",
+                                workerTaskRunning.getTaskRun().getNamespace(),
+                                workerTaskRunning.getTaskRun().getFlowId(),
+                                workerTaskRunning.getTaskRun().getExecutionId(),
+                                workerTaskRunning.getTaskRun().getId()
+                            );
+                        }
 
-                        log.warn(
-                            "[namespace: {}] [flow: {}] [execution: {}] [taskrun: {}] WorkerTask is being resend",
-                            workerTaskRunning.getTaskRun().getNamespace(),
-                            workerTaskRunning.getTaskRun().getFlowId(),
-                            workerTaskRunning.getTaskRun().getExecutionId(),
-                            workerTaskRunning.getTaskRun().getId()
-                        );
                     } else if (workerJobRunning instanceof WorkerTriggerRunning workerTriggerRunning) {
                         workerTaskQueue.emit(WorkerTrigger.builder()
                             .trigger(workerTriggerRunning.getTrigger())
diff --git a/ui/public/init.js b/ui/public/init.js
index cdcba07d1..00c31d271 100644
--- a/ui/public/init.js
+++ b/ui/public/init.js
@@ -30,6 +30,31 @@ const extensionsToFetch = Object.entries(versionByExtensionIdToFetch).map(([exte
 }));
 
 // used to configure VSCode startup
+const sidebarTabs = [
+    {"id": "workbench.view.explorer", "pinned": true, "visible": true, "order": 0},
+    {"id": "workbench.view.search", "pinned": true, "visible": true, "order": 1},
+    {"id": "workbench.view.scm", "pinned": false, "visible": false, "order": 2},
+    {"id": "workbench.view.debug", "pinned": false,"visible": false,"order": 3},
+    {"id": "workbench.view.extensions", "pinned": true,"visible": true,"order": 4},
+    {"id": "workbench.view.remote", "pinned": false,"visible": false,"order": 4},
+    {"id": "workbench.view.extension.test", "pinned": false,"visible": false,"order": 6},
+    {"id": "workbench.view.extension.references-view", "pinned": false,"visible": false,"order": 7},
+    {"id": "workbench.panel.chatSidebar", "pinned": false,"visible": false,"order": 100},
+    {"id": "userDataProfiles", "pinned": false, "visible": false},
+    {"id": "workbench.view.sync", "pinned": false,"visible": false},
+    {"id": "workbench.view.editSessions", "pinned": false, "visible": false}
+];
+
+const bottomBarTabs = [
+    {"id":"workbench.panel.markers", "pinned": false,"visible": false,"order": 0},
+    {"id":"workbench.panel.output", "pinned": false,"visible": false,"order": 1},
+    {"id":"workbench.panel.repl", "pinned": false,"visible": false,"order": 2},
+    {"id":"terminal", "pinned": false,"visible": false,"order": 3},
+    {"id":"workbench.panel.testResults", "pinned": false,"visible": false,"order": 3},
+    {"id":"~remote.forwardedPortsContainer", "pinned": false,"visible": false,"order": 5},
+    {"id":"refactorPreview", "pinned": false,"visible": false}
+];
+
 window.product = {
     productConfiguration: {
         nameShort: "Kestra VSCode",
@@ -77,5 +102,17 @@ window.product = {
         "workbench.colorTheme": THEME === "dark" ? "Sweet Dracula" : "Default Light Modern",
         // provide the Kestra root URL to extension
         "kestra.api.url": KESTRA_API_URL
+    },
+    profile: {
+        name: "Kestra VSCode",
+        contents: JSON.stringify({
+            globalState: JSON.stringify({
+                "storage": {
+                    "workbench.activity.pinnedViewlets2": sidebarTabs,
+                    "workbench.activity.showAccounts": "false",
+                    "workbench.panel.pinnedPanels": bottomBarTabs
+                }
+            })
+        })
     }
 };
\ No newline at end of file
diff --git a/ui/src/components/executions/FilePreview.vue b/ui/src/components/executions/FilePreview.vue
index 06a081b4b..c5393f359 100644
--- a/ui/src/components/executions/FilePreview.vue
+++ b/ui/src/components/executions/FilePreview.vue
@@ -142,7 +142,7 @@
                         executionId: this.executionId,
                         path: this.value,
                         maxRows: this.maxPreview,
-                        encoding: this.encoding
+                        encoding: this.encoding.value
                     })
                     .then(() => {
                         this.isPreviewOpen = true;
diff --git a/ui/src/components/inputs/Editor.vue b/ui/src/components/inputs/Editor.vue
index 5d6d4cd71..4790fb268 100644
--- a/ui/src/components/inputs/Editor.vue
+++ b/ui/src/components/inputs/Editor.vue
@@ -456,15 +456,15 @@
             }
 
             .bottom-right {
-                bottom: var(--spacer);
-                right: var(--spacer);
+                bottom: 0px;
+                right: 0px;
 
                 ul {
                     display: flex;
                     list-style: none;
                     padding: 0;
                     margin: 0;
-                    gap: calc(var(--spacer) / 2);
+                    //gap: calc(var(--spacer) / 2);
                 }
             }
         }
diff --git a/ui/src/components/inputs/EditorButtons.vue b/ui/src/components/inputs/EditorButtons.vue
new file mode 100644
index 000000000..e25518fdf
--- /dev/null
+++ b/ui/src/components/inputs/EditorButtons.vue
@@ -0,0 +1,121 @@
+<template>
+    <div class="to-action-button">
+        <div v-if="isAllowedEdit || canDelete" class="mx-2">
+            <el-dropdown>
+                <el-button type="default" :disabled="isReadOnly">
+                    <DotsVertical title=""/>
+                    {{ $t("actions") }}
+                </el-button>
+                <template #dropdown>
+                    <el-dropdown-menu class="m-dropdown-menu">
+                        <el-dropdown-item
+                            v-if="!isCreating && canDelete"
+                            :icon="Delete"
+                            size="large"
+                            @click="forwardEvent('delete-flow', $event)"
+                        >
+                            {{ $t("delete") }}
+                        </el-dropdown-item>
+
+                        <el-dropdown-item
+                            v-if="!isCreating"
+                            :icon="ContentCopy"
+                            size="large"
+                            @click="forwardEvent('copy', $event)"
+                        >
+                            {{ $t("copy") }}
+                        </el-dropdown-item>
+                        <el-dropdown-item
+                            v-if="isAllowedEdit"
+                            :icon="Exclamation"
+                            size="large"
+                            @click="forwardEvent('open-new-error', null)"
+                            :disabled="!flowHaveTasks"
+                        >
+                            {{ $t("add global error handler") }}
+                        </el-dropdown-item>
+                        <el-dropdown-item
+                            v-if="isAllowedEdit"
+                            :icon="LightningBolt"
+                            size="large"
+                            @click="forwardEvent('open-new-trigger', null)"
+                            :disabled="!flowHaveTasks"
+                        >
+                            {{ $t("add trigger") }}
+                        </el-dropdown-item>
+                        <el-dropdown-item
+                            v-if="isAllowedEdit"
+                            :icon="FileEdit"
+                            size="large"
+                            @click="forwardEvent('open-edit-metadata', null)"
+                        >
+                            {{ $t("edit metadata") }}
+                        </el-dropdown-item>
+                    </el-dropdown-menu>
+                </template>
+            </el-dropdown>
+        </div>
+        <div>
+            <el-button
+                :icon="ContentSave"
+                @click="forwardEvent('save', $event)"
+                v-if="isAllowedEdit"
+                :type="flowError ? 'danger' : 'primary'"
+                :disabled="!haveChange && !isCreating"
+                class="edit-flow-save-button"
+            >
+                {{ $t("save") }}
+            </el-button>
+        </div>
+    </div>
+</template>
+<script setup>
+    import DotsVertical from "vue-material-design-icons/DotsVertical.vue";
+    import Delete from "vue-material-design-icons/Delete.vue";
+    import ContentCopy from "vue-material-design-icons/ContentCopy.vue";
+    import Exclamation from "vue-material-design-icons/Exclamation.vue";
+    import LightningBolt from "vue-material-design-icons/LightningBolt.vue";
+    import FileEdit from "vue-material-design-icons/FileEdit.vue";
+    import ContentSave from "vue-material-design-icons/ContentSave.vue";
+</script>
+<script>
+    import {defineComponent} from "vue";
+
+    export default defineComponent({
+        props: {
+            isCreating: {
+                type: Boolean,
+                default: false
+            },
+            isReadOnly: {
+                type: Boolean,
+                default: false
+            },
+            canDelete: {
+                type: Boolean,
+                default: false
+            },
+            isAllowedEdit: {
+                type: Boolean,
+                default: false
+            },
+            haveChange: {
+                type: Boolean,
+                default: false
+            },
+            flowHaveTasks: {
+                type: Boolean,
+                default: false
+            },
+            flowError: {
+                type: String,
+                default: null
+            }
+        },
+        methods: {
+            forwardEvent(type, event) {
+                this.$emit(type, event);
+            }
+        }
+    })
+</script>
\ No newline at end of file
diff --git a/ui/src/components/inputs/EditorView.vue b/ui/src/components/inputs/EditorView.vue
index ff2b20668..7166772f3 100644
--- a/ui/src/components/inputs/EditorView.vue
+++ b/ui/src/components/inputs/EditorView.vue
@@ -28,6 +28,7 @@
     import {editorViewTypes} from "../../utils/constants";
     import Utils from "@kestra-io/ui-libs/src/utils/Utils";
     import {apiUrl} from "override/utils/route";
+    import EditorButtons from "./EditorButtons.vue";
 
     const store = useStore();
     const router = getCurrentInstance().appContext.config.globalProperties.$router;
@@ -675,75 +676,22 @@
                 <ValidationError ref="validationDomElement" tooltip-placement="bottom-start" size="small" class="ms-2" :error="flowError" :warnings="flowWarnings" />
             </template>
             <template #buttons>
-                <ul>
-                    <li v-if="isAllowedEdit || canDelete">
-                        <el-dropdown>
-                            <el-button type="default" :disabled="isReadOnly">
-                                <DotsVertical title="" />
-                                {{ $t("actions") }}
-                            </el-button>
-                            <template #dropdown>
-                                <el-dropdown-menu class="m-dropdown-menu">
-                                    <el-dropdown-item
-                                        v-if="!props.isCreating && canDelete"
-                                        :icon="Delete"
-                                        size="large"
-                                        @click="deleteFlow"
-                                    >
-                                        {{ $t("delete") }}
-                                    </el-dropdown-item>
-
-                                    <el-dropdown-item
-                                        v-if="!props.isCreating"
-                                        :icon="ContentCopy"
-                                        size="large"
-                                        @click="() => router.push({name: 'flows/create', query: {copy: true}})"
-                                    >
-                                        {{ $t("copy") }}
-                                    </el-dropdown-item>
-                                    <el-dropdown-item
-                                        v-if="isAllowedEdit"
-                                        :icon="Exclamation"
-                                        size="large"
-                                        @click="isNewErrorOpen = true;"
-                                        :disabled="!flowHaveTasks()"
-                                    >
-                                        {{ $t("add global error handler") }}
-                                    </el-dropdown-item>
-                                    <el-dropdown-item
-                                        v-if="isAllowedEdit"
-                                        :icon="LightningBolt"
-                                        size="large"
-                                        @click="isNewTriggerOpen = true;"
-                                        :disabled="!flowHaveTasks()"
-                                    >
-                                        {{ $t("add trigger") }}
-                                    </el-dropdown-item>
-                                    <el-dropdown-item
-                                        v-if="isAllowedEdit"
-                                        :icon="FileEdit"
-                                        size="large"
-                                        @click="isEditMetadataOpen = true;"
-                                    >
-                                        {{ $t("edit metadata") }}
-                                    </el-dropdown-item>
-                                </el-dropdown-menu>
-                            </template>
-                        </el-dropdown>
-                    </li>
-                    <li>
-                        <el-button
-                            :icon="ContentSave"
-                            @click="save"
-                            v-if="isAllowedEdit"
-                            :type="flowError ? 'danger' : 'primary'"
-                            :disabled="!haveChange && !isCreating"
-                            class="edit-flow-save-button"
-                        >
-                            {{ $t("save") }}
-                        </el-button>
-                    </li>
-                </ul>
+                <EditorButtons
+                    v-if="![editorViewTypes.TOPOLOGY, editorViewTypes.SOURCE_TOPOLOGY].includes(viewType)"
+                    :is-creating="props.isCreating"
+                    :is-read-only="props.isReadOnly"
+                    :can-delete="canDelete"
+                    :is-allowed-edit="isAllowedEdit"
+                    :have-change="haveChange"
+                    :flow-have-tasks="flowHaveTasks()"
+                    :flow-error="flowError"
+                    @delete-flow="deleteFlow"
+                    @save="save"
+                    @copy="() => router.push({name: 'flows/create', query: {copy: true}})"
+                    @open-new-error="isNewErrorOpen = true;"
+                    @open-new-trigger="isNewTriggerOpen = true;"
+                    @open-edit-metadata="isEditMetadataOpen = true;"
+                />
             </template>
         </editor>
         <div class="slider" @mousedown="dragEditor" v-if="combinedEditor" />
@@ -859,6 +807,22 @@
             class="to-topology-button"
             @switch-view="switchViewType"
         />
+        <EditorButtons
+            v-if="[editorViewTypes.TOPOLOGY, editorViewTypes.SOURCE_TOPOLOGY].includes(viewType)"
+            :is-creating="props.isCreating"
+            :is-read-only="props.isReadOnly"
+            :can-delete="canDelete"
+            :is-allowed-edit="isAllowedEdit"
+            :have-change="haveChange"
+            :flow-have-tasks="flowHaveTasks()"
+            :flow-error="flowError"
+            @delete-flow="deleteFlow"
+            @save="save"
+            @copy="() => router.push({name: 'flows/create', query: {copy: true}})"
+            @open-new-error="isNewErrorOpen = true;"
+            @open-new-trigger="isNewTriggerOpen = true;"
+            @open-edit-metadata="isEditMetadataOpen = true;"
+        />
     </el-card>
 </template>
 
@@ -879,6 +843,13 @@
         right: 45px;
     }
 
+    .to-action-button {
+        position: absolute;
+        bottom: 30px;
+        right: 45px;
+        display: flex;
+    }
+
     .editor-combined {
         height: 100%;
         width: 50%;
diff --git a/ui/src/styles/layout/element-plus-overload.scss b/ui/src/styles/layout/element-plus-overload.scss
index fcf09728e..9b40df713 100644
--- a/ui/src/styles/layout/element-plus-overload.scss
+++ b/ui/src/styles/layout/element-plus-overload.scss
@@ -413,11 +413,6 @@ form.ks-horizontal {
 
     &.is-dark {
         color: var(--bs-gray-100);
-        html:not(.dark) & {
-            * {
-                color: var(--bs-gray-100);
-            }
-        }
 
         background: var(--bs-gray-900);
         border: 1px solid var(--bs-border-color);
diff --git a/webserver/src/main/java/io/kestra/webserver/controllers/ExecutionController.java b/webserver/src/main/java/io/kestra/webserver/controllers/ExecutionController.java
index 92833039e..166e1962f 100644
--- a/webserver/src/main/java/io/kestra/webserver/controllers/ExecutionController.java
+++ b/webserver/src/main/java/io/kestra/webserver/controllers/ExecutionController.java
@@ -1009,12 +1009,11 @@ public class ExecutionController {
         @Parameter(description = "The execution id") @PathVariable String executionId,
         @Parameter(description = "The internal storage uri") @QueryValue URI path,
         @Parameter(description = "The max row returns") @QueryValue @Nullable Integer maxRows,
-        @Parameter(description = "The file encoding as Java charset name. Defaults to UTF-8", example = "ISO-8859-1") @QueryValue @Nullable String encoding
+        @Parameter(description = "The file encoding as Java charset name. Defaults to UTF-8", example = "ISO-8859-1") @QueryValue(defaultValue = "UTF-8") String encoding
     ) throws IOException {
         this.validateFile(executionId, path, "/api/v1/executions/{executionId}/file?path=" + path);
 
         String extension = FilenameUtils.getExtension(path.toString());
-        InputStream fileStream = storageInterface.get(tenantService.resolveTenant(), path);
         Optional<Charset> charset;
 
         try {
@@ -1023,13 +1022,15 @@ public class ExecutionController {
             throw new IllegalArgumentException("Unable to preview using encoding '" + encoding + "'");
         }
 
-        FileRender fileRender = FileRenderBuilder.of(
-            extension,
-            fileStream,
-            charset,
-            maxRows == null ? this.initialPreviewRows : (maxRows > this.maxPreviewRows ? this.maxPreviewRows : maxRows)
-        );
+        try (InputStream fileStream = storageInterface.get(tenantService.resolveTenant(), path)){
+            FileRender fileRender = FileRenderBuilder.of(
+                extension,
+                fileStream,
+                charset,
+                maxRows == null ? this.initialPreviewRows : (maxRows > this.maxPreviewRows ? this.maxPreviewRows : maxRows)
+            );
 
-        return HttpResponse.ok(fileRender);
+            return HttpResponse.ok(fileRender);
+        }
     }
 }
diff --git a/webserver/src/main/java/io/kestra/webserver/controllers/NamespaceFileController.java b/webserver/src/main/java/io/kestra/webserver/controllers/NamespaceFileController.java
index 5808c04f1..0821a0532 100644
--- a/webserver/src/main/java/io/kestra/webserver/controllers/NamespaceFileController.java
+++ b/webserver/src/main/java/io/kestra/webserver/controllers/NamespaceFileController.java
@@ -138,7 +138,9 @@ public class NamespaceFileController {
     ) throws IOException, URISyntaxException {
         ensureWritableFile(path);
 
-        storageInterface.put(tenantService.resolveTenant(), toNamespacedStorageUri(namespace, path), new BufferedInputStream(fileContent.getInputStream()));
+        try(BufferedInputStream inputStream = new BufferedInputStream(fileContent.getInputStream())) {
+            storageInterface.put(tenantService.resolveTenant(), toNamespacedStorageUri(namespace, path), inputStream);
+        }
     }
 
     @ExecuteOn(TaskExecutors.IO)


Test patch:
diff --git a/jdbc/src/test/java/io/kestra/jdbc/runner/JdbcHeartbeatTest.java b/jdbc/src/test/java/io/kestra/jdbc/runner/JdbcHeartbeatTest.java
index 88ef71cc1..fd9605ff1 100644
--- a/jdbc/src/test/java/io/kestra/jdbc/runner/JdbcHeartbeatTest.java
+++ b/jdbc/src/test/java/io/kestra/jdbc/runner/JdbcHeartbeatTest.java
@@ -12,6 +12,7 @@ import io.kestra.core.queues.QueueFactoryInterface;
 import io.kestra.core.queues.QueueInterface;
 import io.kestra.core.repositories.LocalFlowRepositoryLoader;
 import io.kestra.core.runners.*;
+import io.kestra.core.services.SkipExecutionService;
 import io.kestra.core.tasks.test.Sleep;
 import io.kestra.core.tasks.test.SleepTrigger;
 import io.kestra.core.utils.IdUtils;
@@ -29,13 +30,17 @@ import org.junit.jupiter.api.TestInstance;
 import java.io.IOException;
 import java.net.URISyntaxException;
 import java.util.Collections;
+import java.util.List;
 import java.util.Map;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
 
+import static com.google.common.base.CharMatcher.isNot;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.junit.jupiter.api.Assertions.fail;
 
 @MicronautTest(transactional = false, environments = "heartbeat")
 @TestInstance(TestInstance.Lifecycle.PER_CLASS) // must be per-class to allow calling once init() which took a lot of time
@@ -44,9 +49,6 @@ public abstract class JdbcHeartbeatTest {
     @Inject
     private StandAloneRunner runner;
 
-    @Inject
-    private RunnerUtils runnerUtils;
-
     @Inject
     private LocalFlowRepositoryLoader repositoryLoader;
 
@@ -71,6 +73,9 @@ public abstract class JdbcHeartbeatTest {
     @Named(QueueFactoryInterface.WORKERTRIGGERRESULT_NAMED)
     QueueInterface<WorkerTriggerResult> workerTriggerResultQueue;
 
+    @Inject
+    SkipExecutionService skipExecutionService;
+
     @BeforeAll
     void init() throws IOException, URISyntaxException {
         jdbcTestUtils.drop();
@@ -116,6 +121,46 @@ public abstract class JdbcHeartbeatTest {
         assertThat(workerTaskResult.get().getTaskRun().getState().getCurrent(), is(Type.SUCCESS));
     }
 
+    @Test
+    void taskResubmitSkipExecution() throws Exception {
+        CountDownLatch runningLatch = new CountDownLatch(1);
+
+        Worker worker = new Worker(applicationContext, 8, null);
+        applicationContext.registerSingleton(worker);
+        worker.run();
+        runner.setSchedulerEnabled(false);
+        runner.setWorkerEnabled(false);
+        runner.run();
+        WorkerTask workerTask = workerTask(1500);
+        skipExecutionService.setSkipExecutions(List.of(workerTask.getTaskRun().getExecutionId()));
+
+        AtomicReference<WorkerTaskResult> workerTaskResult = new AtomicReference<>(null);
+        workerTaskResultQueue.receive(either -> {
+            workerTaskResult.set(either.getLeft());
+
+            if (either.getLeft().getTaskRun().getState().getCurrent() == Type.SUCCESS) {
+                // no resubmit should happen!
+                fail();
+            }
+
+            if (either.getLeft().getTaskRun().getState().getCurrent() == Type.RUNNING) {
+                runningLatch.countDown();
+            }
+        });
+
+        workerJobQueue.emit(workerTask);
+        runningLatch.await(2, TimeUnit.SECONDS);
+        worker.shutdown();
+
+        Worker newWorker = new Worker(applicationContext, 8, null);
+        applicationContext.registerSingleton(newWorker);
+        newWorker.run();
+
+        // wait a little to be sure there is no resubmit
+        Thread.sleep(500);
+        assertThat(workerTaskResult.get().getTaskRun().getState().getCurrent(), not(Type.SUCCESS));
+    }
+
     @Test
     void triggerResubmit() throws Exception {
         CountDownLatch countDownLatch = new CountDownLatch(1);