feat(scheduling): node tags + service placement_tags affinity (#58)

Operator-set node labels (node_agents.tags) gate which nodes a service
may schedule onto: services.placement_tags requires every listed tag.
auto and spread filter eligible agents by tags; tags are re-evaluated
each reconcile so retagging a node re-spreads replicas. Pin + tags and
local + tags rejected; empty array clears the requirement.

Surfaces: --placement-tags on services create/update, nodes update
--tags, MCP args, OpenAPI, node-detail tag editor, service-detail
placement-tags input.

Generated with [Devin](https://devin.ai)

Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Tomáš DvořákandDevin authored and GitHub committed 2026-10-09 18:11:19 +02:00
1 parent 9de586f177
commit 4458de56b0
23 files changed
+475 -37

No files matched your search

@@ -20,6 +20,10 @@ type NodeRunner interface {
// ReconcileSpread distributes the service's replicas across every online,
// schedulable agent — replica i lands on agents[i % len].
ReconcileSpread(ctx context.Context, spec RuntimeSpec) (*RuntimeState, error)
// ReconcileAuto picks the least-loaded online agent carrying every tag in
// spec.PlacementTags and reconciles all replicas onto it. Re-resolved on
// every reconcile — the service follows the tag, not a stored node.
ReconcileAuto(ctx context.Context, spec RuntimeSpec) (*RuntimeState, error)
// ControlService fans a lifecycle action ("start"|"stop"|"restart"|"remove")
// out to every replica of the service on the agent.
ControlService(ctx context.Context, serviceID, agentID, action string) error