func (r *ServiceReconciler) Reconcile(request ctrl.Request) (ctrl.Result, error) {
	ctx := context.Background()
	logt := r.Log.WithValues("service", request.NamespacedName)
	instance := &ibmcloudv1.Service{}
	err := r.Get(ctx, request.NamespacedName, instance)
	if err != nil {
		if k8sErrors.IsNotFound(err) {
			return ctrl.Result{}, nil
		}
		return ctrl.Result{}, err
	}
	if specChanged(instance) {
		logt.Info("Spec is immutable", "Restoring", instance.ObjectMeta.Name)
		instance.Spec.Plan = instance.Status.Plan
		instance.Spec.ExternalName = instance.Status.ExternalName
		instance.Spec.ServiceClass = instance.Status.ServiceClass
		instance.Spec.ServiceClassType = instance.Status.ServiceClassType
		instance.Spec.Context = instance.Status.Context
		if err := r.Update(ctx, instance); err != nil {
			return ctrl.Result{}, err
		}
	}
	var (
		resourceContext ibmcloudv1.ResourceContext
		session         *session.Session
		resourceGroupID,
		serviceClassType,
		servicePlanID,
		spaceID,
		targetCRN string
	)
	{
		ibmCloudInfo, err := r.GetIBMCloudInfo(logt, r.Client, instance)
		if err != nil {
			if k8sErrors.IsNotFound(err) && containsServiceFinalizer(instance) &&
				!instance.ObjectMeta.DeletionTimestamp.IsZero() {
				logt.Info("Cannot get IBMCloud related secrets and configmaps, just remove finalizers", "in deletion", err.Error())
				instance.ObjectMeta.Finalizers = deleteServiceFinalizer(instance)
				if err := r.Update(ctx, instance); err != nil {
					logt.Error(err, "Error removing finalizers in deletion")
				}
				return ctrl.Result{}, nil
			}
			logt.Error(err, "Failed to get IBM Cloud info for service")
			return r.updateStatusError(instance, serviceStateFailed, err)
		}
		resourceContext = ibmCloudInfo.Context
		resourceGroupID = ibmCloudInfo.ResourceGroupID
		serviceClassType = ibmCloudInfo.ServiceClassType
		session = ibmCloudInfo.Session
		targetCRN = ibmCloudInfo.TargetCrn
		if ibmCloudInfo.Space != nil {
			spaceID = ibmCloudInfo.Space.GUID
		}
		if ibmCloudInfo.BxPlan != nil {
			servicePlanID = ibmCloudInfo.BxPlan.GUID
		} else {
			servicePlanID = ibmCloudInfo.ServicePlanID
		}
		logt = logt.WithValues("User", ibmCloudInfo.Context.User)
	}
	if reflect.DeepEqual(instance.Status, ibmcloudv1.ServiceStatus{}) {
		instance.Status.State = serviceStatePending
		instance.Status.Message = "Processing Resource"
		if err := r.Status().Update(ctx, instance); err != nil {
			logt.Info("Failed setting status for the first time", "error", err.Error())
			return ctrl.Result{}, err
		}
	}
	if instance.ObjectMeta.DeletionTimestamp.IsZero() {
		if !containsServiceFinalizer(instance) {
			instance.ObjectMeta.Finalizers = append(instance.ObjectMeta.Finalizers, serviceFinalizer)
			if err := r.Update(ctx, instance); err != nil {
				logt.Error(err, "Error adding finalizer", "service", instance.ObjectMeta.Name)
				return ctrl.Result{}, err
			}
		}
	} else {
		if containsServiceFinalizer(instance) {
			err := r.deleteService(session, logt, instance, serviceClassType)
			if err != nil {
				logt.Error(err, "Error deleting resource", "service", instance.ObjectMeta.Name)
				return ctrl.Result{Requeue: true, RequeueAfter: time.Second * 10}, nil
			}
			instance.ObjectMeta.Finalizers = deleteServiceFinalizer(instance)
			err = r.Update(ctx, instance)
			if err != nil {
				logt.Error(err, "Error removing finalizers")
			}
			return ctrl.Result{}, err
		}
	}
	externalName := getExternalName(instance)
	params, err := r.getParams(ctx, instance)
	if err != nil {
		logt.Error(err, "Instance has problems with its parameters", "service", instance.ObjectMeta.Name)
		return r.updateStatusError(instance, serviceStateFailed, err)
	}
	tags := getTags(instance)
	logt.Info("ServiceInstance ", "name", externalName, "tags", tags)
	if serviceClassType == "CF" {
		logt.Info("ServiceInstance is CF", "instance", instance.ObjectMeta.Name)
		if instance.Status.InstanceID == "" {  
			if isAlias(instance) {
				logt.Info("Using `Alias` plan, checking if instance exists")
				instanceID, _, err := r.GetCFServiceInstance(session, externalName)
				if err != nil {
					logt.Error(err, "Instance ", instance.ObjectMeta.Name, " with `Alias` plan does not exists")
					return r.updateStatusError(instance, serviceStateFailed, err)
				}
				return r.updateStatus(session, logt, instance, resourceContext, instanceID, serviceStateOnline, serviceClassType)
			}
			logt.Info("Creating", "instance", instance.ObjectMeta.Name, "service class", instance.Spec.ServiceClass)
			guid, state, err := r.CreateCFServiceInstance(session, externalName, servicePlanID, spaceID, params, tags)
			if err != nil {
				return r.updateStatusError(instance, serviceStateFailed, err)
			}
			return r.updateStatus(session, logt, instance, resourceContext, guid, state, serviceClassType)
		}
		logt.Info("CF ServiceInstance ", "should already exists, verifying", instance.ObjectMeta.Name)
		_, state, err := r.GetCFServiceInstance(session, externalName)
		if err != nil && !isAlias(instance) {
			if _, notFound := err.(cfservice.NotFoundError); notFound {
				logt.Info("Recreating ", instance.ObjectMeta.Name, instance.Spec.ServiceClass)
				guid, state, err := r.CreateCFServiceInstance(session, externalName, servicePlanID, spaceID, params, tags)
				if err != nil {
					return r.updateStatusError(instance, serviceStateFailed, err)
				}
				return r.updateStatus(session, logt, instance, resourceContext, guid, state, serviceClassType)
			}
			return r.updateStatusError(instance, serviceStateFailed, err)
		} else if err != nil && isAlias(instance) {
			instance.Status.InstanceID = ""
			return r.updateStatusError(instance, serviceStatePending, err)
		}
		logt.Info("ServiceInstance ", "exists", instance.ObjectMeta.Name)
		return r.updateStatus(session, logt, instance, resourceContext, instance.Status.InstanceID, state, serviceClassType)
	}
	createServiceInstance := func() (id, state string, err error) {
		return r.CreateResourceServiceInstance(session, externalName, servicePlanID, resourceGroupID, targetCRN, params, tags)
	}
	if instance.Status.InstanceID == "" {  
		if isAlias(instance) {
			logt := logt.WithValues("Name", instance.ObjectMeta.Name)
			logt.Info("Using `Alias` plan, checking if instance exists")
			instanceID := instance.ObjectMeta.GetAnnotations()[instanceIDKey]
			id, state, err := r.GetResourceServiceAliasInstance(session, instanceID, resourceGroupID, servicePlanID, externalName, logt)
			if _, notFound := err.(resource.NotFoundError); notFound {
				return r.updateStatusError(instance, serviceStateFailed, errors.Wrapf(err, "no service instances with name %s found for alias plan", instance.ObjectMeta.Name))
			}
			if err != nil {
				return r.updateStatusError(instance, serviceStateFailed, errors.Wrapf(err, "failed to resolve Alias plan instance %s", instance.ObjectMeta.Name))
			}
			return r.updateStatus(session, logt, instance, resourceContext, id, state, serviceClassType)
		}
		instance.Status.InstanceID = inProgress
		if err := r.Status().Update(ctx, instance); err != nil {
			logt.Info("Error updating InstanceID to be in progress", "Error", err.Error())
			return ctrl.Result{}, err
		}
		logt.Info("Creating ", instance.ObjectMeta.Name, instance.Spec.ServiceClass)
		id, state, err := createServiceInstance()
		if err != nil {
			return r.updateStatusError(instance, serviceStateFailed, err)
		}
		return r.updateStatus(session, logt, instance, resourceContext, id, state, serviceClassType)
	}
	logt.Info("ServiceInstance ", "should already exists, verifying", instance.ObjectMeta.Name)
	state, err := r.GetResourceServiceInstanceState(session, resourceGroupID, servicePlanID, externalName, instance.Status.InstanceID)
	if _, ok := err.(resource.NotFoundError); ok {  
		if !isAlias(instance) {
			logt.Info("Recreating ", instance.ObjectMeta.Name, instance.Spec.ServiceClass)
			instance.Status.InstanceID = inProgress
			if err := r.Status().Update(ctx, instance); err != nil {
				logt.Info("Error updating instanceID to be in progress", "Error", err.Error())
				return ctrl.Result{}, err
			}
			id, state, err := createServiceInstance()
			if err != nil {
				return r.updateStatusError(instance, serviceStateFailed, err)
			}
			return r.updateStatus(session, logt, instance, resourceContext, id, state, serviceClassType)
		}
		instance.Status.InstanceID = ""
		return r.updateStatusError(instance, serviceStatePending, fmt.Errorf("aliased service instance no longer exists"))
	}
	if err != nil {
		return r.updateStatusError(instance, serviceStatePending, err)
	}
	logt.Info("ServiceInstance ", "exists", instance.ObjectMeta.Name)
	if tagsOrParamsChanged(instance) {
		logt.Info("ServiceInstance ", "updating tags and/or parameters", instance.ObjectMeta.Name)
		state, err = r.UpdateResourceServiceInstance(session, instance.Status.InstanceID, externalName, servicePlanID, params, tags)
		if err != nil {
			logt.Info("Error updating tags and/or parameters", "Error", err.Error())
			return r.updateStatusError(instance, serviceStateFailed, err)
		}
	}
	return r.updateStatus(session, logt, instance, resourceContext, instance.Status.InstanceID, state, serviceClassType)
}
+kubebuilder:rbac:groups=ibmcloud.ibm.com,resources=services,verbs=get;list;watch;create;update;patch;delete
+kubebuilder:rbac:groups=ibmcloud.ibm.com,resources=services/status,verbs=get;update;patch
Reconcile reads the state of the cluster for a Service object and makes changes based on the state read
and what is in the Service.Spec.
Automatically generate RBAC rules to allow the Controller to read and write Deployments.
package controllers
import (
	"context"
	"fmt"
	"reflect"
	"strings"
	"time"
	"github.com/IBM-Cloud/bluemix-go/session"
	"github.com/go-logr/logr"
	"github.com/pkg/errors"
	k8sErrors "k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/runtime"
	ctrl "sigs.k8s.io/controller-runtime"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/controller"
	ibmcloudv1 "github.com/ibm/cloud-operators/api/v1"
	"github.com/ibm/cloud-operators/internal/config"
	"github.com/ibm/cloud-operators/internal/ibmcloud/cfservice"
	"github.com/ibm/cloud-operators/internal/ibmcloud/resource"
)
const (
	serviceFinalizer = "service.ibmcloud.ibm.com"
	instanceIDKey    = "ibmcloud.ibm.com/instanceId"
	aliasPlan        = "alias"
)
const (
	serviceStatePending string = "Pending"
	serviceStateFailed string = "Failed"
	serviceStateOnline string = "Online"
)
type ServiceReconciler struct {
	client.Client
	Log    logr.Logger
	Scheme *runtime.Scheme
	CreateCFServiceInstance         cfservice.InstanceCreator
	CreateResourceServiceInstance   resource.ServiceInstanceCreator
	DeleteCFServiceInstance         cfservice.InstanceDeleter
	DeleteResourceServiceInstance   resource.ServiceInstanceDeleter
	GetCFServiceInstance            cfservice.InstanceGetter
	GetIBMCloudInfo                 IBMCloudInfoGetter
	GetResourceServiceAliasInstance resource.ServiceAliasInstanceGetter
	GetResourceServiceInstanceState resource.ServiceInstanceStatusGetter
	UpdateResourceServiceInstance   resource.ServiceInstanceUpdater
}
func (r *ServiceReconciler) SetupWithManager(mgr ctrl.Manager, options controller.Options) error {
	return ctrl.NewControllerManagedBy(mgr).
		WithOptions(options).
		For(&ibmcloudv1.Service{}).
		Complete(r)
}