This is an automated email from the ASF dual-hosted git repository.
pierrejeambrun pushed a commit to branch v3-1-test
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/v3-1-test by this push:
new 788ed214751 [v3-1-test] Pool API improve slots validation (#61071)
(#61114)
788ed214751 is described below
commit 788ed214751785c78b9c5f768b89cae0a9f320d8
Author: github-actions[bot]
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Tue Jan 27 14:12:36 2026 +0100
[v3-1-test] Pool API improve slots validation (#61071) (#61114)
(cherry picked from commit 4a0364a632a93e5daa72afcebd13f2c1a5734289)
Co-authored-by: Pierre Jeambrun <[email protected]>
---
.../api_fastapi/core_api/datamodels/pools.py | 6 +++---
.../core_api/openapi/v2-rest-api-generated.yaml | 3 +++
.../airflow/ui/openapi-gen/requests/schemas.gen.ts | 5 ++++-
.../core_api/routes/public/test_pools.py | 21 ++++++++++++++++++++-
.../src/airflowctl/api/datamodels/generated.py | 10 +++++++---
5 files changed, 37 insertions(+), 8 deletions(-)
diff --git a/airflow-core/src/airflow/api_fastapi/core_api/datamodels/pools.py
b/airflow-core/src/airflow/api_fastapi/core_api/datamodels/pools.py
index 34cf4cb9825..cde5746b42b 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/datamodels/pools.py
+++ b/airflow-core/src/airflow/api_fastapi/core_api/datamodels/pools.py
@@ -20,7 +20,7 @@ from __future__ import annotations
from collections.abc import Callable
from typing import Annotated
-from pydantic import BeforeValidator, Field
+from pydantic import BeforeValidator, Field, PositiveInt
from airflow.api_fastapi.core_api.base import BaseModel, StrictBaseModel
@@ -38,7 +38,7 @@ class BasePool(BaseModel):
"""Base serializer for Pool."""
pool: str = Field(serialization_alias="name")
- slots: int
+ slots: PositiveInt
description: str | None = Field(default=None)
include_deferred: bool
@@ -65,7 +65,7 @@ class PoolPatchBody(StrictBaseModel):
"""Pool serializer for patch bodies."""
name: str | None = Field(default=None, alias="pool")
- slots: int | None = None
+ slots: PositiveInt | None = None
description: str | None = None
include_deferred: bool | None = None
diff --git
a/airflow-core/src/airflow/api_fastapi/core_api/openapi/v2-rest-api-generated.yaml
b/airflow-core/src/airflow/api_fastapi/core_api/openapi/v2-rest-api-generated.yaml
index 45f38707a6e..38db3d22848 100644
---
a/airflow-core/src/airflow/api_fastapi/core_api/openapi/v2-rest-api-generated.yaml
+++
b/airflow-core/src/airflow/api_fastapi/core_api/openapi/v2-rest-api-generated.yaml
@@ -11433,6 +11433,7 @@ components:
title: Name
slots:
type: integer
+ exclusiveMinimum: 0.0
title: Slots
description:
anyOf:
@@ -11476,6 +11477,7 @@ components:
slots:
anyOf:
- type: integer
+ exclusiveMinimum: 0.0
- type: 'null'
title: Slots
description:
@@ -11499,6 +11501,7 @@ components:
title: Name
slots:
type: integer
+ exclusiveMinimum: 0.0
title: Slots
description:
anyOf:
diff --git a/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts
b/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts
index ab7aa4942f7..7366d970a68 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts
@@ -4248,6 +4248,7 @@ export const $PoolBody = {
},
slots: {
type: 'integer',
+ exclusiveMinimum: 0,
title: 'Slots'
},
description: {
@@ -4310,7 +4311,8 @@ export const $PoolPatchBody = {
slots: {
anyOf: [
{
- type: 'integer'
+ type: 'integer',
+ exclusiveMinimum: 0
},
{
type: 'null'
@@ -4355,6 +4357,7 @@ export const $PoolResponse = {
},
slots: {
type: 'integer',
+ exclusiveMinimum: 0,
title: 'Slots'
},
description: {
diff --git
a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_pools.py
b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_pools.py
index a444af70f70..2abd7e23835 100644
--- a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_pools.py
+++ b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_pools.py
@@ -259,6 +259,24 @@ class TestPatchPool(TestPoolsEndpoint):
],
},
),
+ # Negative slot number
+ (
+ POOL1_NAME,
+ {},
+ {"slots": -10},
+ 422,
+ {
+ "detail": [
+ {
+ "ctx": {"gt": 0},
+ "input": -10,
+ "loc": ["body", "slots"],
+ "msg": "Input should be greater than 0",
+ "type": "greater_than",
+ },
+ ],
+ },
+ ),
# Partial body on default_pool
(
Pool.DEFAULT_POOL_NAME,
@@ -335,7 +353,8 @@ class TestPatchPool(TestPoolsEndpoint):
if response.status_code == 422:
for error in body["detail"]:
# pydantic version can vary in tests (lower constraints), we
do not assert the url.
- del error["url"]
+ if "url" in error:
+ del error["url"]
assert body == expected_response
if response.status_code == 200:
diff --git a/airflow-ctl/src/airflowctl/api/datamodels/generated.py
b/airflow-ctl/src/airflowctl/api/datamodels/generated.py
index f546515584a..65364faa318 100644
--- a/airflow-ctl/src/airflowctl/api/datamodels/generated.py
+++ b/airflow-ctl/src/airflowctl/api/datamodels/generated.py
@@ -614,11 +614,15 @@ class PoolBody(BaseModel):
extra="forbid",
)
name: Annotated[str, Field(max_length=256, title="Name")]
- slots: Annotated[int, Field(title="Slots")]
+ slots: Annotated[int, Field(gt=0, title="Slots")]
description: Annotated[str | None, Field(title="Description")] = None
include_deferred: Annotated[bool | None, Field(title="Include Deferred")]
= False
+class Slots(RootModel[int]):
+ root: Annotated[int, Field(gt=0, title="Slots")]
+
+
class PoolPatchBody(BaseModel):
"""
Pool serializer for patch bodies.
@@ -628,7 +632,7 @@ class PoolPatchBody(BaseModel):
extra="forbid",
)
pool: Annotated[str | None, Field(title="Pool")] = None
- slots: Annotated[int | None, Field(title="Slots")] = None
+ slots: Annotated[Slots | None, Field(title="Slots")] = None
description: Annotated[str | None, Field(title="Description")] = None
include_deferred: Annotated[bool | None, Field(title="Include Deferred")]
= None
@@ -639,7 +643,7 @@ class PoolResponse(BaseModel):
"""
name: Annotated[str, Field(title="Name")]
- slots: Annotated[int, Field(title="Slots")]
+ slots: Annotated[int, Field(gt=0, title="Slots")]
description: Annotated[str | None, Field(title="Description")] = None
include_deferred: Annotated[bool, Field(title="Include Deferred")]
occupied_slots: Annotated[int, Field(title="Occupied Slots")]