Skip to content

Commit a2726f8

Browse files
authored
chore: backport changes (11/19/2025) (#1230)
2 parents 4497f8d + 656164b commit a2726f8

37 files changed

+2458
-470
lines changed

.github/workflows/codespell.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@ jobs:
1212
runs-on: ubuntu-latest
1313
steps:
1414
- name: Harden Runner
15-
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
15+
uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2
1616
with:
1717
egress-policy: audit
1818

1919
- uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v4.1.7
20-
- uses: codespell-project/actions-codespell@406322ec52dd7b488e48c1c4b82e2a8b3a1bf630 # master
20+
- uses: codespell-project/actions-codespell@8f01853be192eb0f849a5c7d721450e7a467c579 # master
2121
with:
2222
check_filenames: true
2323
skip: ./.git,./.github/workflows/codespell.yml,.git,*.png,*.jpg,*.svg,*.sum,./vendor,go.sum,testdata

apis/placement/v1beta1/clusterresourceplacement_types.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ type ClusterResourcePlacement struct {
113113

114114
// The desired state of ClusterResourcePlacement.
115115
// +kubebuilder:validation:Required
116-
// +kubebuilder:validation:XValidation:rule="!((has(oldSelf.policy) && !has(self.policy)) || (has(oldSelf.policy) && has(self.policy) && has(self.policy.placementType) && has(oldSelf.policy.placementType) && self.policy.placementType != oldSelf.policy.placementType))",message="placement type is immutable"
116+
// +kubebuilder:validation:XValidation:rule="!(has(oldSelf.policy) && !has(self.policy))",message="policy cannot be removed once set"
117117
// +kubebuilder:validation:XValidation:rule="!(self.statusReportingScope == 'NamespaceAccessible' && size(self.resourceSelectors.filter(x, x.kind == 'Namespace')) != 1)",message="when statusReportingScope is NamespaceAccessible, exactly one resourceSelector with kind 'Namespace' is required"
118118
// +kubebuilder:validation:XValidation:rule="!has(oldSelf.statusReportingScope) || self.statusReportingScope == oldSelf.statusReportingScope",message="statusReportingScope is immutable"
119119
Spec PlacementSpec `json:"spec"`
@@ -135,6 +135,7 @@ type PlacementSpec struct {
135135
// Policy defines how to select member clusters to place the selected resources.
136136
// If unspecified, all the joined member clusters are selected.
137137
// +kubebuilder:validation:Optional
138+
// +kubebuilder:validation:XValidation:rule="!(self.placementType != oldSelf.placementType)",message="placement type is immutable"
138139
Policy *PlacementPolicy `json:"policy,omitempty"`
139140

140141
// The rollout strategy to use to replace existing placement with new ones.
@@ -1628,6 +1629,7 @@ type ResourcePlacement struct {
16281629

16291630
// The desired state of ResourcePlacement.
16301631
// +kubebuilder:validation:Required
1632+
// +kubebuilder:validation:XValidation:rule="!(has(oldSelf.policy) && !has(self.policy))",message="policy cannot be removed once set"
16311633
Spec PlacementSpec `json:"spec"`
16321634

16331635
// The observed status of ResourcePlacement.

apis/placement/v1beta1/stageupdate_types.go

Lines changed: 99 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package v1beta1
1919
import (
2020
"k8s.io/apimachinery/pkg/api/meta"
2121
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
22+
"k8s.io/apimachinery/pkg/util/intstr"
2223
"sigs.k8s.io/controller-runtime/pkg/client"
2324

2425
"go.goms.io/fleet/apis"
@@ -91,6 +92,7 @@ type UpdateRunObjList interface {
9192
// +kubebuilder:printcolumn:JSONPath=`.spec.resourceSnapshotIndex`,name="Resource-Snapshot-Index",type=string
9293
// +kubebuilder:printcolumn:JSONPath=`.status.policySnapshotIndexUsed`,name="Policy-Snapshot-Index",type=string
9394
// +kubebuilder:printcolumn:JSONPath=`.status.conditions[?(@.type=="Initialized")].status`,name="Initialized",type=string
95+
// +kubebuilder:printcolumn:JSONPath=`.status.conditions[?(@.type=="Progressing")].status`,name="Progressing",type=string
9496
// +kubebuilder:printcolumn:JSONPath=`.status.conditions[?(@.type=="Succeeded")].status`,name="Succeeded",type=string
9597
// +kubebuilder:printcolumn:JSONPath=`.metadata.creationTimestamp`,name="Age",type=date
9698
// +kubebuilder:printcolumn:JSONPath=`.spec.stagedRolloutStrategyName`,name="Strategy",priority=1,type=string
@@ -106,9 +108,8 @@ type ClusterStagedUpdateRun struct {
106108
metav1.TypeMeta `json:",inline"`
107109
metav1.ObjectMeta `json:"metadata,omitempty"`
108110

109-
// The desired state of ClusterStagedUpdateRun. The spec is immutable.
111+
// The desired state of ClusterStagedUpdateRun.
110112
// +kubebuilder:validation:Required
111-
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="The spec field is immutable"
112113
Spec UpdateRunSpec `json:"spec"`
113114

114115
// The observed status of ClusterStagedUpdateRun.
@@ -146,27 +147,66 @@ func (c *ClusterStagedUpdateRun) SetUpdateRunStatus(status UpdateRunStatus) {
146147
c.Status = status
147148
}
148149

150+
// State represents the desired state of an update run.
151+
// +enum
152+
type State string
153+
154+
const (
155+
// StateNotStarted describes user intent to initialize but not execute the update run.
156+
// This is the default state when an update run is created.
157+
StateNotStarted State = "NotStarted"
158+
159+
// StateStarted describes user intent to execute (or resume execution if paused).
160+
// Users can subsequently set the state to Stopped or Abandoned.
161+
StateStarted State = "Started"
162+
163+
// StateStopped describes user intent to pause the update run.
164+
// Users can subsequently set the state to Started or Abandoned.
165+
StateStopped State = "Stopped"
166+
167+
// StateAbandoned describes user intent to abandon the update run.
168+
// This is a terminal state; once set, it cannot be changed.
169+
StateAbandoned State = "Abandoned"
170+
)
171+
149172
// UpdateRunSpec defines the desired rollout strategy and the snapshot indices of the resources to be updated.
150173
// It specifies a stage-by-stage update process across selected clusters for the given ResourcePlacement object.
174+
// +kubebuilder:validation:XValidation:rule="!has(oldSelf.state) || oldSelf.state != 'NotStarted' || self.state != 'Stopped'",message="invalid state transition: cannot transition from NotStarted to Stopped"
175+
// +kubebuilder:validation:XValidation:rule="!has(oldSelf.state) || oldSelf.state != 'Started' || self.state != 'NotStarted'",message="invalid state transition: cannot transition from Started to NotStarted"
176+
// +kubebuilder:validation:XValidation:rule="!has(oldSelf.state) || oldSelf.state != 'Stopped' || self.state != 'NotStarted'",message="invalid state transition: cannot transition from Stopped to NotStarted"
177+
// +kubebuilder:validation:XValidation:rule="!has(oldSelf.state) || oldSelf.state != 'Abandoned' || self.state == 'Abandoned'",message="invalid state transition: Abandoned is a terminal state and cannot transition to any other state"
151178
type UpdateRunSpec struct {
152179
// PlacementName is the name of placement that this update run is applied to.
153180
// There can be multiple active update runs for each placement, but
154181
// it's up to the DevOps team to ensure they don't conflict with each other.
155182
// +kubebuilder:validation:Required
156183
// +kubebuilder:validation:MaxLength=255
184+
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="placementName is immutable"
157185
PlacementName string `json:"placementName"`
158186

159187
// The resource snapshot index of the selected resources to be updated across clusters.
160188
// The index represents a group of resource snapshots that includes all the resources a ResourcePlacement selected.
161-
// +kubebuilder:validation:Required
189+
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="resourceSnapshotIndex is immutable"
190+
// +kubebuilder:validation:Optional
162191
ResourceSnapshotIndex string `json:"resourceSnapshotIndex"`
163192

164193
// The name of the update strategy that specifies the stages and the sequence
165194
// in which the selected resources will be updated on the member clusters. The stages
166195
// are computed according to the referenced strategy when the update run starts
167196
// and recorded in the status field.
168197
// +kubebuilder:validation:Required
198+
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="stagedRolloutStrategyName is immutable"
169199
StagedUpdateStrategyName string `json:"stagedRolloutStrategyName"`
200+
201+
// State indicates the desired state of the update run.
202+
// NotStarted: The update run is initialized but execution has not started (default).
203+
// Started: The update run should execute or resume execution.
204+
// Stopped: The update run should pause execution.
205+
// Abandoned: The update run should be abandoned and terminated.
206+
// +kubebuilder:validation:Optional
207+
// +kubebuilder:default=NotStarted
208+
// +kubebuilder:validation:Enum=NotStarted;Started;Stopped;Abandoned
209+
State State `json:"state,omitempty"`
170210
}
171211

172212
// UpdateStrategySpecGetterSetter offers the functionality to work with UpdateStrategySpec.
@@ -275,21 +315,40 @@ type StageConfig struct {
275315
// +kubebuilder:validation:Optional
276316
SortingLabelKey *string `json:"sortingLabelKey,omitempty"`
277317

318+
// MaxConcurrency specifies the maximum number of clusters that can be updated concurrently within this stage.
319+
// Value can be an absolute number (ex: 5) or a percentage of the total clusters in the stage (ex: 50%).
320+
// Fractional results are rounded down. A minimum of 1 update is enforced.
321+
// If not specified, all clusters in the stage are updated sequentially (effectively maxConcurrency = 1).
322+
// Defaults to 1.
323+
// +kubebuilder:default=1
324+
// +kubebuilder:validation:XIntOrString
325+
// +kubebuilder:validation:Pattern="^((100|[0-9]{1,2})%|[0-9]+)$"
326+
// +kubebuilder:validation:Optional
327+
MaxConcurrency *intstr.IntOrString `json:"maxConcurrency,omitempty"`
328+
278329
// The collection of tasks that each stage needs to complete successfully before moving to the next stage.
279330
// Each task is executed in parallel and there cannot be more than one task of the same type.
280331
// +kubebuilder:validation:MaxItems=2
281332
// +kubebuilder:validation:Optional
282333
// +kubebuilder:validation:XValidation:rule="!self.exists(e, e.type == 'Approval' && has(e.waitTime))",message="AfterStageTaskType is Approval, waitTime is not allowed"
283334
// +kubebuilder:validation:XValidation:rule="!self.exists(e, e.type == 'TimedWait' && !has(e.waitTime))",message="AfterStageTaskType is TimedWait, waitTime is required"
284-
AfterStageTasks []AfterStageTask `json:"afterStageTasks,omitempty"`
335+
AfterStageTasks []StageTask `json:"afterStageTasks,omitempty"`
336+
337+
// The collection of tasks that needs to completed successfully by each stage before starting the stage.
338+
// Each task is executed in parallel and there cannot be more than one task of the same type.
339+
// +kubebuilder:validation:Optional
340+
// +kubebuilder:validation:MaxItems=1
341+
// +kubebuilder:validation:XValidation:rule="!self.exists(e, e.type == 'Approval' && has(e.waitTime))",message="AfterStageTaskType is Approval, waitTime is not allowed"
342+
// +kubebuilder:validation:XValidation:rule="!self.exists(e, e.type == 'TimedWait')",message="BeforeStageTaskType cannot be TimedWait"
343+
BeforeStageTasks []StageTask `json:"beforeStageTasks,omitempty"`
285344
}
286345

287-
// AfterStageTask is the collection of post-stage tasks that ALL need to be completed before moving to the next stage.
288-
type AfterStageTask struct {
289-
// The type of the after-stage task.
346+
// StageTask is the pre or post stage task that needs to be completed before starting or moving to the next stage.
347+
type StageTask struct {
348+
// The type of the before or after stage task.
290349
// +kubebuilder:validation:Enum=TimedWait;Approval
291350
// +kubebuilder:validation:Required
292-
Type AfterStageTaskType `json:"type"`
351+
Type StageTaskType `json:"type"`
293352

294353
// The time to wait after all the clusters in the current stage complete the update before moving to the next stage.
295354
// +kubebuilder:validation:Pattern="^0|([0-9]+(\\.[0-9]+)?(s|m|h))+$"
@@ -315,6 +374,11 @@ type UpdateRunStatus struct {
315374
// +kubebuilder:validation:Optional
316375
PolicyObservedClusterCount int `json:"policyObservedClusterCount,omitempty"`
317376

377+
// ResourceSnapshotIndexUsed records the resource snapshot index that the update run is based on.
378+
// The index represents the same resource snapshots as specified in the spec field, or the latest.
379+
// +kubbebuilder:validation:Optional
380+
ResourceSnapshotIndexUsed string `json:"resourceSnapshotIndexUsed,omitempty"`
381+
318382
// ApplyStrategy is the apply strategy that the stagedUpdateRun is using.
319383
// It is the same as the apply strategy in the CRP when the staged update run starts.
320384
// The apply strategy is not updated during the update run even if it changes in the CRP.
@@ -367,7 +431,7 @@ const (
367431
// StagedUpdateRunConditionProgressing indicates whether the staged update run is making progress.
368432
// Its condition status can be one of the following:
369433
// - "True": The staged update run is making progress.
370-
// - "False": The staged update run is waiting/paused.
434+
// - "False": The staged update run is waiting/paused/abandoned.
371435
// - "Unknown" means it is unknown.
372436
StagedUpdateRunConditionProgressing StagedUpdateRunConditionType = "Progressing"
373437

@@ -392,7 +456,12 @@ type StageUpdatingStatus struct {
392456
// Empty if the stage has not finished updating all the clusters.
393457
// +kubebuilder:validation:MaxItems=2
394458
// +kubebuilder:validation:Optional
395-
AfterStageTaskStatus []AfterStageTaskStatus `json:"afterStageTaskStatus,omitempty"`
459+
AfterStageTaskStatus []StageTaskStatus `json:"afterStageTaskStatus,omitempty"`
460+
461+
// The status of the pre-update tasks associated with the current stage.
462+
// +kubebuilder:validation:MaxItems=1
463+
// +kubebuilder:validation:Optional
464+
BeforeStageTaskStatus []StageTaskStatus `json:"beforeStageTaskStatus,omitempty"`
396465

397466
// The time when the update started on the stage. Empty if the stage has not started updating.
398467
// +kubebuilder:validation:Optional
@@ -482,11 +551,11 @@ const (
482551
ClusterUpdatingConditionSucceeded ClusterUpdatingStatusConditionType = "Succeeded"
483552
)
484553

485-
type AfterStageTaskStatus struct {
486-
// The type of the post-update task.
554+
type StageTaskStatus struct {
555+
// The type of the pre or post update task.
487556
// +kubebuilder:validation:Enum=TimedWait;Approval
488557
// +kubebuilder:validation:Required
489-
Type AfterStageTaskType `json:"type"`
558+
Type StageTaskType `json:"type"`
490559

491560
// The name of the approval request object that is created for this stage.
492561
// Only valid if the AfterStageTaskType is Approval.
@@ -498,45 +567,45 @@ type AfterStageTaskStatus struct {
498567
// +listType=map
499568
// +listMapKey=type
500569
//
501-
// Conditions is an array of current observed conditions for the specific type of post-update task.
570+
// Conditions is an array of current observed conditions for the specific type of pre or post update task.
502571
// Known conditions are "ApprovalRequestCreated", "WaitTimeElapsed", and "ApprovalRequestApproved".
503572
// +kubebuilder:validation:Optional
504573
Conditions []metav1.Condition `json:"conditions,omitempty"`
505574
}
506575

507-
// AfterStageTaskType identifies a specific type of the AfterStageTask.
576+
// StageTaskType identifies a specific type of the AfterStageTask or BeforeStageTask.
508577
// +enum
509-
type AfterStageTaskType string
578+
type StageTaskType string
510579

511580
const (
512-
// AfterStageTaskTypeTimedWait indicates the post-stage task is a timed wait.
513-
AfterStageTaskTypeTimedWait AfterStageTaskType = "TimedWait"
581+
// StageTaskTypeTimedWait indicates the stage task is a timed wait.
582+
StageTaskTypeTimedWait StageTaskType = "TimedWait"
514583

515-
// AfterStageTaskTypeApproval indicates the post-stage task is an approval.
516-
AfterStageTaskTypeApproval AfterStageTaskType = "Approval"
584+
// StageTaskTypeApproval indicates the stage task is an approval.
585+
StageTaskTypeApproval StageTaskType = "Approval"
517586
)
518587

519-
// AfterStageTaskConditionType identifies a specific condition of the AfterStageTask.
588+
// StageTaskConditionType identifies a specific condition of the AfterStageTask or BeforeStageTask.
520589
// +enum
521-
type AfterStageTaskConditionType string
590+
type StageTaskConditionType string
522591

523592
const (
524-
// AfterStageTaskConditionApprovalRequestCreated indicates if the approval request has been created.
593+
// StageTaskConditionApprovalRequestCreated indicates if the approval request has been created.
525594
// Its condition status can be:
526595
// - "True": The approval request has been created.
527-
AfterStageTaskConditionApprovalRequestCreated AfterStageTaskConditionType = "ApprovalRequestCreated"
596+
StageTaskConditionApprovalRequestCreated StageTaskConditionType = "ApprovalRequestCreated"
528597

529-
// AfterStageTaskConditionApprovalRequestApproved indicates if the approval request has been approved.
598+
// StageTaskConditionApprovalRequestApproved indicates if the approval request has been approved.
530599
// Its condition status can be:
531600
// - "True": The approval request has been approved.
532-
AfterStageTaskConditionApprovalRequestApproved AfterStageTaskConditionType = "ApprovalRequestApproved"
601+
StageTaskConditionApprovalRequestApproved StageTaskConditionType = "ApprovalRequestApproved"
533602

534-
// AfterStageTaskConditionWaitTimeElapsed indicates if the wait time after each stage has elapsed.
603+
// StageTaskConditionWaitTimeElapsed indicates if the wait time after each stage has elapsed.
535604
// If the status is "False", the condition message will include the remaining wait time.
536605
// Its condition status can be:
537606
// - "True": The wait time has elapsed.
538607
// - "False": The wait time has not elapsed.
539-
AfterStageTaskConditionWaitTimeElapsed AfterStageTaskConditionType = "WaitTimeElapsed"
608+
StageTaskConditionWaitTimeElapsed StageTaskConditionType = "WaitTimeElapsed"
540609
)
541610

542611
// ClusterStagedUpdateRunList contains a list of ClusterStagedUpdateRun.
@@ -721,6 +790,7 @@ func (c *ClusterApprovalRequestList) GetApprovalRequestObjs() []ApprovalRequestO
721790
// +kubebuilder:printcolumn:JSONPath=`.spec.resourceSnapshotIndex`,name="Resource-Snapshot-Index",type=string
722791
// +kubebuilder:printcolumn:JSONPath=`.status.policySnapshotIndexUsed`,name="Policy-Snapshot-Index",type=string
723792
// +kubebuilder:printcolumn:JSONPath=`.status.conditions[?(@.type=="Initialized")].status`,name="Initialized",type=string
793+
// +kubebuilder:printcolumn:JSONPath=`.status.conditions[?(@.type=="Progressing")].status`,name="Progressing",type=string
724794
// +kubebuilder:printcolumn:JSONPath=`.status.conditions[?(@.type=="Succeeded")].status`,name="Succeeded",type=string
725795
// +kubebuilder:printcolumn:JSONPath=`.metadata.creationTimestamp`,name="Age",type=date
726796
// +kubebuilder:printcolumn:JSONPath=`.spec.stagedRolloutStrategyName`,name="Strategy",priority=1,type=string
@@ -736,9 +806,8 @@ type StagedUpdateRun struct {
736806
metav1.TypeMeta `json:",inline"`
737807
metav1.ObjectMeta `json:"metadata,omitempty"`
738808

739-
// The desired state of StagedUpdateRun. The spec is immutable.
809+
// The desired state of StagedUpdateRun.
740810
// +kubebuilder:validation:Required
741-
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="The spec field is immutable"
742811
Spec UpdateRunSpec `json:"spec"`
743812

744813
// The observed status of StagedUpdateRun.

0 commit comments

Comments
 (0)