Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions go/api/config/crd/bases/kagent.dev_agents.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5217,6 +5217,44 @@ spec:
description: NodeSelector restricts the nodes the agent pods
can be scheduled on.
type: object
podDisruptionBudget:
description: |-
PodDisruptionBudget requests a PodDisruptionBudget for the agent pods, limiting how
many may be disrupted by voluntary evictions such as node drains. Omit the field to
create no budget; removing it later deletes the budget. This has no effect when the
agent runs with the Sandbox workload mode, as no Deployment is created in that mode.
properties:
maxUnavailable:
anyOf:
- type: integer
- type: string
description: |-
MaxUnavailable is the number or percentage of pods that may be unavailable.
Mutually exclusive with MinAvailable.
x-kubernetes-int-or-string: true
minAvailable:
anyOf:
- type: integer
- type: string
description: |-
MinAvailable is the number or percentage of pods that must remain available.
Mutually exclusive with MaxUnavailable.
x-kubernetes-int-or-string: true
unhealthyPodEvictionPolicy:
description: |-
UnhealthyPodEvictionPolicy defines when unhealthy pods should be considered for
eviction. AlwaysAllow permits evicting unhealthy pods even once the budget is
exhausted, which stops a crash-looping pod from wedging a node drain.
Defaults to the Kubernetes default of IfHealthyBudget. Requires Kubernetes >= 1.27.
enum:
- IfHealthyBudget
- AlwaysAllow
type: string
type: object
x-kubernetes-validations:
- message: exactly one of minAvailable or maxUnavailable must
be set
rule: has(self.minAvailable) != has(self.maxUnavailable)
podSecurityContext:
description: |-
PodSecurityContext holds pod-level security attributes and common container settings.
Expand Down Expand Up @@ -10795,6 +10833,44 @@ spec:
description: NodeSelector restricts the nodes the agent pods
can be scheduled on.
type: object
podDisruptionBudget:
description: |-
PodDisruptionBudget requests a PodDisruptionBudget for the agent pods, limiting how
many may be disrupted by voluntary evictions such as node drains. Omit the field to
create no budget; removing it later deletes the budget. This has no effect when the
agent runs with the Sandbox workload mode, as no Deployment is created in that mode.
properties:
maxUnavailable:
anyOf:
- type: integer
- type: string
description: |-
MaxUnavailable is the number or percentage of pods that may be unavailable.
Mutually exclusive with MinAvailable.
x-kubernetes-int-or-string: true
minAvailable:
anyOf:
- type: integer
- type: string
description: |-
MinAvailable is the number or percentage of pods that must remain available.
Mutually exclusive with MaxUnavailable.
x-kubernetes-int-or-string: true
unhealthyPodEvictionPolicy:
description: |-
UnhealthyPodEvictionPolicy defines when unhealthy pods should be considered for
eviction. AlwaysAllow permits evicting unhealthy pods even once the budget is
exhausted, which stops a crash-looping pod from wedging a node drain.
Defaults to the Kubernetes default of IfHealthyBudget. Requires Kubernetes >= 1.27.
enum:
- IfHealthyBudget
- AlwaysAllow
type: string
type: object
x-kubernetes-validations:
- message: exactly one of minAvailable or maxUnavailable must
be set
rule: has(self.minAvailable) != has(self.maxUnavailable)
podSecurityContext:
description: |-
PodSecurityContext holds pod-level security attributes and common container settings.
Expand Down
76 changes: 76 additions & 0 deletions go/api/config/crd/bases/kagent.dev_sandboxagents.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2874,6 +2874,44 @@ spec:
description: NodeSelector restricts the nodes the agent pods
can be scheduled on.
type: object
podDisruptionBudget:
description: |-
PodDisruptionBudget requests a PodDisruptionBudget for the agent pods, limiting how
many may be disrupted by voluntary evictions such as node drains. Omit the field to
create no budget; removing it later deletes the budget. This has no effect when the
agent runs with the Sandbox workload mode, as no Deployment is created in that mode.
properties:
maxUnavailable:
anyOf:
- type: integer
- type: string
description: |-
MaxUnavailable is the number or percentage of pods that may be unavailable.
Mutually exclusive with MinAvailable.
x-kubernetes-int-or-string: true
minAvailable:
anyOf:
- type: integer
- type: string
description: |-
MinAvailable is the number or percentage of pods that must remain available.
Mutually exclusive with MaxUnavailable.
x-kubernetes-int-or-string: true
unhealthyPodEvictionPolicy:
description: |-
UnhealthyPodEvictionPolicy defines when unhealthy pods should be considered for
eviction. AlwaysAllow permits evicting unhealthy pods even once the budget is
exhausted, which stops a crash-looping pod from wedging a node drain.
Defaults to the Kubernetes default of IfHealthyBudget. Requires Kubernetes >= 1.27.
enum:
- IfHealthyBudget
- AlwaysAllow
type: string
type: object
x-kubernetes-validations:
- message: exactly one of minAvailable or maxUnavailable must
be set
rule: has(self.minAvailable) != has(self.maxUnavailable)
podSecurityContext:
description: |-
PodSecurityContext holds pod-level security attributes and common container settings.
Expand Down Expand Up @@ -8452,6 +8490,44 @@ spec:
description: NodeSelector restricts the nodes the agent pods
can be scheduled on.
type: object
podDisruptionBudget:
description: |-
PodDisruptionBudget requests a PodDisruptionBudget for the agent pods, limiting how
many may be disrupted by voluntary evictions such as node drains. Omit the field to
create no budget; removing it later deletes the budget. This has no effect when the
agent runs with the Sandbox workload mode, as no Deployment is created in that mode.
properties:
maxUnavailable:
anyOf:
- type: integer
- type: string
description: |-
MaxUnavailable is the number or percentage of pods that may be unavailable.
Mutually exclusive with MinAvailable.
x-kubernetes-int-or-string: true
minAvailable:
anyOf:
- type: integer
- type: string
description: |-
MinAvailable is the number or percentage of pods that must remain available.
Mutually exclusive with MaxUnavailable.
x-kubernetes-int-or-string: true
unhealthyPodEvictionPolicy:
description: |-
UnhealthyPodEvictionPolicy defines when unhealthy pods should be considered for
eviction. AlwaysAllow permits evicting unhealthy pods even once the budget is
exhausted, which stops a crash-looping pod from wedging a node drain.
Defaults to the Kubernetes default of IfHealthyBudget. Requires Kubernetes >= 1.27.
enum:
- IfHealthyBudget
- AlwaysAllow
type: string
type: object
x-kubernetes-validations:
- message: exactly one of minAvailable or maxUnavailable must
be set
rule: has(self.minAvailable) != has(self.maxUnavailable)
podSecurityContext:
description: |-
PodSecurityContext holds pod-level security attributes and common container settings.
Expand Down
34 changes: 34 additions & 0 deletions go/api/v1alpha2/agent_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@ import (
"fmt"

corev1 "k8s.io/api/core/v1"
policyv1 "k8s.io/api/policy/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr"
"sigs.k8s.io/controller-runtime/pkg/client"
)

