func()

in oracle/controllers/instancecontroller/instance_controller.go [509:664]


func (r *InstanceReconciler) reconcileDatabaseInstance(ctx context.Context, inst *v1alpha1.Instance, log logr.Logger, images map[string]string) (ctrl.Result, error) {
	instanceReadyCond := k8s.FindCondition(inst.Status.Conditions, k8s.Ready)
	dbInstanceCond := k8s.FindCondition(inst.Status.Conditions, k8s.DatabaseInstanceReady)
	log.Info("reconciling database instance: ", "instanceReadyCond", instanceReadyCond, "dbInstanceCond", dbInstanceCond)

	// reconcile database only when instance is ready, but requeue.
	if !k8s.ConditionStatusEquals(instanceReadyCond, v1.ConditionTrue) {
		return ctrl.Result{Requeue: true}, nil
	}

	isImageSeeded, err := r.isImageSeeded(ctx, inst, log)
	if err != nil {
		log.Error(err, "unable to determine image type")
		return ctrl.Result{}, err
	}

	if dbInstanceCond == nil {
		if inst.Spec.Restore != nil {
			log.Info("Skip bootstrap CDB database, waiting to be restored")
			k8s.InstanceUpsertCondition(&inst.Status, k8s.DatabaseInstanceReady, v1.ConditionFalse, k8s.RestorePending, "Awaiting restore CDB")
		} else if !isImageSeeded {
			log.Info("Unseeded image used, waiting to be created")
			k8s.InstanceUpsertCondition(&inst.Status, k8s.DatabaseInstanceReady, v1.ConditionFalse, k8s.CreatePending, "Awaiting create CDB")
		} else {
			log.Info("Seeded image used, waiting to be bootstrapped")
			k8s.InstanceUpsertCondition(&inst.Status, k8s.DatabaseInstanceReady, v1.ConditionFalse, k8s.BootstrapPending, "Awaiting bootstrap CDB")
		}
		return ctrl.Result{Requeue: true}, r.Status().Update(ctx, inst)
	}

	// Check for timeout
	if !k8s.ConditionStatusEquals(dbInstanceCond, v1.ConditionTrue) {
		elapsed := k8s.ElapsedTimeFromLastTransitionTime(dbInstanceCond, time.Second)
		createDatabaseInstanceTimeout := DatabaseInstanceReadyTimeoutSeeded
		if !isImageSeeded {
			createDatabaseInstanceTimeout = DatabaseInstanceReadyTimeoutUnseeded
		}
		if elapsed < createDatabaseInstanceTimeout {
			log.Info(fmt.Sprintf("database instance creation in progress for %v", elapsed))
		} else {
			log.Info(fmt.Sprintf("database instance creation timed out. Elapsed Time: %v", elapsed))
			if !strings.Contains(dbInstanceCond.Message, "Warning") { // so that we would create only one database instance timeout event
				r.Recorder.Eventf(inst, corev1.EventTypeWarning, k8s.DatabaseInstanceTimeout, "DatabaseInstance has been in progress for over %v, please try delete and recreate.", createDatabaseInstanceTimeout)
			}
			k8s.InstanceUpsertCondition(&inst.Status, k8s.DatabaseInstanceReady, v1.ConditionFalse, dbInstanceCond.Reason, "Warning: db instance is taking too long to start up - please try delete and recreate")
			return ctrl.Result{}, nil
		}
	}

	switch dbInstanceCond.Reason {
	case k8s.CreatePending:
		// Launch the CreateCDB LRO
		req := &controllers.CreateCDBRequest{
			Sid:           inst.Spec.CDBName,
			DbUniqueName:  inst.Spec.DBUniqueName,
			DbDomain:      controllers.GetDBDomain(inst),
			CharacterSet:  inst.Spec.CharacterSet,
			MemoryPercent: int32(inst.Spec.MemoryPercent),
			//DBCA expects the parameters in the following string array format
			// ["key1=val1", "key2=val2","key3=val3"]
			AdditionalParams: mapsToStringArray(inst.Spec.Parameters),
			LroInput:         &controllers.LROInput{OperationId: lroCreateCDBOperationID(*inst)},
		}
		_, err = controllers.CreateCDB(ctx, r, r.DatabaseClientFactory, inst.GetNamespace(), inst.GetName(), *req)
		if err != nil {
			if !controllers.IsAlreadyExistsError(err) {
				log.Error(err, "CreateCDB failed")
				return ctrl.Result{}, err
			}
		} else {
			log.Info("CreateCDB started")
		}
		k8s.InstanceUpsertCondition(&inst.Status, k8s.DatabaseInstanceReady, v1.ConditionFalse, k8s.CreateInProgress, "Database creation in progress")
		return ctrl.Result{Requeue: true}, r.Status().Update(ctx, inst)
	case k8s.CreateInProgress:
		id := lroCreateCDBOperationID(*inst)
		done, err := controllers.IsLROOperationDone(ctx, r.DatabaseClientFactory, r.Client, id, inst.GetNamespace(), inst.GetName())
		if !done {
			log.Info("CreateCDB still in progress, waiting")
			return ctrl.Result{RequeueAfter: 15 * time.Second}, nil
		}
		controllers.DeleteLROOperation(ctx, r.DatabaseClientFactory, r.Client, id, inst.Namespace, inst.Name)
		if err != nil {
			k8s.InstanceUpsertCondition(&inst.Status, k8s.DatabaseInstanceReady, v1.ConditionFalse, k8s.CreateFailed, "CreateCDB LRO returned error")
			log.Error(err, "CreateCDB LRO returned error")
			return ctrl.Result{Requeue: true}, r.Status().Update(ctx, inst)
		}
		log.Info("CreateCDB done successfully")
		k8s.InstanceUpsertCondition(&inst.Status, k8s.DatabaseInstanceReady, v1.ConditionFalse, k8s.BootstrapPending, "")
		// Reconcile again
		return ctrl.Result{Requeue: true}, r.Status().Update(ctx, inst)
	case k8s.BootstrapPending:
		// Launch the BootstrapDatabase LRO
		bootstrapMode := controllers.BootstrapDatabaseRequest_ProvisionUnseeded
		if isImageSeeded {
			bootstrapMode = controllers.BootstrapDatabaseRequest_ProvisionSeeded
		}
		req := &controllers.BootstrapDatabaseRequest{
			CdbName:      inst.Spec.CDBName,
			DbUniqueName: inst.Spec.DBUniqueName,
			Dbdomain:     controllers.GetDBDomain(inst),
			Mode:         bootstrapMode,
			LroInput:     &controllers.LROInput{OperationId: lroBootstrapCDBOperationID(*inst)},
		}
		lro, err := controllers.BootstrapDatabase(ctx, r, r.DatabaseClientFactory, inst.Namespace, inst.Name, *req)

		if err != nil {
			if !controllers.IsAlreadyExistsError(err) {
				log.Error(err, "BootstrapDatabase failed")
				return ctrl.Result{}, err
			}
		} else if lro.GetDone() {
			// handle synchronous version of BootstrapDatabase
			r.Log.Info("encountered synchronous version of BootstrapDatabase")
			r.Log.Info("BootstrapDatabase DONE")
			k8s.InstanceUpsertCondition(&inst.Status, k8s.DatabaseInstanceReady, v1.ConditionFalse, k8s.ReconcileServices, "Services starting")
			return ctrl.Result{Requeue: true}, r.Status().Update(ctx, inst)
		}
		log.Info("BootstrapDatabase started")
		k8s.InstanceUpsertCondition(&inst.Status, k8s.DatabaseInstanceReady, v1.ConditionFalse, k8s.BootstrapInProgress, "Database bootstrap in progress")
		return ctrl.Result{Requeue: true}, r.Status().Update(ctx, inst)
	case k8s.BootstrapInProgress:
		id := lroBootstrapCDBOperationID(*inst)
		done, err := controllers.IsLROOperationDone(ctx, r.DatabaseClientFactory, r.Client, id, inst.GetNamespace(), inst.GetName())
		if !done {
			log.Info("BootstrapDatabase still in progress, waiting")
			return ctrl.Result{RequeueAfter: 15 * time.Second}, nil
		}
		controllers.DeleteLROOperation(ctx, r.DatabaseClientFactory, r.Client, id, inst.Namespace, inst.Name)
		if err != nil {
			k8s.InstanceUpsertCondition(&inst.Status, k8s.DatabaseInstanceReady, v1.ConditionFalse, k8s.CreateFailed, "BootstrapDatabase LRO returned error")
			log.Error(err, "BootstrapDatabase LRO returned error")
			return ctrl.Result{Requeue: true}, r.Status().Update(ctx, inst)
		}
		log.Info("BootstrapDatabase done successfully")
		k8s.InstanceUpsertCondition(&inst.Status, k8s.DatabaseInstanceReady, v1.ConditionFalse, k8s.ReconcileServices, "Services starting")
		// Reconcile again
		return ctrl.Result{Requeue: true}, r.Status().Update(ctx, inst)
	case k8s.ReconcileServices:
		res, err := r.reconcileMonitoring(ctx, inst, log, images)
		if err == nil && res.RequeueAfter == 0 {
			k8s.InstanceUpsertCondition(&inst.Status, k8s.DatabaseInstanceReady, v1.ConditionTrue, k8s.CreateComplete, "")
			return ctrl.Result{Requeue: true}, r.Status().Update(ctx, inst)
		}
		return res, err
	case k8s.RestorePending:
		if k8s.ConditionReasonEquals(instanceReadyCond, k8s.RestoreComplete) {
			k8s.InstanceUpsertCondition(&inst.Status, k8s.DatabaseInstanceReady, v1.ConditionTrue, k8s.CreateComplete, "")
			return ctrl.Result{Requeue: true}, r.Status().Update(ctx, inst)
		}
	default:
		r.Log.Info("reconcileDatabaseInstance: no action needed, proceed with main reconciliation")
	}

	return ctrl.Result{}, nil
}