github.com/verrazzano/verrazzano@v1.7.1/platform-operator/controllers/module/component-handler/common/handler_utils.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 common 5 6 import ( 7 "context" 8 "fmt" 9 moduleapi "github.com/verrazzano/verrazzano-modules/module-operator/apis/platform/v1alpha1" 10 modulestatus "github.com/verrazzano/verrazzano-modules/module-operator/controllers/module/status" 11 "github.com/verrazzano/verrazzano-modules/pkg/controller/result" 12 "github.com/verrazzano/verrazzano-modules/pkg/controller/spi/handlerspi" 13 vzconst "github.com/verrazzano/verrazzano/pkg/constants" 14 vzapi "github.com/verrazzano/verrazzano/platform-operator/apis/verrazzano/v1alpha1" 15 modulecatalog "github.com/verrazzano/verrazzano/platform-operator/controllers/verrazzano/catalog" 16 "github.com/verrazzano/verrazzano/platform-operator/controllers/verrazzano/component/registry" 17 "github.com/verrazzano/verrazzano/platform-operator/controllers/verrazzano/component/spi" 18 "github.com/verrazzano/verrazzano/platform-operator/internal/config" 19 corev1 "k8s.io/api/core/v1" 20 "k8s.io/apimachinery/pkg/api/errors" 21 "k8s.io/apimachinery/pkg/types" 22 "sigs.k8s.io/controller-runtime/pkg/client" 23 ) 24 25 func GetComponentAndContext(ctx handlerspi.HandlerContext, operation string) (spi.ComponentContext, spi.Component, error) { 26 module := ctx.CR.(*moduleapi.Module) 27 vz, err := GetVerrazzanoCR(ctx) 28 if err != nil { 29 return nil, nil, err 30 } 31 32 return getComponentByNameAndContext(ctx, vz, module.Spec.ModuleName, operation) 33 } 34 35 func GetVerrazzanoCR(ctx handlerspi.HandlerContext) (*vzapi.Verrazzano, error) { 36 nsn, err := GetVerrazzanoNSN(ctx) 37 if err != nil { 38 return nil, err 39 } 40 41 vz := &vzapi.Verrazzano{} 42 if err := ctx.Client.Get(context.TODO(), *nsn, vz); err != nil { 43 return nil, err 44 } 45 return vz, nil 46 } 47 48 func GetVerrazzanoNSN(ctx handlerspi.HandlerContext) (*types.NamespacedName, error) { 49 vzlist := &vzapi.VerrazzanoList{} 50 if err := ctx.Client.List(context.TODO(), vzlist); err != nil { 51 return nil, err 52 } 53 if len(vzlist.Items) != 1 { 54 return nil, fmt.Errorf("Failed, found %d Verrazzano CRs in the cluster. There must be exactly 1 Verrazzano CR", len(vzlist.Items)) 55 } 56 vz := vzlist.Items[0] 57 return &types.NamespacedName{Namespace: vz.Namespace, Name: vz.Name}, nil 58 } 59 60 // AreDependenciesReady check if dependencies are ready using the Module condition 61 func AreDependenciesReady(ctx handlerspi.HandlerContext, depModulesNames []string) (res result.Result, deps []string) { 62 var remainingDeps []string 63 64 vz, err := GetVerrazzanoCR(ctx) 65 if err != nil { 66 return result.NewResultShortRequeueDelayWithError(err), nil 67 } 68 // Check if every dependency is ready, skip ones that are not enabled 69 for _, moduleName := range depModulesNames { 70 res := isDependencyReady(ctx, vz, moduleName) 71 if res.ShouldRequeue() { 72 remainingDeps = append(remainingDeps, moduleName) 73 } 74 } 75 76 if len(remainingDeps) > 0 { 77 return result.NewResultShortRequeueDelay(), remainingDeps 78 } 79 return result.NewResult(), nil 80 } 81 82 // isDependencyReady checks if a single dependency is ready. Return requeue if not ready. 83 func isDependencyReady(ctx handlerspi.HandlerContext, vz *vzapi.Verrazzano, moduleName string) result.Result { 84 compCtx, comp, err := getComponentByNameAndContext(ctx, vz, moduleName, "") 85 if err != nil { 86 return result.NewResultShortRequeueDelayWithError(err) 87 } 88 if !comp.IsEnabled(compCtx.EffectiveCR()) { 89 return result.NewResult() 90 } 91 module := moduleapi.Module{} 92 nsn := types.NamespacedName{Namespace: vzconst.VerrazzanoInstallNamespace, Name: moduleName} 93 if err := ctx.Client.Get(context.TODO(), nsn, &module, &client.GetOptions{}); err != nil { 94 if !errors.IsNotFound(err) { 95 ctx.Log.ErrorfThrottled("Failed to get Module %s, retrying: %v", moduleName, err) 96 return result.NewResultShortRequeueDelayWithError(err) 97 } 98 return result.NewResultShortRequeueDelay() 99 } 100 101 cond := modulestatus.GetReadyCondition(&module) 102 if cond == nil || cond.Status != corev1.ConditionTrue { 103 return result.NewResultShortRequeueDelay() 104 } 105 if module.Status.LastSuccessfulGeneration != module.Generation { 106 return result.NewResultShortRequeueDelay() 107 } 108 if module.Status.LastSuccessfulVersion != module.Spec.Version { 109 return result.NewResultShortRequeueDelay() 110 } 111 112 // Make sure the module version matches the catalog version. This ensures that 113 // dependent modules finish upgrade before module that depend on them start upgrade. 114 catalog, err := modulecatalog.NewCatalog(config.GetCatalogPath()) 115 if err != nil { 116 ctx.Log.ErrorfThrottled("Error loading module catalog: %v", err) 117 return result.NewResultShortRequeueDelayWithError(err) 118 } 119 version := catalog.GetVersion(comp.Name()) 120 if version == "" { 121 err = ctx.Log.ErrorfThrottledNewErr("Failed to find version for module %s in the module catalog", comp.Name()) 122 return result.NewResultShortRequeueDelayWithError(err) 123 } 124 if version != module.Status.LastSuccessfulVersion { 125 return result.NewResultShortRequeueDelay() 126 } 127 128 return result.NewResult() 129 } 130 131 func getComponentByNameAndContext(ctx handlerspi.HandlerContext, vz *vzapi.Verrazzano, compName string, operation string) (spi.ComponentContext, spi.Component, error) { 132 compCtx, err := spi.NewContext(ctx.Log, ctx.Client, vz, nil, false) 133 if err != nil { 134 compCtx.Log().Errorf("Failed to create component context: %v", err) 135 return nil, nil, err 136 } 137 138 found, comp := registry.FindComponent(compName) 139 if !found { 140 compCtx.Log().Errorf("Failed to find component %s in registry: %s", compName) 141 return nil, nil, err 142 } 143 144 return compCtx.Init(compName).Operation(operation), comp, nil 145 } 146 147 // CheckDependencies checks if the dependencies are ready 148 func CheckDependencies(ctx handlerspi.HandlerContext, action string, reason moduleapi.ModuleConditionReason) result.Result { 149 module := ctx.CR.(*moduleapi.Module) 150 151 _, comp, err := GetComponentAndContext(ctx, string(action)) 152 if err != nil { 153 return result.NewResultShortRequeueDelayWithError(err) 154 } 155 156 // Check if dependencies are ready 157 if res, deps := AreDependenciesReady(ctx, comp.GetDependencies()); res.ShouldRequeue() { 158 ctx.Log.Oncef("Component %s is waiting for dependent components to be installed", comp.Name()) 159 msg := fmt.Sprintf("Waiting for dependencies %v", deps) 160 modulestatus.UpdateReadyConditionFailed(ctx, module, reason, msg) 161 return res 162 } 163 return result.NewResult() 164 }