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 }