diff --git a/backend/.sqlx/query-5d1ec728380ea8baf64df54743e73008ee9a58e6f39a5bf31a2ed099727f5c04.json b/backend/.sqlx/query-5d1ec728380ea8baf64df54743e73008ee9a58e6f39a5bf31a2ed099727f5c04.json new file mode 100644 index 0000000000..8a4fb840b1 --- /dev/null +++ b/backend/.sqlx/query-5d1ec728380ea8baf64df54743e73008ee9a58e6f39a5bf31a2ed099727f5c04.json @@ -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" +} diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index 33d9b4320a..fd7691745b 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -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 diff --git a/backend/windmill-api/src/workers.rs b/backend/windmill-api/src/workers.rs index 4e6828e580..bbc7fa6e2a 100644 --- a/backend/windmill-api/src/workers.rs +++ b/backend/windmill-api/src/workers.rs @@ -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, + Query(tag_query): Query, +) -> JsonResult { + 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> { Json(ALL_TAGS.read().await.clone().into()) } diff --git a/frontend/src/lib/components/JobStatus.svelte b/frontend/src/lib/components/JobStatus.svelte index 241547cbd9..601e559afe 100644 --- a/frontend/src/lib/components/JobStatus.svelte +++ b/frontend/src/lib/components/JobStatus.svelte @@ -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 @@ {:else if job && 'running' in job} -
+
Queued +
{:else} diff --git a/frontend/src/lib/components/LogViewer.svelte b/frontend/src/lib/components/LogViewer.svelte index 0cc5345391..be5ee8d1c0 100644 --- a/frontend/src/lib/components/LogViewer.svelte +++ b/frontend/src/lib/components/LogViewer.svelte @@ -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 @@
{#if isLoading} -
+
{#if tag} -
tag: {tag}
+
+
tag: {tag}
+ +
{/if}
{:else if duration} diff --git a/frontend/src/lib/components/Popover.svelte b/frontend/src/lib/components/Popover.svelte index 237b468ab7..47404165d0 100644 --- a/frontend/src/lib/components/Popover.svelte +++ b/frontend/src/lib/components/Popover.svelte @@ -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] } }, diff --git a/frontend/src/lib/components/runs/NoWorkerWithTagWarning.svelte b/frontend/src/lib/components/runs/NoWorkerWithTagWarning.svelte new file mode 100644 index 0000000000..a85d8e724f --- /dev/null +++ b/frontend/src/lib/components/runs/NoWorkerWithTagWarning.svelte @@ -0,0 +1,40 @@ + + +{#if noWorkerWithTag} + + + + No worker with tag {tag} is currently running. + + +{/if}