Expand Down Expand Up @@ -499,6 +501,38 @@ type SharedDeploymentSpec struct {
// Useful for sidecars such as token proxies, log shippers, or security agents.
// +optional
ExtraContainers []corev1.Container `json:"extraContainers,omitempty"`
// PodDisruptionBudget requests a PodDisruptionBudget for the agent pods, limiting how
// many may be disrupted by voluntary evictions such as node drains. Omit the field to
// create no budget; removing it later deletes the budget. This has no effect when the
// agent runs with the Sandbox workload mode, as no Deployment is created in that mode.
// +optional
PodDisruptionBudget *PodDisruptionBudgetSpec `json:"podDisruptionBudget,omitempty"`
}

// PodDisruptionBudgetSpec configures the PodDisruptionBudget created for an agent's pods.
// The selector is always derived from the agent, so only the disruption thresholds are
// configurable here.
//
// Beware that a budget the Deployment can never satisfy blocks voluntary evictions
// entirely, which makes node drains and cluster upgrades hang. In particular, avoid
// setting minAvailable to the replica count.
// +kubebuilder:validation:XValidation:message="exactly one of minAvailable or maxUnavailable must be set",rule="has(self.minAvailable) != has(self.maxUnavailable)"
type PodDisruptionBudgetSpec struct {
// MinAvailable is the number or percentage of pods that must remain available.
// Mutually exclusive with MaxUnavailable.
// +optional
MinAvailable *intstr.IntOrString `json:"minAvailable,omitempty"`
// MaxUnavailable is the number or percentage of pods that may be unavailable.
// Mutually exclusive with MinAvailable.
// +optional
MaxUnavailable *intstr.IntOrString `json:"maxUnavailable,omitempty"`
// UnhealthyPodEvictionPolicy defines when unhealthy pods should be considered for
// eviction. AlwaysAllow permits evicting unhealthy pods even once the budget is
// exhausted, which stops a crash-looping pod from wedging a node drain.
// Defaults to the Kubernetes default of IfHealthyBudget. Requires Kubernetes >= 1.27.
// +optional
// +kubebuilder:validation:Enum=IfHealthyBudget;AlwaysAllow
UnhealthyPodEvictionPolicy *policyv1.UnhealthyPodEvictionPolicyType `json:"unhealthyPodEvictionPolicy,omitempty"`
}

