mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-22 08:02:19 +00:00
feat: no worker with tag warning (#2803)
* feat: no worker with tag warning * feat: better design and flow jobs * fix: sqlx * fix: clear timeout on destroy
This commit is contained in:
+22
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT EXISTS(SELECT 1 FROM worker_ping WHERE custom_tags @> $1 AND ping_at > now() - interval '1 minute')",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "exists",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"TextArray"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "5d1ec728380ea8baf64df54743e73008ee9a58e6f39a5bf31a2ed099727f5c04"
|
||||
}
|
||||
@@ -6028,6 +6028,26 @@ paths:
|
||||
items:
|
||||
$ref: "#/components/schemas/WorkerPing"
|
||||
|
||||
/workers/exists_worker_with_tag:
|
||||
get:
|
||||
summary: exists worker with tag
|
||||
operationId: existsWorkerWithTag
|
||||
tags:
|
||||
- worker
|
||||
parameters:
|
||||
- name: tag
|
||||
in: query
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: whether a worker with the tag exists
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: boolean
|
||||
|
||||
/configs/list_worker_groups:
|
||||
get:
|
||||
summary: list worker groups
|
||||
|
||||
@@ -26,6 +26,7 @@ use crate::db::ApiAuthed;
|
||||
pub fn global_service() -> Router {
|
||||
Router::new()
|
||||
.route("/list", get(list_worker_pings))
|
||||
.route("/exists_worker_with_tag", get(exists_worker_with_tag))
|
||||
.route("/custom_tags", get(get_custom_tags))
|
||||
}
|
||||
|
||||
@@ -68,6 +69,27 @@ async fn list_worker_pings(
|
||||
Ok(Json(rows))
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct TagQuery {
|
||||
tag: String,
|
||||
}
|
||||
|
||||
async fn exists_worker_with_tag(
|
||||
authed: ApiAuthed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Query(tag_query): Query<TagQuery>,
|
||||
) -> JsonResult<bool> {
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
let row = sqlx::query!(
|
||||
"SELECT EXISTS(SELECT 1 FROM worker_ping WHERE custom_tags @> $1 AND ping_at > now() - interval '1 minute')",
|
||||
&[tag_query.tag]
|
||||
)
|
||||
.fetch_one(&mut *tx)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
Ok(Json(row.exists.unwrap_or(false)))
|
||||
}
|
||||
|
||||
async fn get_custom_tags() -> Json<Vec<String>> {
|
||||
Json(ALL_TAGS.read().await.clone().into())
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import { forLater } from '$lib/forLater'
|
||||
import DurationMs from './DurationMs.svelte'
|
||||
import { Calendar, CheckCircle2, Circle, Clock, XCircle } from 'lucide-svelte'
|
||||
import NoWorkerWithTagWarning from './runs/NoWorkerWithTagWarning.svelte'
|
||||
|
||||
const SMALL_ICON_SIZE = 12
|
||||
|
||||
@@ -39,8 +40,9 @@
|
||||
</Badge>
|
||||
</div>
|
||||
{:else if job && 'running' in job}
|
||||
<div>
|
||||
<div class="flex flex-row gap-1 items-center">
|
||||
<Badge icon={{ icon: Clock, position: 'left' }}>Queued</Badge>
|
||||
<NoWorkerWithTagWarning tag={job.tag} />
|
||||
</div>
|
||||
{:else}
|
||||
<Circle size={SMALL_ICON_SIZE} class="text-gray-200" />
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import { copyToClipboard } from '$lib/utils'
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import AnsiUp from 'ansi_up'
|
||||
import NoWorkerWithTagWarning from './runs/NoWorkerWithTagWarning.svelte'
|
||||
|
||||
export let content: string | undefined
|
||||
export let isLoading: boolean
|
||||
@@ -89,10 +90,13 @@
|
||||
</div>
|
||||
</div>
|
||||
{#if isLoading}
|
||||
<div class="flex gap-2 absolute top-2 left-2 items-center">
|
||||
<div class="flex gap-2 absolute top-2 left-2 items-center z-10">
|
||||
<Loader2 class="animate-spin" />
|
||||
{#if tag}
|
||||
<div class="text-secondary {small ? '!text-2xs' : '!text-xs'}">tag: {tag}</div>
|
||||
<div class="flex flex-row items-center gap-1">
|
||||
<div class="text-secondary {small ? '!text-2xs' : '!text-xs'}">tag: {tag}</div>
|
||||
<NoWorkerWithTagWarning {tag} />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{:else if duration}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import Portal from 'svelte-portal'
|
||||
import { ExternalLink } from 'lucide-svelte'
|
||||
|
||||
export let placement: PopoverPlacement = 'auto'
|
||||
export let placement: PopoverPlacement = 'bottom-end'
|
||||
export let notClickable = false
|
||||
export let popupClass = ''
|
||||
export let disablePopup = false
|
||||
@@ -16,7 +16,7 @@
|
||||
const [popperRef, popperContent] = createPopperActions({ placement })
|
||||
|
||||
const popperOptions: PopperOptions<{}> = {
|
||||
placement: 'bottom-end',
|
||||
placement,
|
||||
strategy: 'fixed',
|
||||
modifiers: [
|
||||
{ name: 'offset', options: { offset: [8, 8] } },
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
<script lang="ts">
|
||||
import { WorkerService } from '$lib/gen'
|
||||
import { AlertTriangle } from 'lucide-svelte'
|
||||
import Popover from '../Popover.svelte'
|
||||
import { onDestroy } from 'svelte'
|
||||
export let tag: string
|
||||
|
||||
let noWorkerWithTag = false
|
||||
|
||||
let timeout: NodeJS.Timeout | undefined = undefined
|
||||
|
||||
async function lookForTag(): Promise<void> {
|
||||
try {
|
||||
const existsWorkerWithTag = await WorkerService.existsWorkerWithTag({ tag })
|
||||
noWorkerWithTag = !existsWorkerWithTag
|
||||
timeout = setTimeout(() => {
|
||||
lookForTag()
|
||||
}, 1000)
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
|
||||
lookForTag()
|
||||
|
||||
onDestroy(() => {
|
||||
if (timeout) {
|
||||
clearTimeout(timeout)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if noWorkerWithTag}
|
||||
<Popover notClickable placement="top">
|
||||
<AlertTriangle size={16} class="text-yellow-500" />
|
||||
<svelte:fragment slot="text">
|
||||
No worker with tag <b>{tag}</b> is currently running.
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
{/if}
|
||||
Reference in New Issue
Block a user