fix: make the autoscaling Test scaling button run the integration (#11972)

* fix: make the autoscaling Test scaling button run the integration

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore: bump ee ref

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: bound the test scaling worker input at zero

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore: update ee-repo-ref to a2a23aebcf91985778fb632a415a6990d022f7c8

This commit updates the EE repository reference after PR #917 was merged in windmill-ee-private.

Previous ee-repo-ref: c9e8c8853e2042a11797e322e1e4bb4be241380c

New ee-repo-ref: a2a23aebcf91985778fb632a415a6990d022f7c8

Automated by sync-ee-ref workflow.

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
This commit is contained in:
Ruben Fiszel
2026-10-23 14:43:36 +02:00
committed by GitHub
co-authored by Claude Opus 5.5 windmill-internal-app[bot]
parent fb193d7a81
commit 72c6b10821
5 changed files with 159 additions and 17 deletions
+1 -1
View File
@@ -1 +1 @@
5cfc0dc6760ea7f631fc98ea1edd39adfc55bf11
a2a23aebcf91985778fb632a415a6990d022f7c8
+43
View File
@@ -35,6 +35,7 @@ pub fn global_service() -> Router {
"/list_autoscaling_events/{worker_group}",
get(list_autoscaling_events),
)
.route("/test_autoscaling/{worker_group}", post(test_autoscaling))
.route(
"/native_kubernetes_autoscaling_healthcheck",
get(native_kubernetes_autoscaling_healthcheck),
@@ -419,6 +420,48 @@ async fn native_kubernetes_autoscaling_healthcheck() -> Result<(), error::Error>
))
}
#[derive(Serialize)]
struct TestAutoscalingResponse {
job_id: Option<String>,
}
#[cfg(all(feature = "enterprise", feature = "private"))]
async fn test_autoscaling(
authed: ApiAuthed,
Extension(db): Extension<DB>,
Path(worker_group): Path<String>,
Json(test): Json<windmill_autoscaling::autoscaling_ee::TestAutoscaling>,
) -> error::JsonResult<TestAutoscalingResponse> {
require_devops_role(&db, &authed).await?;
audit_log(
&db,
&authed,
"worker_config.test_autoscaling",
ActionKind::Execute,
"global",
Some(&worker_group),
None,
)
.await?;
let job_id = windmill_autoscaling::autoscaling_ee::test_autoscaling(
&db,
&worker_group,
test,
&authed.email,
)
.await?;
Ok(Json(TestAutoscalingResponse {
job_id: job_id.map(|id| id.to_string()),
}))
}
#[cfg(not(all(feature = "enterprise", feature = "private")))]
async fn test_autoscaling() -> error::JsonResult<TestAutoscalingResponse> {
Err(error::Error::BadRequest(
"Autoscaling available only in the enterprise version".to_string(),
))
}
async fn list_available_python_versions() -> error::JsonResult<Vec<String>> {
#[cfg(not(all(feature = "python", feature = "run_inline")))]
return Err(error::Error::BadRequest(
+50
View File
@@ -24649,6 +24649,56 @@ paths:
items:
$ref: "#/components/schemas/AutoscalingEvent"
/configs/test_autoscaling/{worker_group}:
post:
summary: Run an autoscaling integration once for a worker group, without recording an autoscaling event
operationId: testAutoscaling
tags:
- config
parameters:
- name: worker_group
in: path
required: true
schema:
type: string
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
desired_workers:
type: integer
minimum: 0
integration:
description: The integration to run, as in the worker group's autoscaling config
type: object
properties:
type:
type: string
enum: [script, kubernetes, dryrun]
path:
type: string
tag:
type: string
required:
- type
required:
- desired_workers
- integration
responses:
"200":
description: The job queued for a script integration
content:
application/json:
schema:
type: object
properties:
job_id:
type: string
nullable: true
/configs/native_kubernetes_autoscaling_healthcheck:
get:
summary: Check Kubernetes autoscaling health for a worker group
@@ -16,8 +16,12 @@
import ScriptPicker from './ScriptPicker.svelte'
import { sendUserToast } from '$lib/toast'
import { untrack } from 'svelte'
import ConfirmationModal from './common/confirmationModal/ConfirmationModal.svelte'
import { createAsyncConfirmationModal } from './common/confirmationModal/asyncConfirmationModal.svelte'
import Portal from './Portal.svelte'
interface Props {
worker_group: string
config: AutoscalingConfig | undefined
worker_tags: string[] | undefined
/** Whether the draft config gives the group any tags (worker_tags or dedicated workers). */
@@ -29,6 +33,7 @@
}
let {
worker_group,
config = $bindable(),
worker_tags,
group_has_tags,
@@ -66,6 +71,54 @@
}
}
const confirmationModal = createAsyncConfirmationModal()
async function testScaling() {
const integration = config?.integration
if (!integration) return
if (integration.type === 'script' && !integration['path']) {
sendUserToast('Please select a script before testing scaling', true)
return
}
const desired_workers = test_input
let job_id: string | null | undefined
let failed = false
const confirmed = await confirmationModal.ask({
title: `Scale ${worker_group} to ${desired_workers} workers?`,
confirmationText: 'Scale now',
type: 'reload',
children:
integration.type === 'kubernetes'
? `This sets the worker group's replica count to ${desired_workers} now. If autoscaling is enabled, it adjusts the count again on its next pass.`
: `This runs the integration script once with desired_workers set to ${desired_workers}, and it scales the group if the script does. If autoscaling is enabled, it adjusts the count again on its next pass.`,
onConfirmed: async () => {
try {
job_id = (
await ConfigService.testAutoscaling({
workerGroup: worker_group,
requestBody: { desired_workers, integration: $state.snapshot(integration) }
})
).job_id
} catch (e: any) {
failed = true
sendUserToast('Test scaling failed', true, [], e?.body ?? e?.message)
}
}
})
if (!confirmed || failed) return
if (job_id) {
const id = job_id
sendUserToast('Autoscaling script queued in the admins workspace', false, [
{
label: 'See job',
callback: () => window.open('/run/' + id + '?workspace=admins', '_blank')
}
])
} else {
sendUserToast(`Scaled ${worker_group} to ${desired_workers} workers`)
}
}
let noTagsToCount = $derived(!!config?.enabled && !group_has_tags && !config.custom_tags?.length)
let collapsed: boolean = $state(untrack(() => !noTagsToCount))
@@ -266,22 +319,11 @@
/>
<div class="flex flex-row gap-2 justify-end mt-4">
<Button
variant="default"
unifiedSize="md"
onclick={() => {
if (!config?.integration?.['path']) {
sendUserToast('Please select a script before testing scaling', true)
return
}
if (!config?.integration?.['tag']) {
sendUserToast('Please select a custom tag before testing scaling', true)
return
}
}}>Test scaling</Button
<Button variant="default" unifiedSize="md" {disabled} onclick={testScaling}
>Test scaling</Button
>
<div class="flex text-xs flex-row gap-2 items-center">
<input class="!w-16" type="number" bind:value={test_input} />
<input class="!w-16" type="number" min="0" bind:value={test_input} />
workers
</div>
</div>
@@ -317,9 +359,11 @@
</div>
<div class="flex flex-row gap-2 justify-end">
<Button unifiedSize="md" variant="default">Test scaling</Button>
<Button unifiedSize="md" variant="default" {disabled} onclick={testScaling}
>Test scaling</Button
>
<div class="flex text-xs flex-row gap-2 items-center">
<input class="!w-16" type="number" bind:value={test_input} />
<input class="!w-16" type="number" min="0" bind:value={test_input} />
workers
</div>
</div>
@@ -547,3 +591,7 @@
</Label>
</Section>
</Section>
<Portal>
<ConfirmationModal {...confirmationModal.props} />
</Portal>
@@ -1078,6 +1078,7 @@
<div class="mt-8"></div>
<AutoscalingConfigEditor
worker_group={name}
worker_tags={config?.worker_tags}
group_has_tags={nconfig.worker_tags !== undefined
? nconfig.worker_tags.length > 0