github.com/interconnectedcloud/qdr-operator@v0.0.0-20210826174505-576d2b33dac7/pkg/controller/interconnect/interconnect_controller.go (about)

     1  package interconnect
     2  
     3  import (
     4  	"context"
     5  	"reflect"
     6  	"strconv"
     7  	"strings"
     8  
     9  	rhseutils "github.com/RHsyseng/operator-utils/pkg/utils/openshift"
    10  	"github.com/interconnectedcloud/qdr-operator/pkg/apis/interconnectedcloud/v1alpha1"
    11  	"github.com/interconnectedcloud/qdr-operator/pkg/resources/certificates"
    12  	"github.com/interconnectedcloud/qdr-operator/pkg/resources/configmaps"
    13  	"github.com/interconnectedcloud/qdr-operator/pkg/resources/deployments"
    14  	"github.com/interconnectedcloud/qdr-operator/pkg/resources/ingresses"
    15  	"github.com/interconnectedcloud/qdr-operator/pkg/resources/rolebindings"
    16  	"github.com/interconnectedcloud/qdr-operator/pkg/resources/roles"
    17  	"github.com/interconnectedcloud/qdr-operator/pkg/resources/routes"
    18  	"github.com/interconnectedcloud/qdr-operator/pkg/resources/serviceaccounts"
    19  	"github.com/interconnectedcloud/qdr-operator/pkg/resources/services"
    20  	"github.com/interconnectedcloud/qdr-operator/pkg/utils/configs"
    21  	"github.com/interconnectedcloud/qdr-operator/pkg/utils/random"
    22  	"github.com/interconnectedcloud/qdr-operator/pkg/utils/selectors"
    23  	cmv1alpha1 "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha1"
    24  	routev1 "github.com/openshift/api/route/v1"
    25  	appsv1 "k8s.io/api/apps/v1"
    26  	corev1 "k8s.io/api/core/v1"
    27  	extv1b1 "k8s.io/api/extensions/v1beta1"
    28  	rbacv1 "k8s.io/api/rbac/v1"
    29  	"k8s.io/apimachinery/pkg/api/errors"
    30  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    31  	"k8s.io/apimachinery/pkg/runtime"
    32  	"k8s.io/apimachinery/pkg/types"
    33  	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
    34  	"sigs.k8s.io/controller-runtime/pkg/client"
    35  	"sigs.k8s.io/controller-runtime/pkg/controller"
    36  	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
    37  	"sigs.k8s.io/controller-runtime/pkg/handler"
    38  	"sigs.k8s.io/controller-runtime/pkg/manager"
    39  	"sigs.k8s.io/controller-runtime/pkg/reconcile"
    40  	logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
    41  	"sigs.k8s.io/controller-runtime/pkg/source"
    42  )
    43  
    44  var (
    45  	log                = logf.Log.WithName("controller_interconnect")
    46  	openshift_detected *bool
    47  )
    48  
    49  const maxConditions = 6
    50  
    51  func isOpenShift() bool {
    52  	if openshift_detected == nil {
    53  		isos, err := rhseutils.IsOpenShift(nil)
    54  		if err != nil {
    55  			log.Error(err, "Failed to detect cluster type")
    56  		}
    57  		openshift_detected = &isos
    58  	}
    59  	return *openshift_detected
    60  }
    61  
    62  // Add creates a new Interconnect Controller and adds it to the Manager. The Manager will set fields on the Controller
    63  // and Start it when the Manager is Started.
    64  func Add(mgr manager.Manager) error {
    65  	return add(mgr, newReconciler(mgr))
    66  }
    67  
    68  // newReconciler returns a new reconcile.Reconciler
    69  func newReconciler(mgr manager.Manager) reconcile.Reconciler {
    70  	// TODO(ansmith): verify this is still needed if cert-manager is fully installed
    71  	scheme := mgr.GetScheme()
    72  	if certificates.DetectCertmgrIssuer() {
    73  		utilruntime.Must(cmv1alpha1.AddToScheme(scheme))
    74  		utilruntime.Must(scheme.SetVersionPriority(cmv1alpha1.SchemeGroupVersion))
    75  	}
    76  
    77  	if isOpenShift() {
    78  		utilruntime.Must(routev1.AddToScheme(scheme))
    79  		utilruntime.Must(scheme.SetVersionPriority(routev1.SchemeGroupVersion))
    80  	}
    81  	return &ReconcileInterconnect{client: mgr.GetClient(), scheme: mgr.GetScheme()}
    82  }
    83  
    84  // add adds a new Controller to mgr with r as the reconcile.Reconciler
    85  func add(mgr manager.Manager, r reconcile.Reconciler) error {
    86  	// Create a new controller
    87  	c, err := controller.New("interconnect-controller", mgr, controller.Options{Reconciler: r})
    88  	if err != nil {
    89  		return err
    90  	}
    91  
    92  	// Watch for changes to primary resource Interconnect
    93  	err = c.Watch(&source.Kind{Type: &v1alpha1.Interconnect{}}, &handler.EnqueueRequestForObject{})
    94  	if err != nil {
    95  		return err
    96  	}
    97  
    98  	// Watch for changes to secondary resource Deployment and requeue the owner Interconnect
    99  	err = c.Watch(&source.Kind{Type: &appsv1.Deployment{}}, &handler.EnqueueRequestForOwner{
   100  		IsController: true,
   101  		OwnerType:    &v1alpha1.Interconnect{},
   102  	})
   103  	if err != nil {
   104  		return err
   105  	}
   106  
   107  	// Watch for changes to secondary resource Service and requeue the owner Interconnect
   108  	err = c.Watch(&source.Kind{Type: &corev1.Service{}}, &handler.EnqueueRequestForOwner{
   109  		IsController: true,
   110  		OwnerType:    &v1alpha1.Interconnect{},
   111  	})
   112  	if err != nil {
   113  		return err
   114  	}
   115  
   116  	// Watch for changes to secondary resource ServiceAccount and requeue the owner Interconnect
   117  	err = c.Watch(&source.Kind{Type: &corev1.ServiceAccount{}}, &handler.EnqueueRequestForOwner{
   118  		IsController: true,
   119  		OwnerType:    &v1alpha1.Interconnect{},
   120  	})
   121  	if err != nil {
   122  		return err
   123  	}
   124  
   125  	// Watch for changes to secondary resource RoleBinding and requeue the owner Interconnect
   126  	err = c.Watch(&source.Kind{Type: &rbacv1.RoleBinding{}}, &handler.EnqueueRequestForOwner{
   127  		IsController: true,
   128  		OwnerType:    &v1alpha1.Interconnect{},
   129  	})
   130  	if err != nil {
   131  		return err
   132  	}
   133  
   134  	// Watch for changes to secondary resource Secret and requeue the owner Interconnect
   135  	err = c.Watch(&source.Kind{Type: &corev1.Secret{}}, &handler.EnqueueRequestForOwner{
   136  		IsController: true,
   137  		OwnerType:    &v1alpha1.Interconnect{},
   138  	})
   139  	if err != nil {
   140  		return err
   141  	}
   142  
   143  	// Watch for changes to secondary resource ConfigMap and requeue the owner Interconnect
   144  	err = c.Watch(&source.Kind{Type: &corev1.ConfigMap{}}, &handler.EnqueueRequestForOwner{
   145  		IsController: true,
   146  		OwnerType:    &v1alpha1.Interconnect{},
   147  	})
   148  	if err != nil {
   149  		return err
   150  	}
   151  
   152  	// Watch for changes to secondary resource Pods and requeue the owner Interconnect
   153  	err = c.Watch(&source.Kind{Type: &corev1.Pod{}}, &handler.EnqueueRequestForOwner{
   154  		IsController: true,
   155  		OwnerType:    &v1alpha1.Interconnect{},
   156  	})
   157  	if err != nil {
   158  		return err
   159  	}
   160  
   161  	if certificates.DetectCertmgrIssuer() {
   162  		// Watch for changes to secondary resource Issuer and requeue the owner Interconnect
   163  		err = c.Watch(&source.Kind{Type: &cmv1alpha1.Issuer{}}, &handler.EnqueueRequestForOwner{
   164  			IsController: true,
   165  			OwnerType:    &v1alpha1.Interconnect{},
   166  		})
   167  
   168  		// Watch for changes to secondary resource Certificates and requeue the owner Interconnect
   169  		err = c.Watch(&source.Kind{Type: &cmv1alpha1.Certificate{}}, &handler.EnqueueRequestForOwner{
   170  			IsController: true,
   171  			OwnerType:    &v1alpha1.Interconnect{},
   172  		})
   173  	}
   174  
   175  	if isOpenShift() {
   176  		// Watch for changes to secondary resource Route and requeue the owner Interconnect
   177  		err = c.Watch(&source.Kind{Type: &routev1.Route{}}, &handler.EnqueueRequestForOwner{
   178  			IsController: true,
   179  			OwnerType:    &v1alpha1.Interconnect{},
   180  		})
   181  		if err != nil {
   182  			return err
   183  		}
   184  	}
   185  
   186  	return nil
   187  }
   188  
   189  var _ reconcile.Reconciler = &ReconcileInterconnect{}
   190  
   191  // ReconcileInterconnect reconciles a Interconnect object
   192  type ReconcileInterconnect struct {
   193  	// This client, initialized using mgr.Client() above, is a split client
   194  	// that reads objects from the cache and writes to the apiserver
   195  	client client.Client
   196  	scheme *runtime.Scheme
   197  }
   198  
   199  func addCondition(conditions []v1alpha1.InterconnectCondition, condition v1alpha1.InterconnectCondition) []v1alpha1.InterconnectCondition {
   200  	size := len(conditions) + 1
   201  	first := 0
   202  	if size > maxConditions {
   203  		first = size - maxConditions
   204  	}
   205  	return append(conditions, condition)[first:size]
   206  }
   207  
   208  // Reconcile reads that state of the cluster for a Interconnect object and makes changes based on the state read
   209  // and what is in the Interconnect.Spec
   210  // Note:
   211  // The Controller will requeue the Request to be processed again if the returned error is non-nil or
   212  // Result.Requeue is true, otherwise upon completion it will remove the work from the queue.
   213  func (r *ReconcileInterconnect) Reconcile(request reconcile.Request) (reconcile.Result, error) {
   214  	reqLogger := log.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name)
   215  	reqLogger.Info("Reconciling Interconnect")
   216  
   217  	// Fetch the Interconnect instance
   218  	instance := &v1alpha1.Interconnect{}
   219  	err := r.client.Get(context.TODO(), request.NamespacedName, instance)
   220  	if err != nil {
   221  		if errors.IsNotFound(err) {
   222  			// Request object not found, could have been deleted after reconcile request.
   223  			// Owned objects are automatically garbage collected. For additional cleanup logic use finalizers.
   224  			// Return and don't requeue
   225  			return reconcile.Result{}, nil
   226  		}
   227  		// Error reading the object - requeue the request.
   228  		return reconcile.Result{}, err
   229  	}
   230  
   231  	// Assign the generated resource version to the status
   232  	if instance.Status.RevNumber == "" {
   233  		instance.Status.RevNumber = instance.ObjectMeta.ResourceVersion
   234  		// update status
   235  		condition := v1alpha1.InterconnectCondition{
   236  			Type:           v1alpha1.InterconnectConditionProvisioning,
   237  			Reason:         "provision spec to desired state",
   238  			TransitionTime: metav1.Now(),
   239  		}
   240  		instance.Status.Conditions = addCondition(instance.Status.Conditions, condition)
   241  		r.client.Status().Update(context.TODO(), instance)
   242  	}
   243  
   244  	requestCert, updateDefaults := configs.SetInterconnectDefaults(instance, certificates.DetectCertmgrIssuer())
   245  	if updateDefaults {
   246  		reqLogger.Info("Updating interconnect instance defaults")
   247  		r.client.Update(context.TODO(), instance)
   248  	}
   249  
   250  	// Check if role already exists, if not create a new one
   251  	roleFound := &rbacv1.Role{}
   252  	err = r.client.Get(context.TODO(), types.NamespacedName{Name: instance.Name, Namespace: instance.Namespace}, roleFound)
   253  	if err != nil && errors.IsNotFound(err) {
   254  		// Define a new role
   255  		role := roles.NewRoleForCR(instance)
   256  		controllerutil.SetControllerReference(instance, role, r.scheme)
   257  		reqLogger.Info("Creating a new Role", "role", role)
   258  		err = r.client.Create(context.TODO(), role)
   259  		if err != nil && !errors.IsAlreadyExists(err) {
   260  			reqLogger.Error(err, "Failed to create new Role")
   261  			return reconcile.Result{}, err
   262  		}
   263  		return reconcile.Result{Requeue: true}, nil
   264  	} else if err != nil {
   265  		reqLogger.Error(err, "Failed to get Role")
   266  		return reconcile.Result{}, err
   267  	}
   268  
   269  	// Check if rolebinding already exists, if not create a new one
   270  	rolebindingFound := &rbacv1.RoleBinding{}
   271  	err = r.client.Get(context.TODO(), types.NamespacedName{Name: instance.Name, Namespace: instance.Namespace}, rolebindingFound)
   272  	if err != nil && errors.IsNotFound(err) {
   273  		// Define a new rolebinding
   274  		rolebinding := rolebindings.NewRoleBindingForCR(instance)
   275  		controllerutil.SetControllerReference(instance, rolebinding, r.scheme)
   276  		reqLogger.Info("Creating a new RoleBinding", "RoleBinding", rolebinding)
   277  		err = r.client.Create(context.TODO(), rolebinding)
   278  		if err != nil && !errors.IsAlreadyExists(err) {
   279  			reqLogger.Error(err, "Failed to create new RoleBinding")
   280  			return reconcile.Result{}, err
   281  		}
   282  		return reconcile.Result{Requeue: true}, nil
   283  	} else if err != nil {
   284  		reqLogger.Error(err, "Failed to get RoleBinding")
   285  		return reconcile.Result{}, err
   286  	}
   287  
   288  	// Check if serviceaccount already exists, if not create a new one
   289  	svcAccntFound := &corev1.ServiceAccount{}
   290  	err = r.client.Get(context.TODO(), types.NamespacedName{Name: instance.Name, Namespace: instance.Namespace}, svcAccntFound)
   291  	if err != nil && errors.IsNotFound(err) {
   292  		// Define a new serviceaccount
   293  		svcaccnt := serviceaccounts.NewServiceAccountForCR(instance)
   294  		controllerutil.SetControllerReference(instance, svcaccnt, r.scheme)
   295  		reqLogger.Info("Creating a new ServiceAccount", "ServiceAccount", svcaccnt)
   296  		err = r.client.Create(context.TODO(), svcaccnt)
   297  		if err != nil && !errors.IsAlreadyExists(err) {
   298  			reqLogger.Error(err, "Failed to create new ServiceAccount")
   299  			return reconcile.Result{}, err
   300  		}
   301  		return reconcile.Result{Requeue: true}, nil
   302  	} else if err != nil {
   303  		reqLogger.Error(err, "Failed to get ServiceAccount")
   304  		return reconcile.Result{}, err
   305  	}
   306  
   307  	if requestCert {
   308  		// If no spec.Issuer, set up a self-signed issuer
   309  		caSecret := instance.Spec.DeploymentPlan.Issuer
   310  		if instance.Spec.DeploymentPlan.Issuer == "" {
   311  			selfSignedIssuerFound := &cmv1alpha1.Issuer{}
   312  			err = r.client.Get(context.TODO(), types.NamespacedName{Name: instance.Name + "-selfsigned", Namespace: instance.Namespace}, selfSignedIssuerFound)
   313  			if err != nil && errors.IsNotFound(err) {
   314  				// Define a new selfsigned issuer
   315  				newIssuer := certificates.NewSelfSignedIssuerForCR(instance)
   316  				controllerutil.SetControllerReference(instance, newIssuer, r.scheme)
   317  				reqLogger.Info("Creating a new self signed issuer %s%s\n", newIssuer.Namespace, newIssuer.Name)
   318  				err = r.client.Create(context.TODO(), newIssuer)
   319  				if err != nil && !errors.IsAlreadyExists(err) {
   320  					reqLogger.Info("Failed to create new self signed issuer", "error", err)
   321  					return reconcile.Result{}, err
   322  				}
   323  				// Issuer created successfully - return and requeue
   324  				return reconcile.Result{Requeue: true}, nil
   325  			} else if err != nil {
   326  				reqLogger.Info("Failed to get self signed issuer", "error", err)
   327  				return reconcile.Result{}, err
   328  			}
   329  
   330  			selfSignedCertFound := &cmv1alpha1.Certificate{}
   331  			err = r.client.Get(context.TODO(), types.NamespacedName{Name: instance.Name + "-selfsigned", Namespace: instance.Namespace}, selfSignedCertFound)
   332  			if err != nil && errors.IsNotFound(err) {
   333  				// Create a new self signed certificate
   334  				cert := certificates.NewSelfSignedCACertificateForCR(instance)
   335  				controllerutil.SetControllerReference(instance, cert, r.scheme)
   336  				reqLogger.Info("Creating a new self signed cert %s%s\n", cert.Namespace, cert.Name)
   337  				err = r.client.Create(context.TODO(), cert)
   338  				if err != nil && !errors.IsAlreadyExists(err) {
   339  					reqLogger.Info("Failed to create new self signed cert", "error", err)
   340  					return reconcile.Result{}, err
   341  				}
   342  				// Cert created successfully - return and requeue
   343  				return reconcile.Result{Requeue: true}, nil
   344  			} else if err != nil {
   345  				reqLogger.Info("Failed to create self signed cert", "error", err)
   346  				return reconcile.Result{}, err
   347  			}
   348  			caSecret = selfSignedCertFound.Name
   349  		}
   350  
   351  		// Check if CA issuer exists and if not create one
   352  		caIssuerFound := &cmv1alpha1.Issuer{}
   353  		err = r.client.Get(context.TODO(), types.NamespacedName{Name: instance.Name + "-ca", Namespace: instance.Namespace}, caIssuerFound)
   354  		if err != nil && errors.IsNotFound(err) {
   355  			// Define a new ca issuer
   356  			newIssuer := certificates.NewCAIssuerForCR(instance, caSecret)
   357  			controllerutil.SetControllerReference(instance, newIssuer, r.scheme)
   358  			reqLogger.Info("Creating a new ca issuer %s%s\n", newIssuer.Namespace, newIssuer.Name)
   359  			err = r.client.Create(context.TODO(), newIssuer)
   360  			if err != nil && !errors.IsAlreadyExists(err) {
   361  				reqLogger.Info("Failed to create new ca issuer", "error", err)
   362  				return reconcile.Result{}, err
   363  			}
   364  			// Issuer created successfully - return and requeue
   365  			return reconcile.Result{Requeue: true}, nil
   366  		} else if err != nil {
   367  			reqLogger.Info("Failed to get ca issuer", "error", err)
   368  			return reconcile.Result{}, err
   369  		}
   370  
   371  		// As needed, create certs for SslProfiles
   372  		for i := range instance.Spec.SslProfiles {
   373  			if instance.Spec.SslProfiles[i].GenerateCaCert {
   374  				caCertFound := &cmv1alpha1.Certificate{}
   375  				err = r.client.Get(context.TODO(), types.NamespacedName{Name: instance.Spec.SslProfiles[i].CaCert, Namespace: instance.Namespace}, caCertFound)
   376  				if err != nil && errors.IsNotFound(err) {
   377  					// Create a new ca certificate
   378  					cert := certificates.NewCACertificateForCR(instance, instance.Spec.SslProfiles[i].CaCert)
   379  					controllerutil.SetControllerReference(instance, cert, r.scheme)
   380  					reqLogger.Info("Creating a new ca cert %s%s\n", cert.Namespace, cert.Name)
   381  					err = r.client.Create(context.TODO(), cert)
   382  					if err != nil && !errors.IsAlreadyExists(err) {
   383  						reqLogger.Info("Failed to create new ca cert", "error", err)
   384  						return reconcile.Result{}, err
   385  					}
   386  				} else if err != nil {
   387  					reqLogger.Info("Failed to create ca cert", "error", err)
   388  					return reconcile.Result{}, err
   389  				}
   390  			}
   391  			if instance.Spec.SslProfiles[i].GenerateCredentials {
   392  				certFound := &cmv1alpha1.Certificate{}
   393  				err = r.client.Get(context.TODO(), types.NamespacedName{Name: instance.Spec.SslProfiles[i].Credentials, Namespace: instance.Namespace}, certFound)
   394  				if err != nil && errors.IsNotFound(err) {
   395  					var issuerName string
   396  					//if MutualAuth is specified use the CA for the profile to generate the credentials, else use the top level issuer
   397  					if instance.Spec.SslProfiles[i].MutualAuth {
   398  						//ensure we have the necessary issuer
   399  						issuerName = instance.Spec.SslProfiles[i].CaCert
   400  						err = ensureCAIssuer(r, instance, issuerName, instance.Namespace, issuerName)
   401  						if err != nil {
   402  							reqLogger.Info("Failed to reconcile CA issuer", "error", err)
   403  							return reconcile.Result{}, err
   404  						}
   405  						reqLogger.Info("Reconciled CA issuer", "profile", instance.Spec.SslProfiles[i].Name)
   406  					}
   407  
   408  					// Create a new certificate
   409  					cert := certificates.NewCertificateForCR(instance, instance.Spec.SslProfiles[i].Name, instance.Spec.SslProfiles[i].Credentials, issuerName)
   410  					controllerutil.SetControllerReference(instance, cert, r.scheme)
   411  					reqLogger.Info("Creating a new cert %s%s\n", cert.Namespace, cert.Name, "issuer", issuerName)
   412  					err = r.client.Create(context.TODO(), cert)
   413  					if err != nil && !errors.IsAlreadyExists(err) {
   414  						reqLogger.Info("Failed to create new cert", "error", err)
   415  						return reconcile.Result{}, err
   416  					}
   417  					// Cert created successfully - set credential return and requeue
   418  					return reconcile.Result{Requeue: true}, nil
   419  				} else if err != nil {
   420  					reqLogger.Info("Failed to create cert", "error", err)
   421  					return reconcile.Result{}, err
   422  				}
   423  			}
   424  		}
   425  	}
   426  
   427  	// Check if the deployment already exists, if not create a new one
   428  	if instance.Spec.DeploymentPlan.Placement == v1alpha1.PlacementAny ||
   429  		instance.Spec.DeploymentPlan.Placement == v1alpha1.PlacementAntiAffinity {
   430  		depFound := &appsv1.Deployment{}
   431  		err = r.client.Get(context.TODO(), types.NamespacedName{Name: instance.Name, Namespace: instance.Namespace}, depFound)
   432  		if err != nil && errors.IsNotFound(err) {
   433  			// Define a new deployment
   434  			dep := deployments.NewDeploymentForCR(instance)
   435  			controllerutil.SetControllerReference(instance, dep, r.scheme)
   436  			reqLogger.Info("Creating a new Deployment", "Deployment", dep)
   437  			err = r.client.Create(context.TODO(), dep)
   438  			if err != nil && !errors.IsAlreadyExists(err) {
   439  				reqLogger.Error(err, "Failed to create new Deployment")
   440  				return reconcile.Result{}, err
   441  			}
   442  			// update status
   443  			condition := v1alpha1.InterconnectCondition{
   444  				Type:           v1alpha1.InterconnectConditionDeployed,
   445  				Reason:         "deployment created",
   446  				TransitionTime: metav1.Now(),
   447  			}
   448  			instance.Status.Conditions = addCondition(instance.Status.Conditions, condition)
   449  			r.client.Status().Update(context.TODO(), instance)
   450  			// Deployment created successfully - return and requeue
   451  			return reconcile.Result{Requeue: true}, nil
   452  		} else if err != nil {
   453  			reqLogger.Error(err, "Failed to get Deployment")
   454  			return reconcile.Result{}, err
   455  		}
   456  
   457  		// Ensure the deployment count is the same as the spec size
   458  		// TODO(ansmith): for now, when deployment does not match,
   459  		// delete to recreate pod instances
   460  		size := instance.Spec.DeploymentPlan.Size
   461  		if size != 0 && *depFound.Spec.Replicas != size {
   462  			ct := v1alpha1.InterconnectConditionScalingUp
   463  			if *depFound.Spec.Replicas > size {
   464  				ct = v1alpha1.InterconnectConditionScalingDown
   465  			}
   466  			*depFound.Spec.Replicas = size
   467  			r.client.Update(context.TODO(), depFound)
   468  			// update status
   469  			condition := v1alpha1.InterconnectCondition{
   470  				Type:           ct,
   471  				Reason:         "Instance spec count updated",
   472  				TransitionTime: metav1.Now(),
   473  			}
   474  			instance.Status.Conditions = addCondition(instance.Status.Conditions, condition)
   475  			instance.Status.PodNames = instance.Status.PodNames[:0]
   476  			r.client.Status().Update(context.TODO(), instance)
   477  			return reconcile.Result{Requeue: true}, nil
   478  		} else if !deployments.CheckDeployedContainer(&depFound.Spec.Template, instance) {
   479  			reqLogger.Info("Container config has changed")
   480  			ct := v1alpha1.InterconnectConditionProvisioning
   481  			err = r.client.Update(context.TODO(), depFound)
   482  			if err != nil {
   483  				reqLogger.Error(err, "Failed to update Deployment", "error", err)
   484  				return reconcile.Result{}, err
   485  			} else {
   486  				reqLogger.Info("Deployment updated", "name", depFound.Name)
   487  			}
   488  			// update status
   489  			condition := v1alpha1.InterconnectCondition{
   490  				Type:           ct,
   491  				Reason:         "Configuration changed",
   492  				TransitionTime: metav1.Now(),
   493  			}
   494  			instance.Status.Conditions = addCondition(instance.Status.Conditions, condition)
   495  			instance.Status.PodNames = instance.Status.PodNames[:0]
   496  			r.client.Status().Update(context.TODO(), instance)
   497  			return reconcile.Result{Requeue: true}, nil
   498  		}
   499  	} else if instance.Spec.DeploymentPlan.Placement == v1alpha1.PlacementEvery {
   500  		dsFound := &appsv1.DaemonSet{}
   501  		err = r.client.Get(context.TODO(), types.NamespacedName{Name: instance.Name, Namespace: instance.Namespace}, dsFound)
   502  		if err != nil && errors.IsNotFound(err) {
   503  			// Define a new daemon set
   504  			ds := deployments.NewDaemonSetForCR(instance)
   505  			controllerutil.SetControllerReference(instance, ds, r.scheme)
   506  			reqLogger.Info("Creating a new DaemonSet", "DaemonSet", ds)
   507  			err = r.client.Create(context.TODO(), ds)
   508  			if err != nil && !errors.IsAlreadyExists(err) {
   509  				reqLogger.Error(err, "Failed to create new DaemonSet")
   510  				return reconcile.Result{}, err
   511  			}
   512  			// update status
   513  			condition := v1alpha1.InterconnectCondition{
   514  				Type:           v1alpha1.InterconnectConditionDeployed,
   515  				Reason:         "daemonset created",
   516  				TransitionTime: metav1.Now(),
   517  			}
   518  			instance.Status.Conditions = addCondition(instance.Status.Conditions, condition)
   519  			r.client.Update(context.TODO(), instance)
   520  			// DaemonSet created successfully - return and requeue
   521  			return reconcile.Result{Requeue: true}, nil
   522  		} else if err != nil {
   523  			reqLogger.Error(err, "Failed to get DaemonSet")
   524  			return reconcile.Result{}, err
   525  		} else if !deployments.CheckDeployedContainer(&dsFound.Spec.Template, instance) {
   526  			reqLogger.Info("Container config has changed")
   527  			ct := v1alpha1.InterconnectConditionProvisioning
   528  			r.client.Update(context.TODO(), dsFound)
   529  			// update status
   530  			condition := v1alpha1.InterconnectCondition{
   531  				Type:           ct,
   532  				Reason:         "Configuration changed",
   533  				TransitionTime: metav1.Now(),
   534  			}
   535  			instance.Status.Conditions = addCondition(instance.Status.Conditions, condition)
   536  			instance.Status.PodNames = instance.Status.PodNames[:0]
   537  			r.client.Status().Update(context.TODO(), instance)
   538  			return reconcile.Result{Requeue: true}, nil
   539  		}
   540  	} //end of placement is every
   541  
   542  	// Check if the service for the deployment already exists, if not create a new one
   543  	svcFound := &corev1.Service{}
   544  	err = r.client.Get(context.TODO(), types.NamespacedName{Name: instance.Name, Namespace: instance.Namespace}, svcFound)
   545  	if err != nil && errors.IsNotFound(err) {
   546  		// Define a new service
   547  		svc := services.NewServiceForCR(instance, requestCert)
   548  		controllerutil.SetControllerReference(instance, svc, r.scheme)
   549  		reqLogger.Info("Creating service for interconnect deployment", "Service", svc)
   550  		err = r.client.Create(context.TODO(), svc)
   551  		if err != nil && !errors.IsAlreadyExists(err) {
   552  			reqLogger.Error(err, "Failed to create new Service")
   553  			return reconcile.Result{}, err
   554  		}
   555  		// Service created successfully - return and requeue
   556  		return reconcile.Result{Requeue: true}, nil
   557  	} else if err != nil {
   558  		reqLogger.Error(err, "Failed to get Service")
   559  		return reconcile.Result{}, err
   560  	}
   561  
   562  	// create route for exposed listeners
   563  	exposedListeners := configs.GetInterconnectExposedListeners(instance)
   564  	for _, listener := range exposedListeners {
   565  		target := listener.Name
   566  		if target == "" {
   567  			target = strconv.Itoa(int(listener.Port))
   568  		}
   569  		if isOpenShift() {
   570  			routeFound := &routev1.Route{}
   571  			err = r.client.Get(context.TODO(), types.NamespacedName{Name: instance.Name + "-" + target, Namespace: instance.Namespace}, routeFound)
   572  			if err != nil && errors.IsNotFound(err) {
   573  				// Define a new route
   574  				if listener.SslProfile == "" && !listener.Http {
   575  					// create the route but issue warning
   576  					reqLogger.Info("Warning an exposed listener should be http or ssl enabled", "listener", listener)
   577  				}
   578  				route := routes.NewRouteForCR(instance, listener)
   579  				controllerutil.SetControllerReference(instance, route, r.scheme)
   580  				reqLogger.Info("Creating route for interconnect deployment", "listener", listener)
   581  				err = r.client.Create(context.TODO(), route)
   582  				if err != nil && !errors.IsAlreadyExists(err) {
   583  					reqLogger.Error(err, "Failed to create new Route")
   584  					return reconcile.Result{}, err
   585  				}
   586  				// Route created successfully - return and requeue
   587  				return reconcile.Result{Requeue: true}, nil
   588  			} else if err != nil {
   589  				reqLogger.Error(err, "Failed to get Route")
   590  				return reconcile.Result{}, err
   591  			}
   592  		} else {
   593  			ingressFound := &extv1b1.Ingress{}
   594  			err = r.client.Get(context.TODO(), types.NamespacedName{Name: instance.Name + "-" + target, Namespace: instance.Namespace}, ingressFound)
   595  			if err != nil && errors.IsNotFound(err) {
   596  				// Define a new Ingress
   597  				if listener.SslProfile == "" && !listener.Http {
   598  					// create the ingress but issue warning
   599  					reqLogger.Info("Warning an exposed listener should be http or ssl enabled", "listener", listener)
   600  				}
   601  				ingress := ingresses.NewIngressForCR(instance, listener)
   602  				controllerutil.SetControllerReference(instance, ingress, r.scheme)
   603  				reqLogger.Info("Creating Ingress for interconnect deployment", "listener", listener)
   604  				err = r.client.Create(context.TODO(), ingress)
   605  				if err != nil && !errors.IsAlreadyExists(err) {
   606  					reqLogger.Error(err, "Failed to create new Ingress")
   607  					return reconcile.Result{}, err
   608  				}
   609  				// Ingress created successfully - return and requeue
   610  				return reconcile.Result{Requeue: true}, nil
   611  			} else if err != nil {
   612  				reqLogger.Error(err, "Failed to get Ingress")
   613  				return reconcile.Result{}, err
   614  			}
   615  		}
   616  	}
   617  
   618  	if !(strings.EqualFold(instance.Spec.Users, "none") || strings.EqualFold(instance.Spec.Users, "false")) {
   619  		// Check if the secret containing users already exists, if not create it
   620  		userSecret := &corev1.Secret{}
   621  		err = r.client.Get(context.TODO(), types.NamespacedName{Name: instance.Spec.Users, Namespace: instance.Namespace}, userSecret)
   622  		if err != nil && errors.IsNotFound(err) {
   623  			labels := selectors.LabelsForInterconnect(instance.Name)
   624  			users := &corev1.Secret{
   625  				TypeMeta: metav1.TypeMeta{
   626  					APIVersion: "v1",
   627  					Kind:       "Secret",
   628  				},
   629  				ObjectMeta: metav1.ObjectMeta{
   630  					Labels:    labels,
   631  					Name:      instance.Spec.Users,
   632  					Namespace: instance.Namespace,
   633  				},
   634  				StringData: map[string]string{"guest": random.GenerateRandomString(8)},
   635  			}
   636  
   637  			controllerutil.SetControllerReference(instance, users, r.scheme)
   638  			reqLogger.Info("Creating user secret for interconnect deployment", "Secret", users)
   639  			err = r.client.Create(context.TODO(), users)
   640  			if err != nil && !errors.IsAlreadyExists(err) {
   641  				reqLogger.Error(err, "Failed to create user secret")
   642  				return reconcile.Result{}, err
   643  			}
   644  			return reconcile.Result{Requeue: true}, nil
   645  		} else if err != nil {
   646  			reqLogger.Error(err, "Failed to get Secret")
   647  			return reconcile.Result{}, err
   648  		}
   649  
   650  		saslConfig := &corev1.ConfigMap{}
   651  		err = r.client.Get(context.TODO(), types.NamespacedName{Name: instance.Name + "-sasl-config", Namespace: instance.Namespace}, saslConfig)
   652  		if err != nil && errors.IsNotFound(err) {
   653  			saslConfig = configmaps.NewConfigMapForSaslConfig(instance)
   654  			controllerutil.SetControllerReference(instance, saslConfig, r.scheme)
   655  			reqLogger.Info("Creating ConfigMap for sasl config", "ConfigMap", saslConfig)
   656  			err = r.client.Create(context.TODO(), saslConfig)
   657  			if err != nil && !errors.IsAlreadyExists(err) {
   658  				reqLogger.Error(err, "Failed to create ConfigMap for sasl config")
   659  				return reconcile.Result{}, err
   660  			}
   661  			return reconcile.Result{Requeue: true}, nil
   662  		} else if err != nil {
   663  			reqLogger.Error(err, "Failed to get ConfigMap for sasl config")
   664  			return reconcile.Result{}, err
   665  		}
   666  
   667  	}
   668  
   669  	// List the pods for this deployment
   670  	podList := &corev1.PodList{}
   671  	labelSelector := selectors.ResourcesByInterconnectName(instance.Name)
   672  	listOps := &client.ListOptions{Namespace: instance.Namespace, LabelSelector: labelSelector}
   673  	err = r.client.List(context.TODO(), listOps, podList)
   674  	if err != nil {
   675  		reqLogger.Error(err, "Failed to list pods")
   676  		return reconcile.Result{}, err
   677  	}
   678  	podNames := getPodNames(podList.Items)
   679  
   680  	// Update status.PodNames if needed
   681  	if !reflect.DeepEqual(podNames, instance.Status.PodNames) {
   682  		instance.Status.PodNames = podNames
   683  		err := r.client.Status().Update(context.TODO(), instance)
   684  		if err != nil {
   685  			reqLogger.Error(err, "Failed to update pod names")
   686  			return reconcile.Result{}, err
   687  		}
   688  		reqLogger.Info("Pod names updated")
   689  		return reconcile.Result{Requeue: true}, nil
   690  	}
   691  
   692  	return reconcile.Result{}, nil
   693  }
   694  
   695  func ensureCAIssuer(r *ReconcileInterconnect, instance *v1alpha1.Interconnect, name string, namespace string, secret string) error {
   696  	caIssuerFound := &cmv1alpha1.Issuer{}
   697  	err := r.client.Get(context.TODO(), types.NamespacedName{Name: name, Namespace: namespace}, caIssuerFound)
   698  	if err != nil && errors.IsNotFound(err) {
   699  		log.Info("Creating CA Issuer")
   700  		newIssuer := certificates.NewCAIssuer(name, namespace, secret)
   701  		controllerutil.SetControllerReference(instance, newIssuer, r.scheme)
   702  		return r.client.Create(context.TODO(), newIssuer)
   703  	} else if err != nil {
   704  		return err
   705  	} else if caIssuerFound.Spec.IssuerConfig.CA.SecretName != secret {
   706  		log.Info("Updating CA Issuer")
   707  		caIssuerFound.Spec.IssuerConfig.CA.SecretName = secret
   708  		return r.client.Update(context.TODO(), caIssuerFound)
   709  	}
   710  	return nil
   711  }
   712  
   713  // getPodNames returns the pod names of the array of pods passed in
   714  func getPodNames(pods []corev1.Pod) []string {
   715  	var podNames []string
   716  	for _, pod := range pods {
   717  		if pod.GetObjectMeta().GetDeletionTimestamp() == nil {
   718  			podNames = append(podNames, pod.Name)
   719  		}
   720  	}
   721  	return podNames
   722  }