From 7cd51131fefeaa33901109df97336274216632c1 Mon Sep 17 00:00:00 2001 From: William Saakyan Date: Wed, 5 Feb 2025 14:25:29 +0100 Subject: [PATCH] Support old UpdateSelectors too --- api/v1/ytsaurus_types.go | 31 +++++-- api/v1/zz_generated.deepcopy.go | 20 +++++ .../bases/cluster.ytsaurus.tech_ytsaurus.yaml | 3 +- controllers/sync.go | 82 +++++++++++-------- docs/api.md | 28 +++++-- .../crds/ytsaurus.cluster.ytsaurus.tech.yaml | 3 +- 6 files changed, 111 insertions(+), 56 deletions(-) diff --git a/api/v1/ytsaurus_types.go b/api/v1/ytsaurus_types.go index 44b34097..3367ba69 100644 --- a/api/v1/ytsaurus_types.go +++ b/api/v1/ytsaurus_types.go @@ -673,7 +673,7 @@ type YtsaurusSpec struct { EnableFullUpdate bool `json:"enableFullUpdate"` //+optional //+optional - // Deprecated: UpdateSelector is an experimental field. Behaviour may change. + // Deprecated: UpdateSelector is going to be removed soon. Please use UpdateSelectors instead. UpdateSelector UpdateSelector `json:"updateSelector"` //+optional @@ -761,6 +761,27 @@ type TabletCellBundleInfo struct { type UpdateSelector string +const ( + // UpdateSelectorUnspecified means that selector is disabled and would be ignored completely. + UpdateSelectorUnspecified UpdateSelector = "" + // UpdateSelectorNothing means that no component could be updated. + UpdateSelectorNothing UpdateSelector = "Nothing" + // UpdateSelectorMasterOnly means that only master could be updated. + UpdateSelectorMasterOnly UpdateSelector = "MasterOnly" + // UpdateSelectorTabletNodesOnly means that only data nodes could be updated + UpdateSelectorDataNodesOnly UpdateSelector = "DataNodesOnly" + // UpdateSelectorTabletNodesOnly means that only tablet nodes could be updated + UpdateSelectorTabletNodesOnly UpdateSelector = "TabletNodesOnly" + // UpdateSelectorExecNodesOnly means that only tablet nodes could be updated + UpdateSelectorExecNodesOnly UpdateSelector = "ExecNodesOnly" + // UpdateSelectorStatelessOnly means that only stateless components (everything but master, data nodes, and tablet nodes) + // could be updated. + UpdateSelectorStatelessOnly UpdateSelector = "StatelessOnly" + // UpdateSelectorEverything means that all components could be updated. + // With this setting and if master or tablet nodes need update all the components would be updated. + UpdateSelectorEverything UpdateSelector = "Everything" +) + type ComponentUpdateSelector struct { //+optional ComponentType consts.ComponentType `json:"componentType,omitempty"` @@ -774,14 +795,6 @@ type ComponentUpdateSelector struct { type UpdateFlow string -const ( - UpdateFlowNone UpdateFlow = "" - UpdateFlowStateless UpdateFlow = "Stateless" - UpdateFlowMaster UpdateFlow = "Master" - UpdateFlowTabletNodes UpdateFlow = "TabletNodes" - UpdateFlowFull UpdateFlow = "Full" -) - type UpdateStatus struct { //+kubebuilder:default:=None State UpdateState `json:"state,omitempty"` diff --git a/api/v1/zz_generated.deepcopy.go b/api/v1/zz_generated.deepcopy.go index 31ab77ac..665c9869 100644 --- a/api/v1/zz_generated.deepcopy.go +++ b/api/v1/zz_generated.deepcopy.go @@ -419,6 +419,21 @@ func (in *Component) DeepCopy() *Component { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ComponentUpdateSelector) DeepCopyInto(out *ComponentUpdateSelector) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ComponentUpdateSelector. +func (in *ComponentUpdateSelector) DeepCopy() *ComponentUpdateSelector { + if in == nil { + return nil + } + out := new(ComponentUpdateSelector) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ControllerAgentsSpec) DeepCopyInto(out *ControllerAgentsSpec) { *out = *in @@ -1999,6 +2014,11 @@ func (in *YtsaurusSpec) DeepCopyInto(out *YtsaurusSpec) { *out = new(OauthServiceSpec) (*in).DeepCopyInto(*out) } + if in.UpdateSelectors != nil { + in, out := &in.UpdateSelectors, &out.UpdateSelectors + *out = make([]ComponentUpdateSelector, len(*in)) + copy(*out, *in) + } if in.NodeSelector != nil { in, out := &in.NodeSelector, &out.NodeSelector *out = make(map[string]string, len(*in)) diff --git a/config/crd/bases/cluster.ytsaurus.tech_ytsaurus.yaml b/config/crd/bases/cluster.ytsaurus.tech_ytsaurus.yaml index 74f5ec20..5f6f7d87 100644 --- a/config/crd/bases/cluster.ytsaurus.tech_ytsaurus.yaml +++ b/config/crd/bases/cluster.ytsaurus.tech_ytsaurus.yaml @@ -39570,8 +39570,7 @@ spec: uiImage: type: string updateSelector: - description: 'Deprecated: UpdateSelector is an experimental field. - Behaviour may change.' + description: 'Deprecated: UpdateSelector is going to be removed soon.' type: string updateSelectors: description: Controls the components that should be updated during diff --git a/controllers/sync.go b/controllers/sync.go index 579c7d8d..35465b25 100644 --- a/controllers/sync.go +++ b/controllers/sync.go @@ -15,59 +15,43 @@ import ( apiProxy "github.com/ytsaurus/ytsaurus-k8s-operator/pkg/apiproxy" ) -func getFlowFromComponent(component consts.ComponentType) ytv1.UpdateFlow { - if component == consts.MasterType { - return ytv1.UpdateFlowMaster - } - if component == consts.TabletNodeType { - return ytv1.UpdateFlowTabletNodes - } - if component == consts.DataNodeType || component == consts.ExecNodeType { - return ytv1.UpdateFlowFull - } - return ytv1.UpdateFlowStateless -} - -func canUpdateComponent(selectors []ytv1.ComponentUpdateSelector, component ytv1.Component) (bool, error) { +func canUpdateComponent(selectors []ytv1.ComponentUpdateSelector, component ytv1.Component) bool { for _, selector := range selectors { - if selector.ComponentType != "" { - if selector.ComponentType == component.Type { - return true, nil - } - } else if selector.ComponentName != "" { + if selector.ComponentName != "" { if selector.ComponentName == component.Name { - return true, nil + return true + } + } else if selector.ComponentType != "" { + if selector.ComponentType == component.Type { + return true } } else { switch selector.ComponentGroup { case consts.ComponentGroupEverything: - return true, nil + return true case consts.ComponentGroupNothing: - return false, nil + return false case consts.ComponentGroupStateful: if component.Type == consts.DataNodeType || component.Type == consts.TabletNodeType { - return true, nil + return true } case consts.ComponentGroupStateless: if component.Type != consts.DataNodeType && component.Type != consts.TabletNodeType && component.Type != consts.MasterType { - return true, nil + return true } default: - return false, fmt.Errorf("unexpected component group %s", selector.ComponentGroup) + return false } } } - return false, nil + return false } // Considers spec and decides if operator should proceed with update or block. // Block case is indicated with non-empty blockMsg. // If update is not blocked, updateMeta containing a chosen flow and the component names to update returned. func chooseUpdatingComponents(spec ytv1.YtsaurusSpec, needUpdate []components.Component, allComponents []components.Component) (components []ytv1.Component, blockMsg string) { - configuredSelectors := spec.UpdateSelectors - if len(configuredSelectors) == 0 && spec.EnableFullUpdate { - configuredSelectors = []ytv1.ComponentUpdateSelector{{ComponentGroup: consts.ComponentGroupEverything}} - } + configuredSelectors := getEffectiveSelectors(spec) needFullUpdate := false var canUpdate []ytv1.Component @@ -78,16 +62,13 @@ func chooseUpdatingComponents(spec ytv1.YtsaurusSpec, needUpdate []components.Co Name: comp.GetName(), Type: comp.GetType(), } - upd, err := canUpdateComponent(configuredSelectors, component) - if err != nil { - return nil, err.Error() - } + upd := canUpdateComponent(configuredSelectors, component) if upd { canUpdate = append(canUpdate, component) } else { cannotUpdate = append(cannotUpdate, component) } - statelessCheck, err := canUpdateComponent([]ytv1.ComponentUpdateSelector{{ComponentGroup: consts.ComponentGroupStateless}}, component) + statelessCheck := canUpdateComponent([]ytv1.ComponentUpdateSelector{{ComponentGroup: consts.ComponentGroupStateless}}, component) if !statelessCheck && component.Type != consts.DataNodeType { needFullUpdate = true } @@ -110,6 +91,37 @@ func chooseUpdatingComponents(spec ytv1.YtsaurusSpec, needUpdate []components.Co return canUpdate, "" } +func getEffectiveSelectors(spec ytv1.YtsaurusSpec) []ytv1.ComponentUpdateSelector { + if len(spec.UpdateSelectors) != 0 { + return spec.UpdateSelectors + } + + if spec.UpdateSelector != ytv1.UpdateSelectorUnspecified { + switch spec.UpdateSelector { + case ytv1.UpdateSelectorNothing: + return []ytv1.ComponentUpdateSelector{{ComponentGroup: consts.ComponentGroupNothing}} + case ytv1.UpdateSelectorMasterOnly: + return []ytv1.ComponentUpdateSelector{{ComponentType: consts.MasterType}} + case ytv1.UpdateSelectorDataNodesOnly: + return []ytv1.ComponentUpdateSelector{{ComponentType: consts.DataNodeType}} + case ytv1.UpdateSelectorTabletNodesOnly: + return []ytv1.ComponentUpdateSelector{{ComponentType: consts.TabletNodeType}} + case ytv1.UpdateSelectorExecNodesOnly: + return []ytv1.ComponentUpdateSelector{{ComponentType: consts.ExecNodeType}} + case ytv1.UpdateSelectorStatelessOnly: + return []ytv1.ComponentUpdateSelector{{ComponentGroup: consts.ComponentGroupStateless}} + case ytv1.UpdateSelectorEverything: + return []ytv1.ComponentUpdateSelector{{ComponentGroup: consts.ComponentGroupEverything}} + } + } + + if spec.EnableFullUpdate { + return []ytv1.ComponentUpdateSelector{{ComponentGroup: consts.ComponentGroupEverything}} + } + + return []ytv1.ComponentUpdateSelector{{ComponentGroup: consts.ComponentGroupStateless}} +} + func convertToComponent(components []components.Component) []ytv1.Component { var result []ytv1.Component for _, c := range components { diff --git a/docs/api.md b/docs/api.md index 1677ec60..5c537090 100644 --- a/docs/api.md +++ b/docs/api.md @@ -344,6 +344,24 @@ _Appears in:_ | `type` _[ComponentType](#componenttype)_ | | | | +#### ComponentUpdateSelector + + + + + + + +_Appears in:_ +- [YtsaurusSpec](#ytsaurusspec) + +| Field | Description | Default | Validation | +| --- | --- | --- | --- | +| `componentType` _[ComponentType](#componenttype)_ | | | | +| `componentGroup` _[ComponentGroup](#componentgroup)_ | | | | +| `componentName` _string_ | | | | + + #### ControllerAgentsSpec @@ -2134,13 +2152,6 @@ _Underlying type:_ _string_ _Appears in:_ - [UpdateStatus](#updatestatus) -| Field | Description | -| --- | --- | -| `` | | -| `Stateless` | | -| `Master` | | -| `TabletNodes` | | -| `Full` | | #### UpdateSelector @@ -2317,7 +2328,8 @@ _Appears in:_ | `oauthService` _[OauthServiceSpec](#oauthservicespec)_ | | | | | `isManaged` _boolean_ | | true | | | `enableFullUpdate` _boolean_ | | true | | -| `updateSelector` _[UpdateSelector](#updateselector)_ | UpdateSelector is an experimental field. Behaviour may change.
If UpdateSelector is not empty EnableFullUpdate is ignored. | | Enum: [ Nothing MasterOnly DataNodesOnly TabletNodesOnly ExecNodesOnly StatelessOnly Everything]
| +| `updateSelector` _[UpdateSelector](#updateselector)_ | Deprecated: UpdateSelector is going to be removed soon. Please use UpdateSelectors instead. | | | +| `updateSelectors` _[ComponentUpdateSelector](#componentupdateselector) array_ | Controls the components that should be updated during the update process. | | | | `nodeSelector` _object (keys:string, values:string)_ | | | | | `tolerations` _[Toleration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#toleration-v1-core) array_ | | | | | `dnsConfig` _[PodDNSConfig](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#poddnsconfig-v1-core)_ | DNSConfig allows customizing the DNS settings for the pods. | | | diff --git a/ytop-chart/templates/crds/ytsaurus.cluster.ytsaurus.tech.yaml b/ytop-chart/templates/crds/ytsaurus.cluster.ytsaurus.tech.yaml index 0a27bef5..cd3a35b5 100644 --- a/ytop-chart/templates/crds/ytsaurus.cluster.ytsaurus.tech.yaml +++ b/ytop-chart/templates/crds/ytsaurus.cluster.ytsaurus.tech.yaml @@ -39581,8 +39581,7 @@ spec: uiImage: type: string updateSelector: - description: 'Deprecated: UpdateSelector is an experimental field. - Behaviour may change.' + description: 'Deprecated: UpdateSelector is going to be removed soon.' type: string updateSelectors: description: Controls the components that should be updated during