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
36 changes: 35 additions & 1 deletion apis/placement/v1beta1/stageupdate_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ type UpdateRunObjList interface {
// +kubebuilder:printcolumn:JSONPath=`.spec.resourceSnapshotIndex`,name="Resource-Snapshot-Index",type=string
// +kubebuilder:printcolumn:JSONPath=`.status.policySnapshotIndexUsed`,name="Policy-Snapshot-Index",type=string
// +kubebuilder:printcolumn:JSONPath=`.status.conditions[?(@.type=="Initialized")].status`,name="Initialized",type=string
// +kubebuilder:printcolumn:JSONPath=`.status.conditions[?(@.type=="Progressing")].status`,name="Progressing",type=string
// +kubebuilder:printcolumn:JSONPath=`.status.conditions[?(@.type=="Succeeded")].status`,name="Succeeded",type=string
// +kubebuilder:printcolumn:JSONPath=`.metadata.creationTimestamp`,name="Age",type=date
// +kubebuilder:printcolumn:JSONPath=`.spec.stagedRolloutStrategyName`,name="Strategy",priority=1,type=string
Expand Down Expand Up @@ -147,6 +148,28 @@ func (c *ClusterStagedUpdateRun) SetUpdateRunStatus(status UpdateRunStatus) {
c.Status = status
}

// State represents the desired state of an update run.
// +enum
type State string

const (
// StateNotStarted describes user intent to initialize but not execute the update run.
// This is the default state when an update run is created.
StateNotStarted State = "NotStarted"

// StateStarted describes user intent to execute (or resume execution if paused).
// Users can subsequently set the state to Stopped or Abandoned.
StateStarted State = "Started"

// StateStopped describes user intent to pause the update run.
// Users can subsequently set the state to Started or Abandoned.
StateStopped State = "Stopped"

// StateAbandoned describes user intent to abandon the update run.
// This is a terminal state; once set, it cannot be changed.
StateAbandoned State = "Abandoned"
)

// UpdateRunSpec defines the desired rollout strategy and the snapshot indices of the resources to be updated.
// It specifies a stage-by-stage update process across selected clusters for the given ResourcePlacement object.
type UpdateRunSpec struct {
Expand All @@ -168,6 +191,16 @@ type UpdateRunSpec struct {
// and recorded in the status field.
// +kubebuilder:validation:Required
StagedUpdateStrategyName string `json:"stagedRolloutStrategyName"`

// State indicates the desired state of the update run.
// NotStarted: The update run is initialized but execution has not started (default).
// Started: The update run should execute or resume execution.
// Stopped: The update run should pause execution.
// Abandoned: The update run should be abandoned and terminated.
// +kubebuilder:validation:Optional
// +kubebuilder:validation:Enum=NotStarted;Started;Stopped;Abandoned
// +kubebuilder:default=NotStarted
State State `json:"state"`
Copy link
Contributor

Choose a reason for hiding this comment

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

we need to guard against a few invalid state transition like start -> NotStarted. Abandoned->* it seems CEL can do that?

Copy link
Contributor

Choose a reason for hiding this comment

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

we also need to block any run that start with other than "NotStarted" state. I am not even sure if this is something that user should set. It seems like every run always starts with this state so there is no need for user to specify this state ever.

Copy link
Contributor

Choose a reason for hiding this comment

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

as the previous vesion shows, NotStarted is more like a status than a state.

}

// UpdateStrategySpecGetterSetter offers the functionality to work with UpdateStrategySpec.
Expand Down Expand Up @@ -387,7 +420,7 @@ const (
// StagedUpdateRunConditionProgressing indicates whether the staged update run is making progress.
// Its condition status can be one of the following:
// - "True": The staged update run is making progress.
// - "False": The staged update run is waiting/paused.
// - "False": The staged update run is waiting/paused/abandoned.
// - "Unknown" means it is unknown.
StagedUpdateRunConditionProgressing StagedUpdateRunConditionType = "Progressing"

Expand Down Expand Up @@ -746,6 +779,7 @@ func (c *ClusterApprovalRequestList) GetApprovalRequestObjs() []ApprovalRequestO
// +kubebuilder:printcolumn:JSONPath=`.spec.resourceSnapshotIndex`,name="Resource-Snapshot-Index",type=string
// +kubebuilder:printcolumn:JSONPath=`.status.policySnapshotIndexUsed`,name="Policy-Snapshot-Index",type=string
// +kubebuilder:printcolumn:JSONPath=`.status.conditions[?(@.type=="Initialized")].status`,name="Initialized",type=string
// +kubebuilder:printcolumn:JSONPath=`.status.conditions[?(@.type=="Progressing")].status`,name="Progressing",type=string
// +kubebuilder:printcolumn:JSONPath=`.status.conditions[?(@.type=="Succeeded")].status`,name="Succeeded",type=string
// +kubebuilder:printcolumn:JSONPath=`.metadata.creationTimestamp`,name="Age",type=date
// +kubebuilder:printcolumn:JSONPath=`.spec.stagedRolloutStrategyName`,name="Strategy",priority=1,type=string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1112,6 +1112,9 @@ spec:
- jsonPath: .status.conditions[?(@.type=="Initialized")].status
name: Initialized
type: string
- jsonPath: .status.conditions[?(@.type=="Progressing")].status
name: Progressing
type: string
- jsonPath: .status.conditions[?(@.type=="Succeeded")].status
name: Succeeded
type: string
Expand Down Expand Up @@ -1173,6 +1176,20 @@ spec:
are computed according to the referenced strategy when the update run starts
and recorded in the status field.
type: string
state:
default: NotStarted
description: |-
State indicates the desired state of the update run.
NotStarted: The update run is initialized but execution has not started (default).
Started: The update run should execute or resume execution.
Stopped: The update run should pause execution.
Abandoned: The update run should be abandoned and terminated.
enum:
- NotStarted
- Started
- Stopped
- Abandoned
type: string
required:
- placementName
- resourceSnapshotIndex
Expand Down Expand Up @@ -1998,7 +2015,7 @@ spec:
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.
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.
pattern: ^((100|[0-9]{1,2})%|[0-9]+)$
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ spec:
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.
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.
pattern: ^((100|[0-9]{1,2})%|[0-9]+)$
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ spec:
- jsonPath: .status.conditions[?(@.type=="Initialized")].status
name: Initialized
type: string
- jsonPath: .status.conditions[?(@.type=="Progressing")].status
name: Progressing
type: string
- jsonPath: .status.conditions[?(@.type=="Succeeded")].status
name: Succeeded
type: string
Expand Down Expand Up @@ -92,6 +95,20 @@ spec:
are computed according to the referenced strategy when the update run starts
and recorded in the status field.
type: string
state:
default: NotStarted
description: |-
State indicates the desired state of the update run.
NotStarted: The update run is initialized but execution has not started (default).
Started: The update run should execute or resume execution.
Stopped: The update run should pause execution.
Abandoned: The update run should be abandoned and terminated.
enum:
- NotStarted
- Started
- Stopped
- Abandoned
type: string
required:
- placementName
- resourceSnapshotIndex
Expand Down Expand Up @@ -917,7 +934,7 @@ spec:
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.
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.
pattern: ^((100|[0-9]{1,2})%|[0-9]+)$
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ spec:
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.
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.
pattern: ^((100|[0-9]{1,2})%|[0-9]+)$
Expand Down
1 change: 1 addition & 0 deletions pkg/controllers/updaterun/controller_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,7 @@ func generateTestClusterStagedUpdateRun() *placementv1beta1.ClusterStagedUpdateR
PlacementName: testCRPName,
ResourceSnapshotIndex: testResourceSnapshotIndex,
StagedUpdateStrategyName: testUpdateStrategyName,
State: placementv1beta1.StateStarted,
},
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1122,6 +1122,9 @@ var _ = Describe("Test placement v1beta1 API validation", func() {
ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf(validupdateRunNameTemplate, GinkgoParallelProcess()),
},
Spec: placementv1beta1.UpdateRunSpec{
State: placementv1beta1.StateStarted,
},
}
Expect(hubClient.Create(ctx, &updateRun)).Should(Succeed())
Expect(hubClient.Delete(ctx, &updateRun)).Should(Succeed())
Expand All @@ -1134,6 +1137,9 @@ var _ = Describe("Test placement v1beta1 API validation", func() {
ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf(invalidupdateRunNameTemplate, GinkgoParallelProcess()),
},
Spec: placementv1beta1.UpdateRunSpec{
State: placementv1beta1.StateStarted,
},
}
err := hubClient.Create(ctx, &updateRun)
var statusErr *k8sErrors.StatusError
Expand All @@ -1148,6 +1154,7 @@ var _ = Describe("Test placement v1beta1 API validation", func() {
},
Spec: placementv1beta1.UpdateRunSpec{
PlacementName: "test-placement",
State: placementv1beta1.StateStarted,
},
}
Expect(hubClient.Create(ctx, &updateRun)).Should(Succeed())
Expand Down
1 change: 1 addition & 0 deletions test/e2e/cluster_staged_updaterun_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1668,6 +1668,7 @@ func createClusterStagedUpdateRunSucceed(updateRunName, crpName, resourceSnapsho
PlacementName: crpName,
ResourceSnapshotIndex: resourceSnapshotIndex,
StagedUpdateStrategyName: strategyName,
State: placementv1beta1.StateStarted,
Copy link
Contributor

Choose a reason for hiding this comment

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

shouldn't the default be "stateNotStarted"? It's the default so we don't need to add it?

Copy link
Collaborator Author

@Arvindthiru Arvindthiru Nov 7, 2025

Choose a reason for hiding this comment

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

Field validation fails if value is state is not specified

Image

I tried applying the object using an yaml file it set the default value correctly but when it's go struct field validation kicks in before the default value is set. Since our updateRuns start by default I set it to StateStarted for now

},
}
Expect(hubClient.Create(ctx, updateRun)).To(Succeed(), "Failed to create ClusterStagedUpdateRun %s", updateRunName)
Expand Down
1 change: 1 addition & 0 deletions test/e2e/staged_updaterun_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1201,6 +1201,7 @@ func createStagedUpdateRunSucceed(updateRunName, namespace, rpName, resourceSnap
PlacementName: rpName,
ResourceSnapshotIndex: resourceSnapshotIndex,
StagedUpdateStrategyName: strategyName,
State: placementv1beta1.StateStarted,
},
}
Expect(hubClient.Create(ctx, updateRun)).To(Succeed(), "Failed to create StagedUpdateRun %s", updateRunName)
Expand Down
Loading