@@ -212,6 +212,8 @@ func (r *ClusterAddonReconciler) Reconcile(ctx context.Context, req reconcile.Re
212212 return reconcile.Result {}, fmt .Errorf ("failed to get cluster addon config path: %w" , err )
213213 }
214214
215+ logger := log .FromContext (ctx )
216+
215217 // Check whether current Helm chart has been applied in the workload cluster. If not, then we need to apply the helm chart (again).
216218 // the spec.clusterStack is only set after a Helm chart from a ClusterStack has been applied successfully.
217219 // If it is not set, the Helm chart has never been applied.
@@ -250,6 +252,8 @@ func (r *ClusterAddonReconciler) Reconcile(ctx context.Context, req reconcile.Re
250252 conditions .MarkTrue (clusterAddon , csov1alpha1 .HelmChartAppliedCondition )
251253 }
252254
255+ clusterAddon .SetStageAnnotations (csov1alpha1 .StageCreated )
256+ clusterAddon .Spec .Hook = ""
253257 clusterAddon .Spec .ClusterStack = cluster .Spec .Topology .Class
254258 clusterAddon .Status .Ready = true
255259 return ctrl.Result {}, nil
@@ -271,11 +275,14 @@ func (r *ClusterAddonReconciler) Reconcile(ctx context.Context, req reconcile.Re
271275 // set condition that helm chart has been applied successfully
272276 conditions .MarkTrue (clusterAddon , csov1alpha1 .HelmChartAppliedCondition )
273277 }
278+
279+ clusterAddon .Spec .Hook = ""
280+ clusterAddon .SetStageAnnotations (csov1alpha1 .StageCreated )
274281 clusterAddon .Status .Ready = true
275282 return ctrl.Result {}, nil
276283
277284 } else {
278- in .addonStagesInput , err = r .getAddonStagesInput (clusterAddon , in .restConfig , in .clusterAddonChartPath )
285+ in .addonStagesInput , err = r .getAddonStagesInput (in .restConfig , in .clusterAddonChartPath )
279286 if err != nil {
280287 return reconcile.Result {}, fmt .Errorf ("failed to get addon stages input: %w" , err )
281288 }
@@ -305,7 +312,7 @@ func (r *ClusterAddonReconciler) Reconcile(ctx context.Context, req reconcile.Re
305312 in .oldDestinationClusterAddonChartDir = strings .TrimSuffix (oldRelease .ClusterAddonChartPath (), ".tgz" )
306313
307314 if err := unTarContent (oldRelease .ClusterAddonChartPath (), in .oldDestinationClusterAddonChartDir ); err != nil {
308- return reconcile.Result {}, fmt .Errorf ("failed to untar cluster addon chart: %q: %w" , oldRelease .ClusterAddonChartPath (), err )
315+ return reconcile.Result {}, fmt .Errorf ("failed to untar old cluster stack cluster addon chart: %q: %w" , oldRelease .ClusterAddonChartPath (), err )
309316 }
310317 }
311318
@@ -397,7 +404,39 @@ func (r *ClusterAddonReconciler) Reconcile(ctx context.Context, req reconcile.Re
397404 }
398405 }
399406
407+ logger .Info ("the hook is here" , "hook" , in .clusterAddon .Spec .Hook )
408+
400409 if clusterAddon .Spec .Hook == "AfterControlPlaneInitialized" || clusterAddon .Spec .Hook == "BeforeClusterUpgrade" {
410+ if clusterAddon .Spec .Hook == "BeforeClusterUpgrade" {
411+ // create the list of old release objects
412+ oldClusterStackObjectList , err := r .getOldReleaseObjects (ctx , in , clusterAddonConfig , oldRelease )
413+ if err != nil {
414+ return reconcile.Result {}, fmt .Errorf ("failed to get old cluster stack object list from helm charts: %w" , err )
415+ }
416+ logger .Info ("here is the old cluster stack object list" , "list" , oldClusterStackObjectList )
417+
418+ newClusterStackObjectList , err := r .getNewReleaseObjects (ctx , in , clusterAddonConfig )
419+ if err != nil {
420+ return reconcile.Result {}, fmt .Errorf ("failed to get new cluster stack object list from helm charts: %w" , err )
421+ }
422+
423+ logger .Info ("here in the clean up begins" , "currentList" , newClusterStackObjectList )
424+ shouldReque , err := r .cleanUpResources (ctx , in , oldClusterStackObjectList , newClusterStackObjectList )
425+ logger .Info ("here in the clean up done" , "currentList" , newClusterStackObjectList , "reque" , shouldReque )
426+ if err != nil {
427+ return reconcile.Result {}, fmt .Errorf ("failed to clean up resources: %w" , err )
428+ }
429+ if shouldReque {
430+ return reconcile.Result {RequeueAfter : 20 * time .Second }, nil
431+ }
432+
433+ // set upgrade annotation once done
434+ clusterAddon .SetStageAnnotations (csov1alpha1 .StageUpgraded )
435+ }
436+
437+ // if upgrade annotation is not present add the create annotation
438+ clusterAddon .SetStageAnnotations (csov1alpha1 .StageCreated )
439+
401440 clusterAddon .Spec .ClusterStack = cluster .Spec .Topology .Class
402441 }
403442
@@ -419,6 +458,153 @@ func (r *ClusterAddonReconciler) Reconcile(ctx context.Context, req reconcile.Re
419458 return ctrl.Result {}, nil
420459}
421460
461+ func (r * ClusterAddonReconciler ) getNewReleaseObjects (ctx context.Context , in templateAndApplyClusterAddonInput , clusterAddonConfig clusteraddon.ClusterAddonConfig ) ([]* csov1alpha1.Resource , error ) {
462+ var (
463+ newBuildTemplate []byte
464+ resources []* csov1alpha1.Resource
465+ )
466+
467+ for _ , stage := range clusterAddonConfig .AddonStages [in .clusterAddon .Spec .Hook ] {
468+ if _ , err := os .Stat (filepath .Join (in .newDestinationClusterAddonChartDir , stage .HelmChartName , release .OverwriteYaml )); err == nil {
469+ newBuildTemplate , err = buildTemplateFromClusterAddonValues (ctx , filepath .Join (in .newDestinationClusterAddonChartDir , stage .HelmChartName , release .OverwriteYaml ), in .cluster , r .Client , true )
470+ if err != nil {
471+ return nil , fmt .Errorf ("failed to build template from new cluster addon values of the latest cluster stack: %w" , err )
472+ }
473+ }
474+
475+ helmTemplate , err := helmTemplateClusterAddon (filepath .Join (in .newDestinationClusterAddonChartDir , stage .HelmChartName ), newBuildTemplate )
476+ if err != nil {
477+ return nil , fmt .Errorf ("failed to template new helm chart of the latest cluster stack: %w" , err )
478+ }
479+
480+ resource , err := kube .GetResourcesFromHelmTemplate (helmTemplate )
481+ if err != nil {
482+ return nil , fmt .Errorf ("failed to get resources form old cluster stack helm template of the latest cluster stack: %w" , err )
483+ }
484+
485+ if stage .Action == clusteraddon .Apply {
486+ resources = append (resources , resource ... )
487+ } else {
488+ resources = removeResourcesFromCurrentListOfObjects (resources , resource )
489+ }
490+ }
491+
492+ return resources , nil
493+ }
494+
495+ // getOldReleaseObjects returns the old cluster stack objects in the workload cluster.
496+ func (r * ClusterAddonReconciler ) getOldReleaseObjects (ctx context.Context , in templateAndApplyClusterAddonInput , clusterAddonConfig clusteraddon.ClusterAddonConfig , oldRelease release.Release ) ([]* csov1alpha1.Resource , error ) {
497+ // clusteraddon.yaml
498+ clusterAddonConfigPath , err := r .getClusterAddonConfigPath (in .clusterAddon .Spec .ClusterStack )
499+ if err != nil {
500+ return nil , fmt .Errorf ("failed to get old cluster stack cluster addon config path: %w" , err )
501+ }
502+
503+ if _ , err := os .Stat (clusterAddonConfigPath ); err != nil {
504+ if ! os .IsNotExist (err ) {
505+ return nil , fmt .Errorf ("failed to verify the clusteraddon.yaml on old cluster stack release path %q with error: %w" , clusterAddonConfigPath , err )
506+ }
507+
508+ // this is the old way
509+ buildTemplate , err := buildTemplateFromClusterAddonValues (ctx , oldRelease .ClusterAddonValuesPath (), in .cluster , r .Client , false )
510+ if err != nil {
511+ return nil , fmt .Errorf ("failed to build template from the old cluster stack cluster addon values: %w" , err )
512+ }
513+
514+ helmTemplate , err := helmTemplateClusterAddon (oldRelease .ClusterAddonChartPath (), buildTemplate )
515+ if err != nil {
516+ return nil , fmt .Errorf ("failed to template helm chart: %w" , err )
517+ }
518+
519+ resources , err := kube .GetResourcesFromHelmTemplate (helmTemplate )
520+ if err != nil {
521+ return nil , fmt .Errorf ("failed to get resources form old cluster stack helm template: %w" , err )
522+ }
523+
524+ return resources , nil
525+ }
526+
527+ // this is the new way
528+ // Read all the helm charts in new the un-tared cluster addon.
529+
530+ var (
531+ newBuildTemplate []byte
532+ resources []* csov1alpha1.Resource
533+
534+ hook string
535+ )
536+
537+ if in .clusterAddon .HasStageAnnotation (csov1alpha1 .StageCreated ) {
538+ hook = "AfterControlPlaneInitialized"
539+ } else {
540+ hook = "BeforeClusterUpgrade"
541+ }
542+
543+ for _ , stage := range clusterAddonConfig .AddonStages [hook ] {
544+ if _ , err := os .Stat (filepath .Join (in .oldDestinationClusterAddonChartDir , stage .HelmChartName , release .OverwriteYaml )); err == nil {
545+ newBuildTemplate , err = buildTemplateFromClusterAddonValues (ctx , filepath .Join (in .oldDestinationClusterAddonChartDir , stage .HelmChartName , release .OverwriteYaml ), in .cluster , r .Client , true )
546+ if err != nil {
547+ return nil , fmt .Errorf ("failed to build template from new cluster addon values: %w" , err )
548+ }
549+ }
550+
551+ helmTemplate , err := helmTemplateClusterAddon (filepath .Join (in .oldDestinationClusterAddonChartDir , stage .HelmChartName ), newBuildTemplate )
552+ if err != nil {
553+ return nil , fmt .Errorf ("failed to template new helm chart: %w" , err )
554+ }
555+
556+ resource , err := kube .GetResourcesFromHelmTemplate (helmTemplate )
557+ if err != nil {
558+ return nil , fmt .Errorf ("failed to get resources form old cluster stack helm template: %w" , err )
559+ }
560+
561+ if stage .Action == clusteraddon .Apply {
562+ resources = append (resources , resource ... )
563+ } else {
564+ resources = removeResourcesFromCurrentListOfObjects (resources , resource )
565+ }
566+ }
567+
568+ return resources , nil
569+ }
570+
571+ func (r * ClusterAddonReconciler ) cleanUpResources (ctx context.Context , in templateAndApplyClusterAddonInput , oldList , newList []* csov1alpha1.Resource ) (shouldRequeue bool , err error ) {
572+ // Create a map of items in the new slice for faster lookup
573+ newMap := make (map [* csov1alpha1.Resource ]bool )
574+ for _ , item := range newList {
575+ newMap [item ] = true
576+ }
577+
578+ // Find extra objects in the old slice
579+ var extraResources []* csov1alpha1.Resource
580+ for _ , item := range oldList {
581+ if ! newMap [item ] {
582+ extraResources = append (extraResources , item )
583+ }
584+ }
585+ logger := log .FromContext (ctx )
586+
587+ logger .Info ("diff in resources" , "diff" , extraResources )
588+
589+ for _ , resource := range extraResources {
590+ if resource .Namespace == "" {
591+ resource .Namespace = clusterAddonNamespace
592+ }
593+ dr , err := kube .GetDynamicResourceInterface (resource .Namespace , in .restConfig , resource .GroupVersionKind ())
594+ if err != nil {
595+ return false , fmt .Errorf ("failed to get dynamic resource interface: %w" , err )
596+ }
597+
598+ if err := dr .Delete (ctx , resource .Name , metav1.DeleteOptions {}); err != nil && ! apierrors .IsNotFound (err ) {
599+ reterr := fmt .Errorf ("failed to delete object %q: %w" , resource .GroupVersionKind (), err )
600+ resource .Error = reterr .Error ()
601+ shouldRequeue = true
602+ }
603+ }
604+
605+ return shouldRequeue , nil
606+ }
607+
422608func (r * ClusterAddonReconciler ) getClusterAddonConfigPath (clusterClassName string ) (string , error ) {
423609 // path to the clusteraddon config /tmp/cluster-stacks/docker-ferrol-1-27-v1/clusteraddon.yaml
424610 // if present then new way of cluster stack otherwise old way.
@@ -455,7 +641,7 @@ type addonStagesInput struct {
455641 oldDestinationClusterAddonChartDir string
456642}
457643
458- func (r * ClusterAddonReconciler ) getAddonStagesInput (clusterAddon * csov1alpha1. ClusterAddon , restConfig * rest.Config , clusterAddonChart string ) (addonStagesInput , error ) {
644+ func (r * ClusterAddonReconciler ) getAddonStagesInput (restConfig * rest.Config , clusterAddonChart string ) (addonStagesInput , error ) {
459645 var (
460646 addonStages addonStagesInput
461647 err error
@@ -476,7 +662,7 @@ func (r *ClusterAddonReconciler) getAddonStagesInput(clusterAddon *csov1alpha1.C
476662 // dst - /tmp/cluster-stacks/docker-ferrol-1-27-v1/docker-ferrol-1-27-cluster-addon-v1/
477663 addonStages .newDestinationClusterAddonChartDir = strings .TrimSuffix (clusterAddonChart , ".tgz" )
478664 if err := unTarContent (clusterAddonChart , addonStages .newDestinationClusterAddonChartDir ); err != nil {
479- return addonStagesInput {}, fmt .Errorf ("failed to untar cluster addon chart: %q: %w" , clusterAddonChart , err )
665+ return addonStagesInput {}, fmt .Errorf ("failed to untar new cluster stack cluster addon chart: %q: %w" , clusterAddonChart , err )
480666 }
481667
482668 // Read all the helm charts in the un-tared cluster addon.
@@ -517,6 +703,7 @@ func (r *ClusterAddonReconciler) templateAndApplyClusterAddonHelmChart(ctx conte
517703 }
518704
519705 in .clusterAddon .Status .Resources = newResources
706+
520707 return shouldRequeue , nil
521708}
522709
@@ -600,7 +787,7 @@ check:
600787 } else {
601788 // Delete part
602789 logger .V (1 ).Info ("starting to delete helm chart" , "clusterStack" , in .clusterAddon .Spec .ClusterStack , "helm chart" , stage .HelmChartName , "hook" , in .clusterAddon .Spec .Hook )
603- shouldRequeue , err = helmTemplateAndDeleteNewClusterStack (ctx , in , stage .HelmChartName )
790+ shouldRequeue , err = r . helmTemplateAndDeleteNewClusterStack (ctx , in , stage .HelmChartName )
604791 if err != nil {
605792 return false , fmt .Errorf ("failed to delete helm chart: %w" , err )
606793 }
@@ -729,6 +916,7 @@ func (r *ClusterAddonReconciler) templateAndApplyNewClusterStackAddonHelmChart(c
729916 if in .oldDestinationClusterAddonChartDir != "" {
730917 oldClusterStackSubDirPath := filepath .Join (in .oldDestinationClusterAddonChartDir , helmChartName )
731918
919+ // we skip helm templating if last cluster stack don't follow the new convention.
732920 if _ , err := os .Stat (filepath .Join (oldClusterStackSubDirPath , release .OverwriteYaml )); err == nil {
733921 oldBuildTemplate , err = buildTemplateFromClusterAddonValues (ctx , filepath .Join (oldClusterStackSubDirPath , release .OverwriteYaml ), in .cluster , r .Client , true )
734922 if err != nil {
@@ -761,12 +949,13 @@ func (r *ClusterAddonReconciler) templateAndApplyNewClusterStackAddonHelmChart(c
761949 return false , fmt .Errorf ("failed to apply objects from cluster addon Helm chart: %w" , err )
762950 }
763951
952+ // This is for the current stage objects and will be removed once done.
764953 in .clusterAddon .Status .Resources = newResources
765954
766955 return shouldRequeue , nil
767956}
768957
769- func helmTemplateAndDeleteNewClusterStack (ctx context.Context , in templateAndApplyClusterAddonInput , helmChartName string ) (bool , error ) {
958+ func ( r * ClusterAddonReconciler ) helmTemplateAndDeleteNewClusterStack (ctx context.Context , in templateAndApplyClusterAddonInput , helmChartName string ) (bool , error ) {
770959 var (
771960 buildTemplate []byte
772961 err error
@@ -778,12 +967,13 @@ func helmTemplateAndDeleteNewClusterStack(ctx context.Context, in templateAndApp
778967 return false , fmt .Errorf ("failed to template new helm chart: %w" , err )
779968 }
780969
781- newResources , shouldRequeue , err := in .kubeClient .DeleteNewClusterStack (ctx , newHelmTemplate )
970+ deletedResources , shouldRequeue , err := in .kubeClient .DeleteNewClusterStack (ctx , newHelmTemplate )
782971 if err != nil {
783972 return false , fmt .Errorf ("failed to delete objects from cluster addon Helm chart: %w" , err )
784973 }
785974
786- in .clusterAddon .Status .Resources = newResources
975+ // This is for the current stage objects and will be removed once done.
976+ in .clusterAddon .Status .Resources = deletedResources
787977
788978 return shouldRequeue , nil
789979}
@@ -1096,6 +1286,10 @@ func unTarContent(src, dst string) error {
10961286 }
10971287 case tar .TypeReg :
10981288 // Create regular files
1289+ if err := os .MkdirAll (filepath .Dir (targetPath ), os .ModePerm ); err != nil {
1290+ return fmt .Errorf ("%q: creating directory: %w" , filepath .Dir (targetPath ), err )
1291+ }
1292+
10991293 outputFile , err := os .Create (filepath .Clean (targetPath ))
11001294 if err != nil {
11011295 return fmt .Errorf ("%q: creating file: %w" , targetPath , err )
@@ -1118,3 +1312,21 @@ func unTarContent(src, dst string) error {
11181312
11191313 return nil
11201314}
1315+
1316+ func removeResourcesFromCurrentListOfObjects (baseList []* csov1alpha1.Resource , itemsToRemove []* csov1alpha1.Resource ) []* csov1alpha1.Resource {
1317+ // Create a map of items to remove for faster lookup
1318+ itemsMap := make (map [* csov1alpha1.Resource ]bool )
1319+ for _ , item := range itemsToRemove {
1320+ itemsMap [item ] = true
1321+ }
1322+
1323+ // Create a new list without the items to remove
1324+ var newList []* csov1alpha1.Resource
1325+ for _ , item := range baseList {
1326+ if ! itemsMap [item ] {
1327+ newList = append (newList , item )
1328+ }
1329+ }
1330+
1331+ return newList
1332+ }
0 commit comments