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  }