github.com/verrazzano/verrazzano@v1.7.0/platform-operator/experimental/controllers/integration/single/reconciler.go (about) 1 // Copyright (c) 2023, Oracle and/or its affiliates. 2 // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 3 4 package single 5 6 import ( 7 ctx "context" 8 "fmt" 9 "github.com/verrazzano/verrazzano-modules/pkg/controller/result" 10 "github.com/verrazzano/verrazzano-modules/pkg/controller/spi/controllerspi" 11 "github.com/verrazzano/verrazzano-modules/pkg/helm" 12 "github.com/verrazzano/verrazzano-modules/pkg/vzlog" 13 "github.com/verrazzano/verrazzano/platform-operator/controllers/verrazzano/component/certmanager/certmanager" 14 "github.com/verrazzano/verrazzano/platform-operator/controllers/verrazzano/component/common" 15 "github.com/verrazzano/verrazzano/platform-operator/controllers/verrazzano/component/fluentoperator" 16 "github.com/verrazzano/verrazzano/platform-operator/controllers/verrazzano/component/istio" 17 "github.com/verrazzano/verrazzano/platform-operator/experimental/event" 18 "github.com/verrazzano/verrazzano/platform-operator/internal/config" 19 corev1 "k8s.io/api/core/v1" 20 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 21 "k8s.io/apimachinery/pkg/runtime" 22 "os" 23 "path" 24 ) 25 26 // Create ModuleIntegrateAllRequestEvent for these modules 27 var requireIntegrateAll = map[string]bool{ 28 certmanager.ComponentName: true, 29 fluentoperator.ComponentName: true, 30 istio.ComponentName: true, 31 common.PrometheusOperatorComponentName: true, 32 } 33 34 // Reconcile reconciles the IntegrateSingleRequestEvent (in the form of a configmap) 35 // to perform integration for a single module. Certain modules, such as prometheus-operator, 36 // require that all integration charts for other modules be installed/upgraded. So in addition 37 // to applying the chart for a single module, this reconciler may create second event, 38 // the IntegrateCascadeRequestEvent which processed by the cascade integration controller. 39 func (r Reconciler) Reconcile(spictx controllerspi.ReconcileContext, u *unstructured.Unstructured) result.Result { 40 log := vzlog.DefaultLogger() 41 42 // Get the configmap and convert into an event 43 cm := &corev1.ConfigMap{} 44 if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, cm); err != nil { 45 spictx.Log.ErrorfThrottled(err.Error()) 46 // This is a fatal error, don't requeue 47 return result.NewResult() 48 } 49 ev, err := event.ConfigMapToModuleIntegrationEvent(log, cm) 50 if err != nil { 51 spictx.Log.ErrorfThrottled(err.Error()) 52 return result.NewResultShortRequeueDelayWithError(err) 53 } 54 55 // Either delete the Helm release or apply the integration chart to install/update the release 56 if ev.Action == event.Deleted { 57 res := r.deleteIntegrationRelease(log, ev) 58 if res.ShouldRequeue() { 59 return res 60 } 61 } else { 62 res := r.applyIntegrationChart(log, ev) 63 if res.ShouldRequeue() { 64 return res 65 } 66 } 67 68 // If needed, create an IntegrateCascadeRequestEvent using the same payload as the module event that was just processed 69 // this is needed to integrate related modules affected by this module 70 if ev.Cascade { 71 _, ok := requireIntegrateAll[ev.ModuleName] 72 if ok { 73 res := event.CreateModuleIntegrationCascadeEvent(log, r.Client, ev) 74 if res.ShouldRequeue() { 75 return res 76 } 77 } 78 } 79 80 // Delete the event. This is safe to do since the integration controller 81 // is the only controller processing IntegrateSingleRequestEvent events 82 if err := r.Client.Delete(ctx.TODO(), cm); err != nil { 83 log.ErrorfThrottled("Failed to delete event configmap %s", cm.Name) 84 return result.NewResultShortRequeueDelayWithError(err) 85 } 86 return result.NewResult() 87 } 88 89 // applyIntegrationChart applies the integration chart for the module if chart exists 90 func (r Reconciler) applyIntegrationChart(log vzlog.VerrazzanoLogger, ev *event.ModuleIntegrationEvent) result.Result { 91 // Get the chart directories 92 itegrationChartsDir := config.GetIntegrationChartsDir() 93 94 // Nothing to do if an integration chart doesn't exist for this module 95 moduleChartDir := path.Join(itegrationChartsDir, ev.ModuleName) 96 _, err := os.Stat(moduleChartDir) 97 if err != nil { 98 if os.IsNotExist(err) { 99 return result.NewResult() 100 } 101 log.ErrorfThrottled("Failed to check if integration chart exists for module %s: %v", ev.ModuleName, err) 102 return result.NewResultShortRequeueDelayWithError(err) 103 } 104 105 // Get the chart.yaml for this module 106 chartInfo, err := helm.GetChartInfo(moduleChartDir) 107 if err != nil { 108 log.ErrorfThrottled("Failed to read Chart.yaml for chart %s", moduleChartDir) 109 return result.NewResultShortRequeueDelayWithError(err) 110 } 111 112 // Perform a Helm install using the helm upgrade --install command 113 // Block until helm finishes (wait = true) 114 var opts = &helm.HelmReleaseOpts{ 115 ReleaseName: getReleaseName(ev.ResourceNSN.Name), 116 Namespace: ev.TargetNamespace, 117 ChartPath: moduleChartDir, 118 ChartVersion: chartInfo.Version, 119 Overrides: []helm.HelmOverrides{}, 120 } 121 _, err = helm.UpgradeRelease(log, opts, true, false) 122 if err != nil { 123 return result.NewResultShortRequeueDelayIfError(err) 124 } 125 return result.NewResult() 126 } 127 128 // deleteIntegrationRelease deletes the integration release 129 func (r Reconciler) deleteIntegrationRelease(log vzlog.VerrazzanoLogger, ev *event.ModuleIntegrationEvent) result.Result { 130 relName := getReleaseName(ev.ResourceNSN.Name) 131 relNamespace := ev.TargetNamespace 132 133 // Check if release is installed 134 installed, err := helm.IsReleaseInstalled(relName, relNamespace) 135 if err != nil { 136 log.ErrorfThrottled("Failed checking if integration Helm release %s is installed: %v", relName, err.Error()) 137 return result.NewResultShortRequeueDelayWithError(err) 138 } 139 if !installed { 140 return result.NewResult() 141 } 142 143 // Uninstall release 144 err = helm.Uninstall(log, relName, ev.TargetNamespace, false) 145 if err != nil { 146 log.ErrorfThrottled("Failed deleting integration Helm release %s: %v", relName, err.Error()) 147 return result.NewResultShortRequeueDelayWithError(err) 148 } 149 150 return result.NewResult() 151 } 152 153 func getReleaseName(moduleName string) string { 154 return fmt.Sprintf("%s-%s", moduleName, "integration") 155 }