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
12 changes: 12 additions & 0 deletions apis/placement/v1beta1/stageupdate_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package v1beta1
import (
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
"sigs.k8s.io/controller-runtime/pkg/client"

"github.com/kubefleet-dev/kubefleet/apis"
Expand Down Expand Up @@ -275,6 +276,17 @@ type StageConfig struct {
// +kubebuilder:validation:Optional
SortingLabelKey *string `json:"sortingLabelKey,omitempty"`

// MaxConcurrency specifies the maximum number of clusters that can be updated concurrently within this stage.
// Value can be an absolute number (ex: 5) or a percentage of the total clusters in the stage (ex: 50%).
// Fractional results are rounded down. A minimum of 1 update is enforced.
// If not specified, all clusters in the stage are updated sequentially (effectively maxConcurrency = 1).
// Defaults to 1.
// +kubebuilder:default=1
// +kubebuilder:validation:XIntOrString
// +kubebuilder:validation:Pattern="^((100|[0-9]{1,2})%|[0-9]+)$"
// +kubebuilder:validation:Optional
MaxConcurrency *intstr.IntOrString `json:"maxConcurrency,omitempty"`

// The collection of tasks that each stage needs to complete successfully before moving to the next stage.
// Each task is executed in parallel and there cannot be more than one task of the same type.
// +kubebuilder:validation:MaxItems=2
Expand Down
7 changes: 6 additions & 1 deletion apis/placement/v1beta1/zz_generated.deepcopy.go

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

Original file line number Diff line number Diff line change
Expand Up @@ -1871,6 +1871,19 @@ spec:
type: object
type: object
x-kubernetes-map-type: atomic
maxConcurrency:
anyOf:
- type: integer
- type: string
default: 1
description: |-
MaxConcurrency specifies the maximum number of clusters that can be updated concurrently within this stage.
Value can be an absolute number (ex: 5) or a percentage of the total clusters in the stage (ex: 50%).
Absolute number is calculated from percentage by rounding up.
If not specified, all clusters in the stage are updated sequentially (effectively maxConcurrency = 1).
Defaults to 1.
pattern: ^((100|[0-9]{1,2})%|[0-9]+)$
x-kubernetes-int-or-string: true
name:
description: The name of the stage. This MUST be unique
within the same StagedUpdateStrategy.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,19 @@ spec:
type: object
type: object
x-kubernetes-map-type: atomic
maxConcurrency:
anyOf:
- type: integer
- type: string
default: 1
description: |-
MaxConcurrency specifies the maximum number of clusters that can be updated concurrently within this stage.
Value can be an absolute number (ex: 5) or a percentage of the total clusters in the stage (ex: 50%).
Absolute number is calculated from percentage by rounding up.
If not specified, all clusters in the stage are updated sequentially (effectively maxConcurrency = 1).
Defaults to 1.
pattern: ^((100|[0-9]{1,2})%|[0-9]+)$
x-kubernetes-int-or-string: true
name:
description: The name of the stage. This MUST be unique within
the same StagedUpdateStrategy.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -790,6 +790,19 @@ spec:
type: object
type: object
x-kubernetes-map-type: atomic
maxConcurrency:
anyOf:
- type: integer
- type: string
default: 1
description: |-
MaxConcurrency specifies the maximum number of clusters that can be updated concurrently within this stage.
Value can be an absolute number (ex: 5) or a percentage of the total clusters in the stage (ex: 50%).
Absolute number is calculated from percentage by rounding up.
If not specified, all clusters in the stage are updated sequentially (effectively maxConcurrency = 1).
Defaults to 1.
pattern: ^((100|[0-9]{1,2})%|[0-9]+)$
x-kubernetes-int-or-string: true
name:
description: The name of the stage. This MUST be unique
within the same StagedUpdateStrategy.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,19 @@ spec:
type: object
type: object
x-kubernetes-map-type: atomic
maxConcurrency:
anyOf:
- type: integer
- type: string
default: 1
description: |-
MaxConcurrency specifies the maximum number of clusters that can be updated concurrently within this stage.
Value can be an absolute number (ex: 5) or a percentage of the total clusters in the stage (ex: 50%).
Absolute number is calculated from percentage by rounding up.
If not specified, all clusters in the stage are updated sequentially (effectively maxConcurrency = 1).
Defaults to 1.
pattern: ^((100|[0-9]{1,2})%|[0-9]+)$
x-kubernetes-int-or-string: true
name:
description: The name of the stage. This MUST be unique within
the same StagedUpdateStrategy.
Expand Down
9 changes: 9 additions & 0 deletions pkg/controllers/updaterun/validation_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr"

clusterv1beta1 "github.com/kubefleet-dev/kubefleet/apis/cluster/v1beta1"
placementv1beta1 "github.com/kubefleet-dev/kubefleet/apis/placement/v1beta1"
Expand Down Expand Up @@ -303,6 +304,10 @@ var _ = Describe("UpdateRun validation tests", func() {
"region": "no-exist",
},
},
MaxConcurrency: &intstr.IntOrString{
Type: intstr.Int,
IntVal: 1,
},
})
Expect(k8sClient.Status().Update(ctx, updateRun)).Should(Succeed())

Expand All @@ -316,6 +321,10 @@ var _ = Describe("UpdateRun validation tests", func() {
"region": "no-exist",
},
},
MaxConcurrency: &intstr.IntOrString{
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should there be an integration test for the percentage case?

Copy link
Collaborator Author

@Arvindthiru Arvindthiru Nov 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added these changes to fix failing ITs, since MaxConcurrency defaults to 1. Will add more tests when implementing the API

Type: intstr.Int,
IntVal: 1,
},
})
validateClusterStagedUpdateRunStatus(ctx, updateRun, wantStatus, "the number of stages in the updateRun has changed")
})
Expand Down
Loading