Skip to content

Commit cd4c40c

Browse files
jopemachineclaude
andcommitted
feat(BA-4631): Add update_deployment_policy GQL mutation
Add Strawberry GraphQL mutation for updating deployment policies, internally using the existing upsert mechanism. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent da21da6 commit cd4c40c

6 files changed

Lines changed: 111 additions & 0 deletions

File tree

src/ai/backend/manager/api/gql/deployment/__init__.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040
routes,
4141
sync_replicas,
4242
update_auto_scaling_rule,
43+
# Policy
44+
update_deployment_policy,
4345
update_model_deployment,
4446
update_route_traffic_status,
4547
)
@@ -144,6 +146,9 @@
144146
UpdateAutoScalingRulePayload,
145147
UpdateDeploymentInput,
146148
UpdateDeploymentPayload,
149+
# Policy (mutation types)
150+
UpdateDeploymentPolicyInputGQL,
151+
UpdateDeploymentPolicyPayloadGQL,
147152
UpdateRouteTrafficStatusInputGQL,
148153
UpdateRouteTrafficStatusPayloadGQL,
149154
get_route_pagination_spec,
@@ -204,6 +209,8 @@
204209
"DeploymentStrategyTypeGQL",
205210
"RollingUpdateConfigInputGQL",
206211
"RollingUpdateStrategySpecGQL",
212+
"UpdateDeploymentPolicyInputGQL",
213+
"UpdateDeploymentPolicyPayloadGQL",
207214
# Replica Types
208215
"ActivenessStatus",
209216
"LivenessStatus",
@@ -267,6 +274,8 @@
267274
"deployments",
268275
"sync_replicas",
269276
"update_model_deployment",
277+
# Resolvers - Policy
278+
"update_deployment_policy",
270279
# Resolvers - Replica
271280
"replica",
272281
"replica_status_changed",

