func (r *ServiceReconciler) paramToJSON(ctx context.Context, p ibmcloudv1.Param, namespace string) (interface{}, error) {
	if p.Value != nil && p.ValueFrom != nil {
		return nil, fmt.Errorf("Value and ValueFrom properties are mutually exclusive (for %s variable)", p.Name)
	}
	valueFrom := p.ValueFrom
	if valueFrom != nil {
		return r.paramValueToJSON(ctx, *valueFrom, namespace)
	}
	if p.Value == nil {
		return nil, nil
	}
	return paramToJSONFromRaw(p.Value)
}
paramToJSON converts variable value to JSON value
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)
}
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)
}
func specChanged(instance *ibmcloudv1.Service) bool {
	if reflect.DeepEqual(instance.Status, ibmcloudv1.ServiceStatus{}) {  
		return false
	}
	if instance.Status.Plan == "" {
		return false
	}
	if instance.Spec.ExternalName != instance.Status.ExternalName {
		return true
	}
	if instance.Spec.Plan != instance.Status.Plan {
		return true
	}
	if instance.Spec.ServiceClass != instance.Status.ServiceClass {
		return true
	}
	if instance.Spec.ServiceClassType != instance.Status.ServiceClassType {
		return true
	}
	if !reflect.DeepEqual(instance.Spec.Context, instance.Status.Context) {
		return true
	}
	return false
}
func containsServiceFinalizer(instance *ibmcloudv1.Service) bool {
	for _, finalizer := range instance.ObjectMeta.Finalizers {
		if strings.Contains(finalizer, serviceFinalizer) {
			return true
		}
	}
	return false
}
func deleteServiceFinalizer(instance *ibmcloudv1.Service) []string {
	var result []string
	for _, finalizer := range instance.ObjectMeta.Finalizers {
		if finalizer == serviceFinalizer {
			continue
		}
		result = append(result, finalizer)
	}
	return result
}
func (r *ServiceReconciler) updateStatusError(instance *ibmcloudv1.Service, state string, err error) (ctrl.Result, error) {
	logt := r.Log.WithValues("namespacedname", instance.Namespace+"/"+instance.Name)
	message := err.Error()
	logt.Error(err, "Updating status with error")
	if strings.Contains(message, "no such host") {
		return ctrl.Result{Requeue: true, RequeueAfter: time.Minute * 5}, nil
	}
	if instance.Status.State != state {
		instance.Status.State = state
		instance.Status.Message = message
		if err := r.Status().Update(context.Background(), instance); err != nil {
			logt.Info("Error updating status", state, err.Error())
			return ctrl.Result{}, err
		}
	}
	return ctrl.Result{Requeue: true, RequeueAfter: config.Get().SyncPeriod}, nil
}
func (r *ServiceReconciler) deleteService(session *session.Session, logt logr.Logger, instance *ibmcloudv1.Service, serviceClassType string) error {
	if isAlias(instance) {
		logt.Info("Aliased service will not be deleted", "Name", instance.Name)
		return nil
	}
	if instance.Status.InstanceID == "" {
		return nil  
	}
	if serviceClassType == "CF" {
		logt.Info("Deleting ", instance.ObjectMeta.Name, instance.Spec.ServiceClass)
		return r.DeleteCFServiceInstance(session, instance.Status.InstanceID, logt)
	}
	logt.Info("Deleting ", instance.ObjectMeta.Name, instance.Spec.ServiceClass)
	return r.DeleteResourceServiceInstance(session, instance.Status.InstanceID, logt)
}
func getExternalName(instance *ibmcloudv1.Service) string {
	if instance.Spec.ExternalName != "" {
		return instance.Spec.ExternalName
	}
	return instance.Name
}
func (r *ServiceReconciler) getParams(ctx context.Context, instance *ibmcloudv1.Service) (map[string]interface{}, error) {
	params := make(map[string]interface{})
	for _, p := range instance.Spec.Parameters {
		val, err := r.paramToJSON(ctx, p, instance.Namespace)
		if err != nil {
			return params, err
		}
		params[p.Name] = val
	}
	return params, nil
}