diff --git a/.golangci.yaml b/.golangci.yaml index c2311c4aa..6867b4b16 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -160,6 +160,12 @@ linters: # Ref: https://go.dev/doc/effective_go#redeclaration - path: (.+)\.go$ text: declaration of "err" shadows declaration at + # FailoverReservations are deprecated in favor of FailoverHostReservations, + # but remain in active use during the transition. Ignore the deprecation + # warning until the old type is fully removed. + - linters: + - staticcheck + text: "SA1019:.*FailoverReservations will be replaced with FailoverHostReservations" - linters: - goconst path: (.+)_test\.go diff --git a/api/v1alpha1/reservation_types.go b/api/v1alpha1/reservation_types.go index f52797654..2b9e25470 100644 --- a/api/v1alpha1/reservation_types.go +++ b/api/v1alpha1/reservation_types.go @@ -19,11 +19,45 @@ type ReservationType string const ( // ReservationTypeCommittedResource is a reservation for committed/reserved capacity. ReservationTypeCommittedResource ReservationType = "CommittedResourceReservation" + // ReservationTypeFailover is a reservation for failover capacity. + // + // Deprecated: FailoverReservations will be replaced with + // FailoverHostReservations. See ReservationTypeFailoverHost. ReservationTypeFailover ReservationType = "FailoverReservation" + // ReservationTypeInFlight is a reservation that blocks capacity for virtual // machines that are currently being scheduled, to avoid double-booking. ReservationTypeInFlight ReservationType = "InFlightReservation" + + // ReservationTypeFailoverHost is a reservation that blocks an entire + // host for failover purposes, so that another host can gang-evacuate + // its workloads to a designated spot. If this type is set, the reservation + // will follow this lifecycle: + // + // 1. Designation. The reservation has been created on a host. There may be + // virtual machines running on this host that need live migration away. As + // long as not all virtual machines can be migrated away from the host, the + // reservation stays in this state. For pipeline: no new virtual machine + // onto this host. + // + // 2. Active. No more virtual machines on this host. On evacuation this + // space can be used. + // + // 3. Failed. The descheduling failed repeatedly or takes too long, the host + // is marked for a certain time to not be designated again. + // + // 4. In Use. Ongoing gang evacuation onto this host, which will be reused + // by subsequent failover events until the evacuation is completed. + // + // 5. Revoked (=Deleted). The reservation is no longer valid because it no + // longer matches any failover partition. This may be because the host has + // been marked as no longer schedulable or has been otherwise changed. + // + // 6. Used (=Deleted). The failover event has been completed. The + // reservation is no longer valid and the host is transformed back into + // a regular host. + ReservationTypeFailoverHost ReservationType = "FailoverHostReservation" ) // Label keys for Reservation metadata. @@ -39,6 +73,7 @@ const ( ReservationTypeLabelCommittedResource = "committed-resource" ReservationTypeLabelFailover = "failover" ReservationTypeLabelInFlight = "in-flight" + ReservationTypeLabelFailoverHost = "failover-host" ) // Annotation keys for Reservation metadata. @@ -101,7 +136,15 @@ type CommittedResourceReservationSpec struct { Allocations map[string]CommittedResourceAllocation `json:"allocations,omitempty"` } +// MatchesGroup reports whether the reservation targets the given project and resource group. +func (s *CommittedResourceReservationSpec) MatchesGroup(projectID, resourceGroup string) bool { + return s.ProjectID == projectID && s.ResourceGroup == resourceGroup +} + // FailoverReservationSpec defines the spec fields specific to failover reservations. +// +// Deprecated: FailoverReservations will be replaced with FailoverHostReservations. +// See FailoverHostReservationSpec. type FailoverReservationSpec struct { // ResourceGroup is the group/category of the resource (e.g., "hana_medium_v2"). // +kubebuilder:validation:Optional @@ -128,10 +171,20 @@ type InFlightReservationSpec struct { Intent SchedulingIntent `json:"intent"` } +// FailoverHostReservationSpec defines the spec fields specific to failover +// host reservations. See ReservationTypeFailoverHost. +type FailoverHostReservationSpec struct { + // SourceHosts is a list of hosts inside the same failover partition from + // which a failover event towards the target host may originate. + // See Spec.TargetHost for the target host of the failover reservation. + // +kubebuilder:validation:Optional + SourceHosts []string `json:"sourceHosts,omitempty"` +} + // ReservationSpec defines the desired state of Reservation. type ReservationSpec struct { // Type of reservation. - // +kubebuilder:validation:Enum=CommittedResourceReservation;FailoverReservation;InFlightReservation + // +kubebuilder:validation:Enum=CommittedResourceReservation;FailoverReservation;InFlightReservation;FailoverHostReservation // +kubebuilder:validation:Required Type ReservationType `json:"type"` @@ -170,12 +223,19 @@ type ReservationSpec struct { // FailoverReservation contains fields specific to failover reservations. // Only used when Type is FailoverReservation. + // + // Deprecated: FailoverReservations will be replaced with FailoverHostReservations. // +kubebuilder:validation:Optional FailoverReservation *FailoverReservationSpec `json:"failoverReservation,omitempty"` // InFlightReservation specifies which kind of virtual machine is expected // to land on the reserved slot. Set when Type is InFlightReservation. InFlightReservation *InFlightReservationSpec `json:"inFlightReservation,omitempty"` + + // FailoverHostReservation contains fields specific to failover host reservations. + // Only used when Type is FailoverHostReservation. + // +kubebuilder:validation:Optional + FailoverHostReservation *FailoverHostReservationSpec `json:"failoverHostReservation,omitempty"` } const ( @@ -198,6 +258,9 @@ type CommittedResourceReservationStatus struct { } // FailoverReservationStatus defines the status fields specific to failover reservations. +// +// Deprecated: FailoverReservations will be replaced with FailoverHostReservations. +// See FailoverHostReservationStatus. type FailoverReservationStatus struct { // Allocations maps VM/instance UUIDs to the host they are currently allocated on. // Key: VM/instance UUID, Value: Host name where the VM is currently running. @@ -221,6 +284,51 @@ type FailoverReservationStatus struct { // in-flight reservations. type InFlightReservationStatus struct{} // No captured state for now. +// FailoverHostReservationStatus defines the status fields specific to failover +// host reservations. +type FailoverHostReservationStatus struct { + // SourceHost is the actual host from which the failover event originates. + // Only set when the condition ConditionTypeFailoverHostInUse is True. + // +kubebuilder:validation:Optional + SourceHost string `json:"sourceHost,omitempty"` +} + +// ReservationConditionType represents the type of condition for a reservation. +type ReservationConditionType = string + +const ( + // ConditionTypeFailoverHostDesignated indicates that the reservation has + // been created on a host. There may be virtual machines running on this + // host that need live migration away. As long as not all virtual machines + // can be migrated away from the host, the reservation stays in this state. + // For pipeline: no new virtual machine onto this host. + // + // status=Unknown: It is unknown if the failover host has been designated. + // status=False: The failover host has not been designated (there was an error). + // status=True: The failover host has been designated successfully. + ConditionTypeFailoverHostDesignated ReservationConditionType = "FailoverHostDesignated" + + // ConditionTypeFailoverHostActive indicates that the failover host is + // active and ready to accept evacuations. No more virtual machines have + // been seen on this host, so it is empty and ready to receive all vms + // from another host. + // + // status=Unknown: The failover host is not yet ready to be used. + // status=False: There has been some error or issue preventing the failover + // host from being active. + // status=True: The failover host is active and ready to accept evacuations. + ConditionTypeFailoverHostActive ReservationConditionType = "FailoverHostActive" + + // ConditionTypeFailoverHostInUse indicates that the failover host reservation + // is currently being used by an ongoing evacuation event with inbound + // virtual machines being evacuated. + // + // status=Unknown: It is unknown if the failover host is currently in use. + // status=False: The failover host is not currently in use. + // status=True: The failover host is currently in use. + ConditionTypeFailoverHostInUse ReservationConditionType = "FailoverHostInUse" +) + // ReservationStatus defines the observed state of Reservation. type ReservationStatus struct { // The current status conditions of the reservation. @@ -249,6 +357,8 @@ type ReservationStatus struct { // FailoverReservation contains status fields specific to failover reservations. // Only used when Type is FailoverReservation. + // + // Deprecated: FailoverReservations will be replaced with FailoverHostReservations. // +kubebuilder:validation:Optional FailoverReservation *FailoverReservationStatus `json:"failoverReservation,omitempty"` @@ -256,6 +366,11 @@ type ReservationStatus struct { // Only used when Type is InFlightReservation. // +kubebuilder:validation:Optional InFlightReservation *InFlightReservationStatus `json:"inFlightReservation,omitempty"` + + // FailoverHostReservation contains status fields specific to failover + // host reservations. Only used when Type is FailoverHostReservation. + // +kubebuilder:validation:Optional + FailoverHostReservation *FailoverHostReservationStatus `json:"failoverHostReservation,omitempty"` } // +kubebuilder:object:root=true @@ -264,12 +379,12 @@ type ReservationStatus struct { // +kubebuilder:printcolumn:name="Type",type="string",JSONPath=".metadata.labels['reservations\\.cortex\\.cloud/type']" // +kubebuilder:printcolumn:name="Host",type="string",JSONPath=".status.host" // +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type=='Ready')].status" -// +kubebuilder:printcolumn:name="ResourceGroup",type="string",JSONPath=".spec.committedResourceReservation.resourceGroup" +// +kubebuilder:printcolumn:name="ResourceGroup",type="string",JSONPath=".spec.committedResourceReservation.resourceGroup",priority=1 // +kubebuilder:printcolumn:name="HA ResourceGroup",type="string",JSONPath=".spec.failoverReservation.resourceGroup",priority=1 -// +kubebuilder:printcolumn:name="Project",type="string",JSONPath=".spec.committedResourceReservation.projectID" -// +kubebuilder:printcolumn:name="AZ",type="string",JSONPath=".spec.availabilityZone" +// +kubebuilder:printcolumn:name="Project",type="string",JSONPath=".spec.committedResourceReservation.projectID",priority=1 +// +kubebuilder:printcolumn:name="AZ",type="string",JSONPath=".spec.availabilityZone",priority=1 // +kubebuilder:printcolumn:name="StartTime",type="string",JSONPath=".spec.startTime",priority=1 -// +kubebuilder:printcolumn:name="EndTime",type="string",JSONPath=".spec.endTime" +// +kubebuilder:printcolumn:name="EndTime",type="string",JSONPath=".spec.endTime",priority=1 // +kubebuilder:printcolumn:name="Resources",type="string",JSONPath=".spec.resources",priority=1 // +kubebuilder:printcolumn:name="LastChanged",type="date",JSONPath=".status.failoverReservation.lastChanged",priority=1 // +kubebuilder:printcolumn:name="AcknowledgedAt",type="date",JSONPath=".status.failoverReservation.acknowledgedAt",priority=1 @@ -302,11 +417,6 @@ type ReservationList struct { Items []Reservation `json:"items"` } -// MatchesGroup reports whether the reservation targets the given project and resource group. -func (s *CommittedResourceReservationSpec) MatchesGroup(projectID, resourceGroup string) bool { - return s.ProjectID == projectID && s.ResourceGroup == resourceGroup -} - // IsReady returns true if the reservation has the Ready condition set to True. func (r *Reservation) IsReady() bool { return meta.IsStatusConditionTrue(r.Status.Conditions, ReservationConditionReady) diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 01e9ec113..c4f4115ff 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -663,6 +663,41 @@ func (in *DetectorSpec) DeepCopy() *DetectorSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FailoverHostReservationSpec) DeepCopyInto(out *FailoverHostReservationSpec) { + *out = *in + if in.SourceHosts != nil { + in, out := &in.SourceHosts, &out.SourceHosts + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FailoverHostReservationSpec. +func (in *FailoverHostReservationSpec) DeepCopy() *FailoverHostReservationSpec { + if in == nil { + return nil + } + out := new(FailoverHostReservationSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FailoverHostReservationStatus) DeepCopyInto(out *FailoverHostReservationStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FailoverHostReservationStatus. +func (in *FailoverHostReservationStatus) DeepCopy() *FailoverHostReservationStatus { + if in == nil { + return nil + } + out := new(FailoverHostReservationStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *FailoverReservationSpec) DeepCopyInto(out *FailoverReservationSpec) { *out = *in @@ -1724,6 +1759,11 @@ func (in *ReservationSpec) DeepCopyInto(out *ReservationSpec) { *out = new(InFlightReservationSpec) **out = **in } + if in.FailoverHostReservation != nil { + in, out := &in.FailoverHostReservation, &out.FailoverHostReservation + *out = new(FailoverHostReservationSpec) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReservationSpec. @@ -1761,6 +1801,11 @@ func (in *ReservationStatus) DeepCopyInto(out *ReservationStatus) { *out = new(InFlightReservationStatus) **out = **in } + if in.FailoverHostReservation != nil { + in, out := &in.FailoverHostReservation, &out.FailoverHostReservation + *out = new(FailoverHostReservationStatus) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReservationStatus. diff --git a/helm/library/cortex/files/crds/cortex.cloud_reservations.yaml b/helm/library/cortex/files/crds/cortex.cloud_reservations.yaml index efd4efb56..a9bdceebf 100644 --- a/helm/library/cortex/files/crds/cortex.cloud_reservations.yaml +++ b/helm/library/cortex/files/crds/cortex.cloud_reservations.yaml @@ -26,6 +26,7 @@ spec: type: string - jsonPath: .spec.committedResourceReservation.resourceGroup name: ResourceGroup + priority: 1 type: string - jsonPath: .spec.failoverReservation.resourceGroup name: HA ResourceGroup @@ -33,9 +34,11 @@ spec: type: string - jsonPath: .spec.committedResourceReservation.projectID name: Project + priority: 1 type: string - jsonPath: .spec.availabilityZone name: AZ + priority: 1 type: string - jsonPath: .spec.startTime name: StartTime @@ -43,6 +46,7 @@ spec: type: string - jsonPath: .spec.endTime name: EndTime + priority: 1 type: string - jsonPath: .spec.resources name: Resources @@ -163,10 +167,26 @@ spec: description: EndTime is the time when the reservation expires. format: date-time type: string + failoverHostReservation: + description: |- + FailoverHostReservation contains fields specific to failover host reservations. + Only used when Type is FailoverHostReservation. + properties: + sourceHosts: + description: |- + SourceHosts is a list of hosts inside the same failover partition from + which a failover event towards the target host may originate. + See Spec.TargetHost for the target host of the failover reservation. + items: + type: string + type: array + type: object failoverReservation: description: |- FailoverReservation contains fields specific to failover reservations. Only used when Type is FailoverReservation. + + Deprecated: FailoverReservations will be replaced with FailoverHostReservations. properties: resourceGroup: description: ResourceGroup is the group/category of the resource @@ -229,6 +249,7 @@ spec: - CommittedResourceReservation - FailoverReservation - InFlightReservation + - FailoverHostReservation type: string required: - type @@ -320,10 +341,23 @@ spec: - type type: object type: array + failoverHostReservation: + description: |- + FailoverHostReservation contains status fields specific to failover + host reservations. Only used when Type is FailoverHostReservation. + properties: + sourceHost: + description: |- + SourceHost is the actual host from which the failover event originates. + Only set when the condition ConditionTypeFailoverHostInUse is True. + type: string + type: object failoverReservation: description: |- FailoverReservation contains status fields specific to failover reservations. Only used when Type is FailoverReservation. + + Deprecated: FailoverReservations will be replaced with FailoverHostReservations. properties: acknowledgedAt: description: |-