src/ai/backend/manager/api/gql/deployment/resolver/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
sync_replicas,
2222
update_model_deployment,
2323
)
24+
from .policy import (
25+
update_deployment_policy,
26+
)
2427
from .replica import (
2528
replica,
2629
replica_status_changed,
@@ -55,6 +58,8 @@
5558
"delete_model_deployment",
5659
"sync_replicas",
5760
"deployment_status_changed",
61+
# Policy
62+
"update_deployment_policy",
5863
# Replica
5964
"replicas",
6065
"replica",
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
"""Deployment policy resolver functions."""
2+
3+
from __future__ import annotations
4+
5+
from uuid import UUID
6+
7+
import strawberry
8+
from strawberry import Info
9+
10+
from ai.backend.manager.api.gql.deployment.types.policy import (
11+
DeploymentPolicyGQL,
12+
UpdateDeploymentPolicyInputGQL,
13+
UpdateDeploymentPolicyPayloadGQL,
14+
)
15+
from ai.backend.manager.api.gql.types import StrawberryGQLContext
16+
from ai.backend.manager.services.deployment.actions.deployment_policy.upsert_deployment_policy import (
17+
UpsertDeploymentPolicyAction,
18+
)
19+
20+
21+
@strawberry.mutation(description="Added in 26.4.0") # type: ignore[misc]
22+
async def update_deployment_policy(
23+
input: UpdateDeploymentPolicyInputGQL,
24+
info: Info[StrawberryGQLContext],
25+
) -> UpdateDeploymentPolicyPayloadGQL:
26+
"""Update (upsert) a deployment policy for a deployment."""
27+
deployment_uuid = UUID(str(input.deployment_id))
28+
upserter = input.to_upserter(deployment_uuid)
29+
30+
processor = info.context.processors.deployment
31+
result = await processor.upsert_deployment_policy.wait_for_complete(
32+
UpsertDeploymentPolicyAction(upserter=upserter)
33+
)
34+
35+
return UpdateDeploymentPolicyPayloadGQL(
36+
deployment_policy=DeploymentPolicyGQL.from_data(result.data),
37+
created=result.created,
38+
)

src/ai/backend/manager/api/gql/deployment/types/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@
5757
DeploymentStrategyTypeGQL,
5858
RollingUpdateConfigInputGQL,
5959
RollingUpdateStrategySpecGQL,
60+
UpdateDeploymentPolicyInputGQL,
61+
UpdateDeploymentPolicyPayloadGQL,
6062
)
6163
from .replica import (
6264
ActivenessStatus,
@@ -166,6 +168,8 @@
166168
"DeploymentStrategyTypeGQL",
167169
"RollingUpdateConfigInputGQL",
168170
"RollingUpdateStrategySpecGQL",
171+
"UpdateDeploymentPolicyInputGQL",
172+
"UpdateDeploymentPolicyPayloadGQL",
169173
# Replica
170174
"ActivenessStatus",
171175
"LivenessStatus",

src/ai/backend/manager/api/gql/deployment/types/policy.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@
44

55
from datetime import datetime
66
from typing import Self
7+
from uuid import UUID
78

89
import strawberry
910
from strawberry import ID
1011
from strawberry.relay import Node, NodeID
1112

1213
from ai.backend.common.data.model_deployment.types import DeploymentStrategy
1314
from ai.backend.manager.data.deployment.types import DeploymentPolicyData
15+
from ai.backend.manager.data.deployment.upserter import DeploymentPolicyUpserter
1416
from ai.backend.manager.errors.deployment import InvalidDeploymentStrategySpec
1517
from ai.backend.manager.models.deployment_policy import BlueGreenSpec, RollingUpdateSpec
1618

@@ -129,3 +131,54 @@ def to_spec(self) -> BlueGreenSpec:
129131
auto_promote=self.auto_promote,
130132
promote_delay_seconds=self.promote_delay_seconds,
131133
)
134+
135+
136+
# ========== Mutation Input/Payload Types ==========
137+
138+
139+
@strawberry.input(
140+
name="UpdateDeploymentPolicyInput",
141+
description="Added in 26.3.0. Input for updating a deployment policy. Internally upserts the policy.",
142+
)
143+
class UpdateDeploymentPolicyInputGQL:
144+
deployment_id: ID
145+
strategy: DeploymentStrategyTypeGQL
146+
rollback_on_failure: bool = False
147+
rolling_update: RollingUpdateConfigInputGQL | None = None
148+
blue_green: BlueGreenConfigInputGQL | None = None
149+
150+
def to_upserter(self, deployment_uuid: UUID) -> DeploymentPolicyUpserter:
151+
"""Convert to DeploymentPolicyUpserter for the service layer."""
152+
from ai.backend.manager.errors.api import InvalidAPIParameters
153+
154+
strategy = DeploymentStrategy(self.strategy.value)
155+
strategy_spec: RollingUpdateSpec | BlueGreenSpec
156+
match strategy:
157+
case DeploymentStrategy.ROLLING:
158+
if self.rolling_update is None:
159+
strategy_spec = RollingUpdateSpec(max_surge=1, max_unavailable=0)
160+
else:
161+
strategy_spec = self.rolling_update.to_spec()
162+
case DeploymentStrategy.BLUE_GREEN:
163+
if self.blue_green is None:
164+
strategy_spec = BlueGreenSpec(auto_promote=False, promote_delay_seconds=0)
165+
else:
166+
strategy_spec = self.blue_green.to_spec()
167+
case _:
168+
raise InvalidAPIParameters(f"Unsupported deployment strategy: {strategy}")
169+
170+
return DeploymentPolicyUpserter(
171+
deployment_id=deployment_uuid,
172+
strategy=strategy,
173+
strategy_spec=strategy_spec,
174+
rollback_on_failure=self.rollback_on_failure,
175+
)
176+
177+
178+
@strawberry.type(
179+
name="UpdateDeploymentPolicyPayload",
180+
description="Added in 26.3.0. Result of updating a deployment policy.",
181+
)
182+
class UpdateDeploymentPolicyPayloadGQL:
183+
deployment_policy: DeploymentPolicyGQL
184+
created: bool

src/ai/backend/manager/api/gql/schema.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
routes,
7777
sync_replicas,
7878
update_auto_scaling_rule,
79+
update_deployment_policy,
7980
update_model_deployment,
8081
update_route_traffic_status,
8182
)
@@ -429,6 +430,7 @@ class Mutation:
429430
delete_model_deployment = delete_model_deployment
430431
sync_replicas = sync_replicas
431432
add_model_revision = add_model_revision
433+
update_deployment_policy = update_deployment_policy
432434
# Notification - Admin APIs
433435
admin_create_notification_channel = admin_create_notification_channel
434436
admin_update_notification_channel = admin_update_notification_channel

0 commit comments

Comments
 (0)