func()

in operator/pkg/controller/istiocontrolplane/istiocontrolplane_controller.go [222:386]


func (r *ReconcileIstioOperator) Reconcile(_ context.Context, request reconcile.Request) (reconcile.Result, error) {
	scope.Info("Reconciling IstioOperator")

	ns, iopName := request.Namespace, request.Name
	reqNamespacedName := types.NamespacedName{
		Name:      request.Name,
		Namespace: ns,
	}
	// declare read-only iop instance to create the reconciler
	iop := &iopv1alpha1.IstioOperator{}
	if err := r.client.Get(context.TODO(), reqNamespacedName, iop); err != nil {
		if errors.IsNotFound(err) {
			// Request object not found, could have been deleted after reconcile request.
			// Owned objects are automatically garbage collected. For additional cleanup logic use finalizers.
			// Return and don't requeue
			metrics.CRDeletionTotal.Increment()
			return reconcile.Result{}, nil
		}
		// Error reading the object - requeue the request.
		scope.Warnf(errdict.OperatorFailedToGetObjectFromAPIServer, "error getting IstioOperator %s: %s", iopName, err)
		metrics.CountCRFetchFail(errors.ReasonForError(err))
		return reconcile.Result{}, err
	}
	if iop.Spec == nil {
		iop.Spec = &v1alpha1.IstioOperatorSpec{Profile: name.DefaultProfileName}
	}
	operatorRevision, _ := os.LookupEnv("REVISION")
	if operatorRevision != "" && operatorRevision != iop.Spec.Revision {
		scope.Infof("Ignoring IstioOperator CR %s with revision %s, since operator revision is %s.", iopName, iop.Spec.Revision, operatorRevision)
		return reconcile.Result{}, nil
	}
	if iop.Annotations != nil {
		if ir := iop.Annotations[IgnoreReconcileAnnotation]; ir == "true" {
			scope.Infof("Ignoring the IstioOperator CR %s because it is annotated to be ignored for reconcile ", iopName)
			return reconcile.Result{}, nil
		}
	}

	// for backward compatibility, the previous applied installed-state CR does not have the ignore reconcile annotation
	// TODO(richardwxn): remove this check and rely on annotation check only
	if strings.HasPrefix(iop.Name, name.InstalledSpecCRPrefix) {
		scope.Infof("Ignoring the installed-state IstioOperator CR %s ", iopName)
		return reconcile.Result{}, nil
	}

	var err error
	iopMerged := &iopv1alpha1.IstioOperator{}
	*iopMerged = *iop
	// get the merged values in iop on top of the defaults for the profile given by iop.profile
	iopMerged.Spec, err = mergeIOPSWithProfile(iopMerged)
	if err != nil {
		scope.Errorf(errdict.OperatorFailedToMergeUserIOP, "failed to merge base profile with user IstioOperator CR %s, %s", iopName, err)
		return reconcile.Result{}, err
	}

	deleted := iop.GetDeletionTimestamp() != nil
	finalizers := sets.NewString(iop.GetFinalizers()...)
	if deleted {
		if !finalizers.Has(finalizer) {
			scope.Infof("IstioOperator %s deleted", iopName)
			metrics.CRDeletionTotal.Increment()
			return reconcile.Result{}, nil
		}
		scope.Infof("Deleting IstioOperator %s", iopName)

		reconciler, err := helmreconciler.NewHelmReconciler(r.client, r.kubeClient, iopMerged, nil)
		if err != nil {
			return reconcile.Result{}, err
		}
		if err := reconciler.Delete(); err != nil {
			scope.Errorf("Failed to delete resources with helm reconciler: %s.", err)
			return reconcile.Result{}, err
		}

		finalizers.Delete(finalizer)
		iop.SetFinalizers(finalizers.List())
		finalizerError := r.client.Update(context.TODO(), iop)
		for retryCount := 0; errors.IsConflict(finalizerError) && retryCount < finalizerMaxRetries; retryCount++ {
			scope.Info("API server conflict during finalizer removal, retrying.")
			_ = r.client.Get(context.TODO(), request.NamespacedName, iop)
			finalizers = sets.NewString(iop.GetFinalizers()...)
			finalizers.Delete(finalizer)
			iop.SetFinalizers(finalizers.List())
			finalizerError = r.client.Update(context.TODO(), iop)
		}
		if finalizerError != nil {
			if errors.IsNotFound(finalizerError) {
				scope.Infof("Did not remove finalizer from %s: the object was previously deleted.", iopName)
				metrics.CRDeletionTotal.Increment()
				return reconcile.Result{}, nil
			} else if errors.IsConflict(finalizerError) {
				scope.Infof("Could not remove finalizer from %s due to conflict. Operation will be retried in next reconcile attempt.", iopName)
				return reconcile.Result{}, nil
			}
			scope.Errorf(errdict.OperatorFailedToRemoveFinalizer, "error removing finalizer: %s", finalizerError)
			return reconcile.Result{}, finalizerError
		}
		return reconcile.Result{}, nil
	} else if !finalizers.Has(finalizer) {
		log.Infof("Adding finalizer %v to %v", finalizer, request)
		finalizers.Insert(finalizer)
		iop.SetFinalizers(finalizers.List())
		err := r.client.Update(context.TODO(), iop)
		if err != nil {
			if errors.IsNotFound(err) {
				scope.Infof("Could not add finalizer to %s: the object was deleted.", iopName)
				metrics.CRDeletionTotal.Increment()
				return reconcile.Result{}, nil
			} else if errors.IsConflict(err) {
				scope.Infof("Could not add finalizer to %s due to conflict. Operation will be retried in next reconcile attempt.", iopName)
				return reconcile.Result{}, nil
			}
			scope.Errorf(errdict.OperatorFailedToAddFinalizer, "Failed to add finalizer to IstioOperator CR %s: %s", iopName, err)
			return reconcile.Result{}, err
		}
	}

	scope.Info("Updating IstioOperator")
	val := iopMerged.Spec.Values.AsMap()
	if _, ok := val["global"]; !ok {
		val["global"] = make(map[string]interface{})
	}
	globalValues := val["global"].(map[string]interface{})
	scope.Info("Detecting third-party JWT support")
	var jwtPolicy util.JWTPolicy
	if jwtPolicy, err = util.DetectSupportedJWTPolicy(r.kubeClient); err != nil {
		// TODO(howardjohn): add to dictionary. When resolved, replace this sentence with Done or WontFix - if WontFix, add reason.
		scope.Warnf("Failed to detect third-party JWT support: %v", err)
	} else {
		if jwtPolicy == util.FirstPartyJWT {
			scope.Info("Detected that your cluster does not support third party JWT authentication. " +
				"Falling back to less secure first party JWT. " +
				"See " + url.ConfigureSAToken + " for details.")
		}
		globalValues["jwtPolicy"] = string(jwtPolicy)
	}
	err = util.ValidateIOPCAConfig(r.kubeClient, iopMerged)
	if err != nil {
		scope.Errorf(errdict.OperatorFailedToConfigure, "failed to apply IstioOperator resources. Error %s", err)
		return reconcile.Result{}, err
	}
	helmReconcilerOptions := &helmreconciler.Options{
		Log:         clog.NewDefaultLogger(),
		ProgressLog: progress.NewLog(),
	}
	if r.options != nil {
		helmReconcilerOptions.Force = r.options.Force
	}
	reconciler, err := helmreconciler.NewHelmReconciler(r.client, r.kubeClient, iopMerged, helmReconcilerOptions)
	if err != nil {
		return reconcile.Result{}, err
	}
	if err := reconciler.SetStatusBegin(); err != nil {
		return reconcile.Result{}, err
	}
	status, err := reconciler.Reconcile()
	if err != nil {
		scope.Errorf("Error during reconcile: %s", err)
	}
	if err := reconciler.SetStatusComplete(status); err != nil {
		return reconcile.Result{}, err
	}

	return reconcile.Result{}, err
}