@@ -25,6 +25,7 @@ import (
2525 "fmt"
2626 "reflect"
2727 "slices"
28+ "strconv"
2829
2930 "github.com/imdario/mergo"
3031 "github.com/pkg/errors"
@@ -38,6 +39,8 @@ import (
3839
3940 appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1"
4041 parametersv1alpha1 "github.com/apecloud/kubeblocks/apis/parameters/v1alpha1"
42+ workloads "github.com/apecloud/kubeblocks/apis/workloads/v1"
43+ reconfigurectrl "github.com/apecloud/kubeblocks/controllers/parameters/reconfigure"
4144 "github.com/apecloud/kubeblocks/pkg/constant"
4245 "github.com/apecloud/kubeblocks/pkg/controller/builder"
4346 "github.com/apecloud/kubeblocks/pkg/controller/component"
@@ -104,8 +107,18 @@ func (r *ComponentDrivenParameterReconciler) reconcile(reqCtx intctrlutil.Reques
104107 return intctrlutil .CheckedRequeueWithError (err , reqCtx .Log , "" )
105108 }
106109 if model .IsObjectDeleting (component ) {
110+ if err = r .syncLegacyConfigManagerRequirement (reqCtx , component , false ); err != nil {
111+ return intctrlutil .RequeueWithError (err , reqCtx .Log , errors .Wrap (err , "failed to clear legacy config-manager compatibility annotation" ).Error ())
112+ }
107113 return r .delete (reqCtx , existingObject )
108114 }
115+ required , err := resolveLegacyConfigManagerRequirement (reqCtx .Ctx , r .Client , component )
116+ if err != nil {
117+ return intctrlutil .RequeueWithError (err , reqCtx .Log , "" )
118+ }
119+ if err = r .syncLegacyConfigManagerRequirement (reqCtx , component , required ); err != nil {
120+ return intctrlutil .RequeueWithError (err , reqCtx .Log , errors .Wrap (err , "failed to sync legacy config-manager compatibility annotation" ).Error ())
121+ }
109122 if expectedObject , err = buildComponentParameter (reqCtx , r .Client , component ); err != nil {
110123 return intctrlutil .RequeueWithError (err , reqCtx .Log , "" )
111124 }
@@ -151,6 +164,67 @@ func (r *ComponentDrivenParameterReconciler) update(reqCtx intctrlutil.RequestCt
151164 return intctrlutil .Reconciled ()
152165}
153166
167+ func (r * ComponentDrivenParameterReconciler ) syncLegacyConfigManagerRequirement (reqCtx intctrlutil.RequestCtx , comp * appsv1.Component , required bool ) error {
168+ clusterName , _ := component .GetClusterName (comp )
169+ clusterKey := types.NamespacedName {Namespace : comp .Namespace , Name : clusterName }
170+ cluster := & appsv1.Cluster {}
171+ if err := r .Client .Get (reqCtx .Ctx , clusterKey , cluster ); err != nil {
172+ return client .IgnoreNotFound (err )
173+ }
174+ aggregated , err := r .resolveClusterLegacyConfigManagerRequirement (reqCtx .Ctx , cluster , comp , required )
175+ if err != nil {
176+ return err
177+ }
178+ desiredValue := strconv .FormatBool (aggregated )
179+ currentValue := ""
180+ if cluster .Annotations != nil {
181+ currentValue = cluster .Annotations [constant .LegacyConfigManagerRequiredAnnotationKey ]
182+ }
183+ if currentValue == desiredValue {
184+ return nil
185+ }
186+ patch := client .MergeFrom (cluster .DeepCopy ())
187+ if cluster .Annotations == nil {
188+ cluster .Annotations = map [string ]string {}
189+ }
190+ // Keep an explicit "false" marker instead of deleting the key. The compatible
191+ // config-manager cleanup flow treats a missing key as "unknown, keep legacy
192+ // resources" during controller upgrade races, while "false" means cleanup is
193+ // now safe when the workload naturally recreates Pods.
194+ cluster .Annotations [constant .LegacyConfigManagerRequiredAnnotationKey ] = desiredValue
195+ return r .Client .Patch (reqCtx .Ctx , cluster , patch )
196+ }
197+
198+ func (r * ComponentDrivenParameterReconciler ) resolveClusterLegacyConfigManagerRequirement (ctx context.Context , cluster * appsv1.Cluster , currentComp * appsv1.Component , currentRequired bool ) (bool , error ) {
199+ if cluster == nil {
200+ return false , nil
201+ }
202+ compList := & appsv1.ComponentList {}
203+ if err := r .Client .List (ctx , compList , client .InNamespace (cluster .Namespace ), client.MatchingLabels {constant .AppInstanceLabelKey : cluster .Name }); err != nil {
204+ return false , err
205+ }
206+ for i := range compList .Items {
207+ comp := & compList .Items [i ]
208+ if currentComp != nil && comp .Name == currentComp .Name {
209+ if currentRequired && ! model .IsObjectDeleting (comp ) {
210+ return true , nil
211+ }
212+ continue
213+ }
214+ if model .IsObjectDeleting (comp ) {
215+ continue
216+ }
217+ required , err := resolveLegacyConfigManagerRequirement (ctx , r .Client , comp )
218+ if err != nil {
219+ return false , err
220+ }
221+ if required {
222+ return true , nil
223+ }
224+ }
225+ return false , nil
226+ }
227+
154228func runningComponentParameter (reqCtx intctrlutil.RequestCtx , reader client.Reader , comp * appsv1.Component ) (* parametersv1alpha1.ComponentParameter , error ) {
155229 var componentParameter = & parametersv1alpha1.ComponentParameter {}
156230
@@ -237,6 +311,49 @@ func buildComponentParameter(reqCtx intctrlutil.RequestCtx, reader client.Reader
237311 return parameterObj , err
238312}
239313
314+ // resolveLegacyConfigManagerRequirement reports whether this component still depends on the
315+ // legacy config-manager compatibility path. The requirement exists only when the parameters
316+ // definition still uses legacy actions and the running workload still carries the injected
317+ // config-manager sidecar from older releases.
318+ func resolveLegacyConfigManagerRequirement (ctx context.Context , reader client.Reader , comp * appsv1.Component ) (bool , error ) {
319+ cmpd , err := getCompDefinition (ctx , reader , comp .Spec .CompDef )
320+ if err != nil {
321+ return false , err
322+ }
323+ _ , paramsDefs , err := parameters .ResolveCmpdParametersDefs (ctx , reader , cmpd )
324+ if err != nil {
325+ return false , err
326+ }
327+ if ! parameters .LegacyConfigManagerRequiredForParamsDefs (paramsDefs ) {
328+ return false , nil
329+ }
330+ its , err := resolveLegacyConfigManagerWorkload (ctx , reader , comp )
331+ if client .IgnoreNotFound (err ) != nil {
332+ return false , err
333+ }
334+ return reconfigurectrl .HasLegacyConfigManagerRuntime (its ), nil
335+ }
336+
337+ func resolveLegacyConfigManagerWorkload (ctx context.Context , reader client.Reader , comp * appsv1.Component ) (* workloads.InstanceSet , error ) {
338+ clusterName , err := component .GetClusterName (comp )
339+ if err != nil {
340+ return nil , err
341+ }
342+ componentName , err := component .ShortName (clusterName , comp .Name )
343+ if err != nil {
344+ return nil , err
345+ }
346+ its := & workloads.InstanceSet {}
347+ key := types.NamespacedName {
348+ Namespace : comp .Namespace ,
349+ Name : constant .GenerateWorkloadNamePattern (clusterName , componentName ),
350+ }
351+ if err := reader .Get (ctx , key , its ); err != nil {
352+ return nil , err
353+ }
354+ return its , nil
355+ }
356+
240357func handleCustomParameterTemplate (ctx context.Context , reader client.Reader , annotations map [string ]string , specs []parametersv1alpha1.ConfigTemplateItemDetail ) error {
241358 if len (annotations ) == 0 {
242359 return nil
0 commit comments