Compare commits

..

1 Commits

Author SHA1 Message Date
Roman Acevedo
9575cc1c87 fake commit to run flaky tests 2025-12-05 18:07:27 +01:00
35 changed files with 62 additions and 146 deletions

View File

@@ -100,7 +100,6 @@ public class State {
/**
* @return either the Duration persisted in database, or calculate it on the fly for non-terminated executions
*/
@JsonIgnore
public Duration getDurationOrComputeIt() {
return this.getDuration().orElseGet(() -> Duration.between(this.getStartDate(), Instant.now()));
}

View File

@@ -93,7 +93,7 @@ public class Property<T> {
* @return a new {@link Property} without a pre-rendered value
*/
public Property<T> skipCache() {
return new Property<>(expression, true);
return Property.ofExpression(expression);
}
/**

View File

@@ -32,12 +32,10 @@ public class NamespaceFilesUtils {
private ExecutorsUtils executorsUtils;
private ExecutorService executorService;
private int maxThreads;
@PostConstruct
public void postConstruct() {
this.maxThreads = Math.max(Runtime.getRuntime().availableProcessors() * 4, 32);
this.executorService = executorsUtils.maxCachedThreadPool(maxThreads, "namespace-file");
this.executorService = executorsUtils.maxCachedThreadPool(Math.max(Runtime.getRuntime().availableProcessors() * 4, 32), "namespace-file");
}
public void loadNamespaceFiles(
@@ -65,11 +63,7 @@ public class NamespaceFilesUtils {
matchedNamespaceFiles.addAll(files);
}
// Use half of the available threads to avoid impacting concurrent tasks
int parallelism = maxThreads / 2;
Flux.fromIterable(matchedNamespaceFiles)
.parallel(parallelism)
.runOn(Schedulers.fromExecutorService(executorService))
.doOnNext(throwConsumer(nsFile -> {
InputStream content = runContext.storage().getFile(nsFile.uri());
Path path = folderPerNamespace ?
@@ -77,7 +71,7 @@ public class NamespaceFilesUtils {
Path.of(nsFile.path());
runContext.workingDir().putFile(path, content, fileExistComportment);
}))
.sequential()
.publishOn(Schedulers.fromExecutorService(executorService))
.blockLast();
Duration duration = stopWatch.getDuration();

View File

@@ -157,7 +157,7 @@ public class LoopUntil extends Task implements FlowableTask<LoopUntil.Output> {
public Instant nextExecutionDate(RunContext runContext, Execution execution, TaskRun parentTaskRun) throws IllegalVariableEvaluationException {
if (!this.reachedMaximums(runContext, execution, parentTaskRun, false)) {
String continueLoop = runContext.render(this.condition).skipCache().as(String.class).orElse(null);
String continueLoop = runContext.render(this.condition).as(String.class).orElse(null);
if (!TruthUtils.isTruthy(continueLoop)) {
return Instant.now().plus(runContext.render(this.getCheckFrequency().getInterval()).as(Duration.class).orElseThrow());
}

View File

@@ -123,7 +123,7 @@ public class Switch extends Task implements FlowableTask<Switch.Output> {
}
private String rendererValue(RunContext runContext) throws IllegalVariableEvaluationException {
return runContext.render(this.value).skipCache().as(String.class).orElseThrow();
return runContext.render(this.value).as(String.class).orElseThrow();
}
@Override

View File

@@ -1,11 +1,9 @@
package io.kestra.plugin.core.flow;
import static io.kestra.core.tenant.TenantService.MAIN_TENANT;
import static org.assertj.core.api.Assertions.as;
import static org.assertj.core.api.Assertions.assertThat;
import com.google.common.collect.ImmutableMap;
import io.kestra.core.junit.annotations.ExecuteFlow;
import io.kestra.core.junit.annotations.KestraTest;
import io.kestra.core.junit.annotations.LoadFlows;
import io.kestra.core.models.executions.Execution;
@@ -102,14 +100,4 @@ class SwitchTest {
assertThat(execution.getState().getCurrent()).isEqualTo(State.Type.FAILED);
}
@Test
@ExecuteFlow("flows/valids/switch-in-concurrent-loop.yaml")
void switchInConcurrentLoop(Execution execution) {
assertThat(execution.getState().getCurrent()).isEqualTo(State.Type.SUCCESS);
assertThat(execution.getTaskRunList()).hasSize(5);
// we check that OOMCRM_EB_DD_000 and OOMCRM_EB_DD_001 have been processed once
assertThat(execution.getTaskRunList().stream().filter(t -> t.getTaskId().equals("OOMCRM_EB_DD_000")).count()).isEqualTo(1);
assertThat(execution.getTaskRunList().stream().filter(t -> t.getTaskId().equals("OOMCRM_EB_DD_001")).count()).isEqualTo(1);
}
}

View File

@@ -1,23 +0,0 @@
id: switch-in-concurrent-loop
namespace: io.kestra.tests
tasks:
- id: iterate_and_check_name
type: io.kestra.plugin.core.flow.ForEach
tasks:
- id: switch
type: io.kestra.plugin.core.flow.Switch
value: "{{ taskrun.value }}"
cases:
"Alice":
- id: OOMCRM_EB_DD_000
type: io.kestra.plugin.core.log.Log
message: Alice
"Bob":
- id: OOMCRM_EB_DD_001
type: io.kestra.plugin.core.log.Log
message: Bob
values: ["Alice", "Bob"]
concurrencyLimit: 0

View File

@@ -13,19 +13,18 @@ tasks:
- io.test.second
- io.test.third
enabled: true
folderPerNamespace: true
exclude:
- /ignore/**
tasks:
- id: t1
type: io.kestra.core.tasks.test.Read
path: "/io.test.third/test/a/b/c/1.txt"
path: "/test/a/b/c/1.txt"
- id: t2
type: io.kestra.core.tasks.test.Read
path: "/io.test.second/a/b/c/2.txt"
path: "/a/b/c/2.txt"
- id: t3
type: io.kestra.core.tasks.test.Read
path: "/io.test.first/a/b/3.txt"
path: "/a/b/3.txt"
- id: t4
type: io.kestra.core.tasks.test.Read
path: "/ignore/4.txt"

View File

@@ -176,7 +176,7 @@ public class JdbcExecutor implements ExecutorInterface {
@Inject
private AbstractJdbcWorkerJobRunningRepository workerJobRunningRepository;
@Inject
private SLAMonitorStorage slaMonitorStorage;
@@ -658,16 +658,21 @@ public class JdbcExecutor implements ExecutorInterface {
workerTaskResults.add(new WorkerTaskResult(taskRun));
}
}
// flowable attempt state transition to running
/// flowable attempt state transition to running
if (workerTask.getTask().isFlowable()) {
List<TaskRunAttempt> attempts = Optional.ofNullable(workerTask.getTaskRun().getAttempts())
.map(ArrayList::new)
.orElseGet(ArrayList::new);
attempts.add(
TaskRunAttempt.builder()
.state(new State().withState(State.Type.RUNNING))
.build()
);
TaskRun updatedTaskRun = workerTask.getTaskRun()
.withAttempts(
List.of(
TaskRunAttempt.builder()
.state(new State().withState(State.Type.RUNNING))
.build()
)
)
.withAttempts(attempts)
.withState(State.Type.RUNNING);
workerTaskResults.add(new WorkerTaskResult(updatedTaskRun));

View File

@@ -24,6 +24,7 @@ import java.util.List;
import java.util.Map;
public class DockerService {
// DDDDDDD
public static DockerClient client(DockerClientConfig dockerClientConfig) {
DockerHttpClient dockerHttpClient = new ApacheDockerHttpClient.Builder()
.dockerHost(dockerClientConfig.getDockerHost())

8
ui/package-lock.json generated
View File

@@ -10,7 +10,7 @@
"hasInstallScript": true,
"dependencies": {
"@js-joda/core": "^5.6.5",
"@kestra-io/ui-libs": "^0.0.264",
"@kestra-io/ui-libs": "^0.0.263",
"@vue-flow/background": "^1.3.2",
"@vue-flow/controls": "^1.1.2",
"@vue-flow/core": "^1.47.0",
@@ -2829,9 +2829,9 @@
"license": "BSD-3-Clause"
},
"node_modules/@kestra-io/ui-libs": {
"version": "0.0.264",
"resolved": "https://registry.npmjs.org/@kestra-io/ui-libs/-/ui-libs-0.0.264.tgz",
"integrity": "sha512-yUZDNaE0wUPOuEq/FL/TQBRd1fTV2dyM8s+VcGRjNSM1uv1uZcsSHro56/heHQx17lo00FDcPT7BMKEifrVhBg==",
"version": "0.0.263",
"resolved": "https://registry.npmjs.org/@kestra-io/ui-libs/-/ui-libs-0.0.263.tgz",
"integrity": "sha512-j1rWqcQAK2CudNBkcDPjUXyaGFeBzJ7QEhPKFAbleHSw0N3QFu/iy0rFZxJNIMWRi1mGZBh74D6vL0OqQJkT2Q==",
"dependencies": {
"@nuxtjs/mdc": "^0.17.3",
"@popperjs/core": "^2.11.8",

View File

@@ -24,7 +24,7 @@
},
"dependencies": {
"@js-joda/core": "^5.6.5",
"@kestra-io/ui-libs": "^0.0.264",
"@kestra-io/ui-libs": "^0.0.263",
"@vue-flow/background": "^1.3.2",
"@vue-flow/controls": "^1.1.2",
"@vue-flow/core": "^1.47.0",

View File

@@ -86,8 +86,8 @@
</el-input>
</el-form-item>
<div class="password-requirements mb-4">
<el-text>
{{ t('setup.form.password_requirements') }}
<el-text>
8+ chars, 1 upper, 1 number
</el-text>
</div>
</el-form>
@@ -502,7 +502,7 @@
localStorage.removeItem("basicAuthUserCreated")
localStorage.setItem("basicAuthSetupCompletedAt", new Date().toISOString())
router.push({name: "welcome"})
router.push({name: "login"})
}
</script>

View File

@@ -101,7 +101,7 @@ $checkbox-checked-color: #8405FF;
.el-text {
color: var(--ks-content-tertiary);
font-size: 12px;
font-size: 14px;
}
}

View File

@@ -3,8 +3,6 @@ import Utils from "../../../utils/utils";
import {cssVariable, State} from "@kestra-io/ui-libs";
import {getSchemeValue} from "../../../utils/scheme";
import {useMiscStore} from "override/stores/misc";
export function tooltip(tooltipModel: {
title?: string[];
body?: { lines: string[] }[];
@@ -117,7 +115,7 @@ export function extractState(value: any) {
return value;
}
export function chartClick(moment: any, router: any, route: any, event: any, parsedData: any, elements: any, type = "label", filters: Record<string, any> = {}) {
export function chartClick(moment: any, router: any, route: any, event: any, parsedData: any, elements: any, type = "label") {
const query: Record<string, any> = {};
if (elements && parsedData) {
@@ -194,11 +192,7 @@ export function chartClick(moment: any, router: any, route: any, event: any, par
params: {
tenant: route.params.tenant,
},
query: {
...query,
...filters,
"filters[timeRange][EQUALS]":useMiscStore()?.configs?.chartDefaultDuration ?? "P30D"
},
query: query,
});
}
}

View File

@@ -49,8 +49,6 @@
showDefault: {type: Boolean, default: false},
short: {type: Boolean, default: false},
execution: {type: Boolean, default: false},
flow: {type: String, default: undefined},
namespace: {type: String, default: undefined},
});
@@ -155,10 +153,7 @@
if (data.type === "io.kestra.plugin.core.dashboard.data.Logs" || props.execution) {
return;
}
chartClick(moment, router, route, {}, parsedData.value, elements, "label", {
...(props.namespace ? {"filters[namespace][IN]": props.namespace} : {}),
...(props.flow ? {"filters[flowId][EQUALS]": props.flow} : {})
});
chartClick(moment, router, route, {}, parsedData.value, elements, "label");
},
}, theme.value);
});

View File

@@ -213,8 +213,6 @@
:filters="chartFilters()"
showDefault
short
:flow="scope.row.id"
:namespace="scope.row.namespace"
/>
</template>
</el-table-column>

View File

@@ -7,14 +7,14 @@
:disabled="!playgroundStore.readyToStart"
>
<el-icon><Play /></el-icon>
<span>{{ $t('playground.run_task') }}</span>
<span>{{ t('playground.run_task') }}</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item :icon="Play" @click="playgroundStore.runUntilTask(taskId)">
{{ $t('playground.run_this_task') }}
{{ t('playground.run_this_task') }}
</el-dropdown-item>
<el-dropdown-item :icon="PlayBoxMultiple" @click="playgroundStore.runUntilTask(taskId, true)">
{{ $t('playground.run_task_and_downstream') }}
{{ t('playground.run_task_and_downstream') }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
@@ -22,10 +22,12 @@
</template>
<script setup lang="ts">
import {useI18n} from "vue-i18n";
import {usePlaygroundStore} from "../../stores/playground";
import Play from "vue-material-design-icons/Play.vue";
import PlayBoxMultiple from "vue-material-design-icons/PlayBoxMultiple.vue";
const {t} = useI18n();
const playgroundStore = usePlaygroundStore();
defineProps<{

View File

@@ -12,7 +12,6 @@
import {useCoreStore} from "../../stores/core";
import {useMiscStore} from "override/stores/misc";
import {computed, onMounted} from "vue";
import {useLayoutStore} from "../../stores/layout";
const coreStore = useCoreStore();
const miscStore = useMiscStore();
@@ -23,9 +22,7 @@
document.getElementsByTagName("html")[0].classList.remove(collapse ? "menu-not-collapsed" : "menu-collapsed");
}
const layoutStore = useLayoutStore();
onMounted(() => {
onMenuCollapse(Boolean(layoutStore.sideMenuCollapsed))
onMenuCollapse(localStorage.getItem("menuCollapsed") === "true")
});
</script>

View File

@@ -28,7 +28,7 @@
</template>
<script setup lang="ts">
import {onUpdated, computed, h, watch} from "vue";
import {onUpdated, ref, computed, h, watch} from "vue";
import {useI18n} from "vue-i18n";
import {useRoute} from "vue-router";
import {useMediaQuery} from "@vueuse/core";
@@ -118,10 +118,7 @@
];
});
const collapsed = computed({
get: () => layoutStore.sideMenuCollapsed,
set: (v: boolean) => layoutStore.setSideMenuCollapsed(v),
})
const collapsed = ref(localStorage.getItem("menuCollapsed") === "true")
const isSmallScreen = useMediaQuery("(max-width: 768px)")

View File

@@ -25,6 +25,10 @@ const handleAuthError = (error, to) => {
initApp(app, routes, null, en).then(({router, piniaStore}) => {
router.beforeEach(async (to, from, next) => {
if (to.meta?.anonymous === true) {
return next();
}
if(to.path === from.path && to.query === from.query) {
return next(); // Prevent navigation if the path and query are the same
}
@@ -41,28 +45,13 @@ initApp(app, routes, null, en).then(({router, piniaStore}) => {
if (validationErrors?.length > 0) {
// Creds exist in config but failed validation
// Route to login to show errors
if (to.name === "login") {
return next();
}
return next({name: "login"})
} else {
// No creds in config - redirect to set it up
if (to.name === "setup") {
return next();
}
return next({name: "setup"})
}
}
if (to.meta?.anonymous === true) {
if (to.name === "setup") {
return next({name: "login"});
}
return next();
}
const hasCredentials = BasicAuth.isLoggedIn()
if (!hasCredentials) {
@@ -103,6 +92,6 @@ initApp(app, routes, null, en).then(({router, piniaStore}) => {
}, null, router, true);
// mount
router.isReady().then(() => app.mount("#app"))
app.mount("#app")
});

View File

@@ -12,13 +12,7 @@ export const useLayoutStore = defineStore("layout", {
topNavbar: undefined,
envName: localStorage.getItem("envName") || undefined,
envColor: localStorage.getItem("envColor") || undefined,
sideMenuCollapsed: (() => {
if (typeof window === "undefined") {
return false;
}
return localStorage.getItem("menuCollapsed") === "true" || window.matchMedia("(max-width: 768px)").matches;
})(),
sideMenuCollapsed: localStorage.getItem("menuCollapsed") === "true",
}),
getters: {},
actions: {

View File

@@ -1612,8 +1612,7 @@
"email": "E-Mail",
"firstName": "Vorname",
"lastName": "Nachname",
"password": "Passwort",
"password_requirements": "Das Passwort muss mindestens 8 Zeichen lang sein und mindestens 1 Großbuchstaben und 1 Zahl enthalten."
"password": "Passwort"
},
"login": "Anmelden",
"logout": "Abmelden",

View File

@@ -1477,8 +1477,7 @@
"email": "Email",
"firstName": "First Name",
"lastName": "Last Name",
"password": "Password",
"password_requirements": "Password must be at least 8 characters long and include at least 1 uppercase letter and 1 number."
"password": "Password"
},
"validation": {
"email_required": "Email is required",

View File

@@ -1612,8 +1612,7 @@
"email": "Correo electrónico",
"firstName": "Nombre",
"lastName": "Apellido",
"password": "Contraseña",
"password_requirements": "La contraseña debe tener al menos 8 caracteres y contener al menos 1 letra mayúscula y 1 número."
"password": "Contraseña"
},
"login": "Iniciar sesión",
"logout": "Cerrar sesión",

View File

@@ -1612,8 +1612,7 @@
"email": "E-mail",
"firstName": "Prénom",
"lastName": "Nom de famille",
"password": "Mot de passe",
"password_requirements": "Le mot de passe doit comporter au moins 8 caractères, inclure au moins 1 lettre majuscule et 1 chiffre."
"password": "Mot de passe"
},
"login": "Connexion",
"logout": "Déconnexion",

View File

@@ -1612,8 +1612,7 @@
"email": "ईमेल",
"firstName": "पहला नाम",
"lastName": "अंतिम नाम",
"password": "पासवर्ड",
"password_requirements": "पासवर्ड कम से कम 8 अक्षरों का होना चाहिए और इसमें कम से कम 1 बड़ा अक्षर और 1 संख्या शामिल होनी चाहिए।"
"password": "पासवर्ड"
},
"login": "लॉगिन",
"logout": "लॉगआउट",

View File

@@ -1612,8 +1612,7 @@
"email": "Email",
"firstName": "Nome",
"lastName": "Cognome",
"password": "Password",
"password_requirements": "La password deve essere lunga almeno 8 caratteri e includere almeno 1 lettera maiuscola e 1 numero."
"password": "Password"
},
"login": "Accedi",
"logout": "Logout",

View File

@@ -1612,8 +1612,7 @@
"email": "メール",
"firstName": "名",
"lastName": "姓",
"password": "パスワード",
"password_requirements": "パスワードは8文字以上で、少なくとも1つの大文字と1つの数字を含める必要があります。"
"password": "パスワード"
},
"login": "ログイン",
"logout": "ログアウト",

View File

@@ -1612,8 +1612,7 @@
"email": "이메일",
"firstName": "이름",
"lastName": "성씨",
"password": "비밀번호",
"password_requirements": "비밀번호는 최소 8자 이상이어야 하며, 최소 1개의 대문자와 1개의 숫자를 포함해야 합니다."
"password": "비밀번호"
},
"login": "로그인",
"logout": "로그아웃",

View File

@@ -1612,8 +1612,7 @@
"email": "Email",
"firstName": "Imię",
"lastName": "Nazwisko",
"password": "Hasło",
"password_requirements": "Hasło musi mieć co najmniej 8 znaków i zawierać co najmniej 1 wielką literę oraz 1 cyfrę."
"password": "Hasło"
},
"login": "Zaloguj się",
"logout": "Wyloguj się",

View File

@@ -1612,8 +1612,7 @@
"email": "Email",
"firstName": "Nome",
"lastName": "Sobrenome",
"password": "Senha",
"password_requirements": "A senha deve ter pelo menos 8 caracteres e incluir pelo menos 1 letra maiúscula e 1 número."
"password": "Senha"
},
"login": "Login",
"logout": "Sair",

View File

@@ -1612,8 +1612,7 @@
"email": "Email",
"firstName": "Nome",
"lastName": "Sobrenome",
"password": "Senha",
"password_requirements": "A senha deve ter pelo menos 8 caracteres e incluir pelo menos 1 letra maiúscula e 1 número."
"password": "Senha"
},
"login": "Login",
"logout": "Sair",

View File

@@ -1612,8 +1612,7 @@
"email": "Электронная почта",
"firstName": "Имя",
"lastName": "Фамилия",
"password": "Пароль",
"password_requirements": "Пароль должен содержать не менее 8 символов, включая как минимум 1 заглавную букву и 1 цифру."
"password": "Пароль"
},
"login": "Войти",
"logout": "Выход",

View File

@@ -1612,8 +1612,7 @@
"email": "电子邮件",
"firstName": "名字",
"lastName": "姓氏",
"password": "密码",
"password_requirements": "密码必须至少包含8个字符并且至少包含1个大写字母和1个数字。"
"password": "密码"
},
"login": "登录",
"logout": "注销",