Skip to content
Merged
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
36 changes: 35 additions & 1 deletion api/v1alpha1/sandboxset_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ type SandboxSetSpec struct {
// ScaleStrategy indicates the ScaleStrategy that will be employed to
// create and delete Sandboxes in the SandboxSet.
ScaleStrategy SandboxSetScaleStrategy `json:"scaleStrategy,omitempty"`

// UpdateStrategy indicates the strategy that will be employed to
// update Sandboxes in the SandboxSet when the template changes.
// +optional
UpdateStrategy SandboxSetUpdateStrategy `json:"updateStrategy,omitempty"`
}

// SandboxSetScaleStrategy defines strategies for sandboxes scale.
Expand All @@ -80,6 +85,24 @@ type SandboxSetScaleStrategy struct {
MaxUnavailable *intstr.IntOrString `json:"maxUnavailable,omitempty"`
}

// RollingUpdateStrategyType is the type of rolling update strategy.
type RollingUpdateStrategyType string

const (
// RecreateUpdateStrategyType means the controller will delete old pods and create new ones.
RecreateUpdateStrategyType RollingUpdateStrategyType = "Recreate"
)

// SandboxSetUpdateStrategy defines strategies for rolling update.
type SandboxSetUpdateStrategy struct {
// MaxUnavailable is the maximum number or percentage of pods that can be unavailable during the update.
// MaxUnavailable can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%).
// Absolute number is calculated from percentage by rounding down.
// Default is 20%.
// +optional
MaxUnavailable *intstr.IntOrString `json:"maxUnavailable,omitempty"`
}

