github.com/verrazzano/verrazzano@v1.7.0/platform-operator/controllers/configmaps/components/controller.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 components 5 6 import ( 7 "context" 8 "fmt" 9 vzctrl "github.com/verrazzano/verrazzano/pkg/controller" 10 "github.com/verrazzano/verrazzano/pkg/log/vzlog" 11 vzapi "github.com/verrazzano/verrazzano/platform-operator/apis/verrazzano/v1alpha1" 12 "github.com/verrazzano/verrazzano/platform-operator/constants" 13 "github.com/verrazzano/verrazzano/platform-operator/controllers/verrazzano/component/spi" 14 "go.uber.org/zap" 15 v1 "k8s.io/api/core/v1" 16 k8serrors "k8s.io/apimachinery/pkg/api/errors" 17 "k8s.io/apimachinery/pkg/runtime" 18 ctrl "sigs.k8s.io/controller-runtime" 19 "sigs.k8s.io/controller-runtime/pkg/client" 20 "sigs.k8s.io/controller-runtime/pkg/controller" 21 "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" 22 "sigs.k8s.io/controller-runtime/pkg/event" 23 "sigs.k8s.io/controller-runtime/pkg/predicate" 24 "sigs.k8s.io/controller-runtime/pkg/reconcile" 25 "time" 26 ) 27 28 var shimComponents = map[string]spi.Component{} 29 30 type ComponentConfigMapReconciler struct { 31 client.Client 32 Scheme *runtime.Scheme 33 DryRun bool 34 } 35 36 func initShimComponentList() { 37 // Add any shim components here that you want to test. 38 // For example, 39 // shimComponents[clusterapi.ComponentName] = clusterapi.NewComponent() 40 } 41 42 func (r *ComponentConfigMapReconciler) SetupWithManager(mgr ctrl.Manager) error { 43 initShimComponentList() 44 return ctrl.NewControllerManagedBy(mgr). 45 For(&v1.ConfigMap{}). 46 WithEventFilter(r.createComponentConfigMapPredicate()). 47 WithOptions(controller.Options{ 48 MaxConcurrentReconciles: 10, 49 }). 50 Complete(r) 51 } 52 53 func (r *ComponentConfigMapReconciler) createComponentConfigMapPredicate() predicate.Predicate { 54 return predicate.Funcs{ 55 CreateFunc: func(e event.CreateEvent) bool { 56 return r.isComponentConfigMap(e.Object) 57 }, 58 DeleteFunc: func(e event.DeleteEvent) bool { 59 return r.isComponentConfigMap(e.Object) 60 }, 61 UpdateFunc: func(e event.UpdateEvent) bool { 62 return r.isComponentConfigMap(e.ObjectNew) 63 }, 64 } 65 } 66 67 func (r *ComponentConfigMapReconciler) isComponentConfigMap(o client.Object) bool { 68 configMap := o.(*v1.ConfigMap) 69 return configMap.Labels[devComponentConfigMapAPIVersionLabel] == devComponentConfigMapAPIVersionv1beta2 && 70 configMap.Labels[devComponentConfigMapKindLabel] != "" 71 } 72 73 // Reconcile function for the ComponentConfigMapReconciler 74 func (r *ComponentConfigMapReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { 75 if ctx == nil { 76 ctx = context.Background() 77 } 78 79 verrazzanos := &vzapi.VerrazzanoList{} 80 err := r.List(ctx, verrazzanos) 81 if err != nil && !k8serrors.IsNotFound(err) { 82 zap.S().Errorf("Failed to get Verrazzanos %s/%s", req.Namespace, req.Name) 83 return newRequeueWithDelay(), err 84 } 85 if err != nil || len(verrazzanos.Items) == 0 { 86 zap.S().Debug("No Verrazzanos found in the cluster") 87 return newRequeueWithDelay(), err 88 } 89 vz := verrazzanos.Items[0] 90 91 zap.S().Infof("Reconciling component configmap %s/%s", req.Namespace, req.Name) 92 // Get the configmap for the request 93 configMap := &v1.ConfigMap{} 94 if err = r.Get(ctx, req.NamespacedName, configMap); err != nil { 95 if k8serrors.IsNotFound(err) { 96 return ctrl.Result{}, nil 97 } 98 zap.S().Errorf("Failed to get configmap %s/%s", req.Namespace, req.Name) 99 return newRequeueWithDelay(), err 100 } 101 102 if configMap.Namespace != vz.Namespace { 103 err = fmt.Errorf("Component ConfigMap must be in the same namespace as the Verrazzano resource, ConfigMap namespace: %s, Verrazzano namespace: %s", configMap.Namespace, vz.Namespace) 104 zap.S().Error(err) 105 return ctrl.Result{}, err 106 } 107 108 // Get the resource logger needed to log message using 'progress' and 'once' methods 109 log, err := vzlog.EnsureResourceLogger(&vzlog.ResourceConfig{ 110 Name: configMap.Name, 111 Namespace: configMap.Namespace, 112 ID: string(configMap.UID), 113 Generation: configMap.Generation, 114 ControllerName: "verrazzanodevcomponent", 115 }) 116 if err != nil { 117 zap.S().Errorf("Failed to create controller logger for component configmap %s/%s: %v", configMap.GetName(), configMap.GetNamespace(), err) 118 return newRequeueWithDelay(), err 119 } 120 121 var comp spi.Component 122 if configMap.Labels[devComponentConfigMapKindLabel] == devComponentConfigMapKindHelmComponent { 123 comp, err = newDevHelmComponent(configMap) 124 } else if configMap.Labels[devComponentConfigMapKindLabel] == devComponentConfigMapKindShimComponent { 125 comp, err = newDevShimComponent(configMap) 126 } else { 127 err = fmt.Errorf("%s is not a support configmap-kind, %s and %s are the only configmap-kind supported", 128 configMap.Labels[devComponentConfigMapKindLabel], devComponentConfigMapKindHelmComponent, devComponentConfigMapKindShimComponent) 129 } 130 131 if err != nil { 132 log.Errorf("Invalid configmap %s/%s: %v", configMap.GetNamespace(), configMap.GetName(), err) 133 return ctrl.Result{}, err 134 } 135 136 compCtx, err := spi.NewContext(log, r.Client, &vz, nil, false) 137 if err != nil { 138 log.Errorf("Failed to create context: %v", err) 139 return newRequeueWithDelay(), err 140 } 141 142 return r.processComponent(compCtx, comp, configMap) 143 } 144 145 func (r *ComponentConfigMapReconciler) processComponent(ctx spi.ComponentContext, comp spi.Component, configMap *v1.ConfigMap) (ctrl.Result, error) { 146 // check if component is being deleted 147 if !configMap.DeletionTimestamp.IsZero() { 148 // uninstall component 149 if err := doUninstall(ctx, comp); err != nil { 150 ctx.Log().Errorf("Error uninstalling dev component %s: %v", comp.Name(), err) 151 return newRequeueWithDelay(), err 152 } 153 ctx.Log().Infof("Successfully uninstalled dev component %s", comp.Name()) 154 155 // remove finalizer to delete the component 156 controllerutil.RemoveFinalizer(configMap, constants.DevComponentFinalizer) 157 err := r.Update(context.TODO(), configMap) 158 if err != nil { 159 ctx.Log().Errorf("Error removing finalizer %s for dev component %s: %v", constants.DevComponentFinalizer, comp.Name(), err) 160 return newRequeueWithDelay(), err 161 } 162 ctx.Log().Infof("dev component %s has been successfully uninstalled", comp.Name()) 163 return reconcile.Result{Requeue: true}, nil 164 } 165 166 // Check if our finalizer is already present and add it if not 167 if !controllerutil.ContainsFinalizer(configMap, constants.DevComponentFinalizer) { 168 configMap.Finalizers = append(configMap.Finalizers, constants.DevComponentFinalizer) 169 err := r.Update(context.TODO(), configMap) 170 if err != nil { 171 ctx.Log().Errorf("Error adding finalizer %s for dev component %s: %v", constants.DevComponentFinalizer, comp.Name(), err) 172 return newRequeueWithDelay(), err 173 } 174 ctx.Log().Infof("Successfully added finalizer %s to configmap %s for dev component %s", constants.DevComponentFinalizer, configMap.Name, comp.Name()) 175 // adding finalizer to ConfigMap will trigger a requeue so no need to requeue here 176 return reconcile.Result{}, nil 177 } 178 179 // if the release has not been installed, install it 180 installed, err := comp.IsInstalled(ctx) 181 if err != nil { 182 ctx.Log().Errorf("Error checking release status for release %s: %v", comp.Name(), err) 183 return newRequeueWithDelay(), err 184 } 185 if !installed { 186 if err = doInstall(ctx, comp); err != nil { 187 ctx.Log().Errorf("Error installing dev component %s: %v", comp.Name(), err) 188 return newRequeueWithDelay(), err 189 } 190 ctx.Log().Infof("dev component %s has been successfully installed", comp.Name()) 191 return reconcile.Result{}, nil 192 } 193 194 // if the release has already been installed, upgrade it 195 if err = doUpgrade(ctx, comp); err != nil { 196 ctx.Log().Errorf("Error upgrading dev component %s: %v", comp.Name(), err) 197 return newRequeueWithDelay(), err 198 } 199 ctx.Log().Infof("dev component %s has been successfully upgraded", comp.Name()) 200 return reconcile.Result{}, nil 201 202 } 203 204 // Create a new Result that will cause reconcile to requeue after a short delay 205 func newRequeueWithDelay() ctrl.Result { 206 return vzctrl.NewRequeueWithDelay(3, 5, time.Second) 207 } 208 209 func doInstall(ctx spi.ComponentContext, comp spi.Component) error { 210 if err := comp.PreInstall(ctx); err != nil { 211 return err 212 } 213 if err := comp.Install(ctx); err != nil { 214 return err 215 } 216 for { 217 if !comp.IsReady(ctx) { 218 ctx.Log().Progressf("Component %s has been installed. Waiting for the component to be ready", comp.Name()) 219 time.Sleep(time.Second * 5) 220 } else { 221 break 222 } 223 } 224 return comp.PostInstall(ctx) 225 } 226 227 func doUpgrade(ctx spi.ComponentContext, comp spi.Component) error { 228 if err := comp.PreUpgrade(ctx); err != nil { 229 return err 230 } 231 if err := comp.Upgrade(ctx); err != nil { 232 return err 233 } 234 return comp.PostUpgrade(ctx) 235 } 236 237 func doUninstall(ctx spi.ComponentContext, comp spi.Component) error { 238 if err := comp.PreUninstall(ctx); err != nil { 239 return err 240 } 241 if err := comp.Uninstall(ctx); err != nil { 242 return err 243 } 244 return comp.PostUninstall(ctx) 245 }