type ServiceAccountConfig struct {
Expand Down
37 changes: 37 additions & 0 deletions go/api/v1alpha2/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions go/core/internal/controller/agent_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ type AgentController struct {
// +kubebuilder:rbac:groups=core,resources=serviceaccounts,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=core,resources=configmaps,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=policy,resources=poddisruptionbudgets,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=agents.x-k8s.io,resources=sandboxes,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=agents.x-k8s.io,resources=sandboxes/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=agents.x-k8s.io,resources=sandboxes/finalizers,verbs=update
Expand Down
1 change: 1 addition & 0 deletions go/core/internal/controller/sandboxagent_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ type SandboxAgentController struct {
// +kubebuilder:rbac:groups=core,resources=serviceaccounts,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=core,resources=configmaps,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=policy,resources=poddisruptionbudgets,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=agents.x-k8s.io,resources=sandboxes,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=agents.x-k8s.io,resources=sandboxes/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=agents.x-k8s.io,resources=sandboxes/finalizers,verbs=update
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"github.com/kagent-dev/kmcp/api/v1alpha1"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
policyv1 "k8s.io/api/policy/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
Expand Down Expand Up @@ -255,6 +256,10 @@ func (r *adkApiTranslator) GetOwnedResourceTypes() []client.Object {
&corev1.Secret{},
&corev1.Service{},
&corev1.ServiceAccount{},
// Optional per agent, but must be listed unconditionally: this set also drives
// the prune pass, so omitting it would orphan the budget of an agent that stops
// requesting one.
&policyv1.PodDisruptionBudget{},
}

for _, plugin := range r.plugins {
Expand Down
7 changes: 7 additions & 0 deletions go/core/internal/controller/translator/agent/deployments.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ type resolvedDeployment struct {
ServiceAccountName *string
ServiceAccountConfig *v1alpha2.ServiceAccountConfig
ExtraContainers []corev1.Container
PodDisruptionBudget *v1alpha2.PodDisruptionBudgetSpec
}

// getDefaultResources sets default resource requirements if not specified
Expand Down Expand Up @@ -269,6 +270,9 @@ func resolveInlineDeployment(agent v1alpha2.AgentObject, mdd *modelDeploymentDat
ServiceAccountName: spec.ServiceAccountName,
ServiceAccountConfig: spec.ServiceAccountConfig,
ExtraContainers: slices.Clone(spec.ExtraContainers),
// Left nil when unset: a nil budget means "create no PodDisruptionBudget",
// which is what lets the reconciler prune one that was previously requested.
PodDisruptionBudget: spec.PodDisruptionBudget.DeepCopy(),
}

// Precedence: agent-level serviceAccountName > global default > auto-created SA (agent name)
Expand Down Expand Up @@ -356,6 +360,9 @@ func resolveByoDeployment(agent v1alpha2.AgentObject) (*resolvedDeployment, erro
ServiceAccountName: spec.ServiceAccountName,
ServiceAccountConfig: spec.ServiceAccountConfig,
ExtraContainers: slices.Clone(spec.ExtraContainers),
// Left nil when unset: a nil budget means "create no PodDisruptionBudget",
// which is what lets the reconciler prune one that was previously requested.
PodDisruptionBudget: spec.PodDisruptionBudget.DeepCopy(),
}

// Precedence: agent-level serviceAccountName > global default > auto-created SA (agent name)
Expand Down
Loading
Loading