// SandboxSetStatus defines the observed state of SandboxSet.
type SandboxSetStatus struct {
// observedGeneration is the most recent generation observed for this SandboxSet. It corresponds to the
Expand All @@ -92,9 +115,18 @@ type SandboxSetStatus struct {
// AvailableReplicas is the number of available sandboxes, which are ready to be claimed.
AvailableReplicas int32 `json:"availableReplicas"`

// UpdateRevision is the template-hash calculated from `spec.template`.
// UpdateRevision is the hash label of the ControllerRevision created from `spec.template`.
// It represents the latest desired template version.
UpdateRevision string `json:"updateRevision,omitempty"`

// UpdatedReplicas is the number of sandboxes that have been updated to the UpdateRevision.
Comment thread
furykerry marked this conversation as resolved.
// +optional
UpdatedReplicas int32 `json:"updatedReplicas,omitempty"`

// UpdatedAvailableReplicas is the number of updated sandboxes that are available.
// +optional
UpdatedAvailableReplicas int32 `json:"updatedAvailableReplicas,omitempty"`

// conditions represent the current state of the SandboxSet resource.
// Each condition has a unique type and reflects the status of a specific aspect of the resource.
// The status of each condition is one of True, False, or Unknown.
Expand All @@ -118,6 +150,8 @@ type SandboxSetStatus struct {
// +kubebuilder:storageversion
// +kubebuilder:printcolumn:name="Replicas",type="integer",JSONPath=".status.replicas"
// +kubebuilder:printcolumn:name="Available",type="integer",JSONPath=".status.availableReplicas"
// +kubebuilder:printcolumn:name="UpdatedReplicas",type="integer",JSONPath=".status.updatedReplicas"
// +kubebuilder:printcolumn:name="UpdatedAvailableReplicas",type="integer",JSONPath=".status.updatedAvailableReplicas"
// +kubebuilder:printcolumn:name="UpdateRevision",type="string",JSONPath=".status.updateRevision"
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"

Expand Down
21 changes: 21 additions & 0 deletions api/v1alpha1/zz_generated.deepcopy.go

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

36 changes: 35 additions & 1 deletion config/crd/bases/agents.kruise.io_sandboxsets.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ spec:
- jsonPath: .status.availableReplicas
name: Available
type: integer
- jsonPath: .status.updatedReplicas
name: UpdatedReplicas
type: integer
- jsonPath: .status.updatedAvailableReplicas
name: UpdatedAvailableReplicas
type: integer
- jsonPath: .status.updateRevision
name: UpdateRevision
type: string
Expand Down Expand Up @@ -117,6 +123,22 @@ spec:
required:
- name
type: object
updateStrategy:
Comment thread
furykerry marked this conversation as resolved.
description: |-
UpdateStrategy indicates the strategy that will be employed to
update Sandboxes in the SandboxSet when the template changes.
properties:
maxUnavailable:
anyOf:
- type: integer
- type: string
description: |-
MaxUnavailable is the maximum number or percentage of pods that can be unavailable during the update.
MaxUnavailable can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%).
Absolute number is calculated from percentage by rounding down.
Default is 20%.
x-kubernetes-int-or-string: true
type: object
volumeClaimTemplates:
description: VolumeClaimTemplates is a list of PVC templates to create
for this Sandbox.
Expand Down Expand Up @@ -213,8 +235,20 @@ spec:
duplication for CRDs that do not support structural schemas.
type: string
updateRevision:
description: UpdateRevision is the template-hash calculated from `spec.template`.
description: |-
UpdateRevision is the hash label of the ControllerRevision created from `spec.template`.
It represents the latest desired template version.
type: string
updatedAvailableReplicas:
description: UpdatedAvailableReplicas is the number of updated sandboxes
that are available.
format: int32
type: integer
updatedReplicas:
description: UpdatedReplicas is the number of sandboxes that have
been updated to the UpdateRevision.
format: int32
type: integer
required:
- availableReplicas
- replicas
Expand Down
82 changes: 82 additions & 0 deletions pkg/controller/sandboxset/heap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
Copyright 2025.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package sandboxset

import (
"sort"

agentsv1alpha1 "github.com/openkruise/agents/api/v1alpha1"
)

// oldestFirst is the less function for sorting sandboxes by creation time ascending (oldest first).
func oldestFirst(i, j *agentsv1alpha1.Sandbox) bool {
return i.CreationTimestamp.Before(&j.CreationTimestamp)
}

// findOldestSandboxes finds the k "smallest" sandboxes according to less from the list without
// fully sorting, it creates and returns a new sorted slice. Does not modify the input slice.
// less(i, j) should return true if i should come before j in the result (ascending order).
func findOldestSandboxes(sandboxes []*agentsv1alpha1.Sandbox, k int, less func(i, j *agentsv1alpha1.Sandbox) bool) []*agentsv1alpha1.Sandbox {
if k <= 0 {
return []*agentsv1alpha1.Sandbox{}
}
n := len(sandboxes)
if k >= n {
result := make([]*agentsv1alpha1.Sandbox, n)
copy(result, sandboxes)
sort.SliceStable(result, func(i, j int) bool { return less(result[i], result[j]) })
return result
}

// Use a max-heap of size k (largest at root) to efficiently find the k smallest sandboxes.
result := make([]*agentsv1alpha1.Sandbox, k)
copy(result, sandboxes[:k])
// Build max-heap from first k elements.
for i := k/2 - 1; i >= 0; i-- {
maxHeapify(result, i, k, less)
}
// Replace heap root (largest of k) whenever we find a smaller sandbox.
for i := k; i < n; i++ {
if less(sandboxes[i], result[0]) {
result[0] = sandboxes[i]
maxHeapify(result, 0, k, less)
}
}
sort.SliceStable(result, func(i, j int) bool { return less(result[i], result[j]) })
return result
}

// maxHeapify maintains the max-heap property (largest element at root according to less) iteratively.
func maxHeapify(sandboxes []*agentsv1alpha1.Sandbox, i, heapSize int, less func(i, j *agentsv1alpha1.Sandbox) bool) {
for {
largest := i
left := 2*i + 1
right := 2*i + 2

if left < heapSize && less(sandboxes[largest], sandboxes[left]) {
largest = left
}
if right < heapSize && less(sandboxes[largest], sandboxes[right]) {
largest = right
}
if largest == i {
break
}
sandboxes[i], sandboxes[largest] = sandboxes[largest], sandboxes[i]
i = largest
}
}
Loading
Loading