thelabdude commented on a change in pull request #151: URL: https://github.com/apache/lucene-solr-operator/pull/151#discussion_r568132449
########## File path: controllers/solrcloud_controller.go ########## @@ -772,3 +848,188 @@ func (r *SolrCloudReconciler) indexAndWatchForProvidedConfigMaps(mgr ctrl.Manage }, builder.WithPredicates(predicate.ResourceVersionChangedPredicate{})), nil } + +// Reconciles the TLS cert, returns either a bool to indicate if the cert is ready or an error +func (r *SolrCloudReconciler) reconcileAutoCreateTLS(ctx context.Context, instance *solr.SolrCloud) (bool, error) { + + // short circuit this method with a quick check if the cert exists and is ready + // this is useful b/c it may take many minutes for a cert to be issued, so we avoid + // all the other checking that happens below while we're waiting for the cert + foundCert := &certv1.Certificate{} + if err := r.Get(ctx, types.NamespacedName{Name: instance.Spec.SolrTLS.AutoCreate.Name, Namespace: instance.Namespace}, foundCert); err == nil { + // cert exists, but is it ready? need to wait until we see the TLS secret + if foundTLSSecret := r.isCertificateReady(ctx, foundCert, instance.Spec.SolrTLS); foundTLSSecret != nil { + cert := util.GenerateCertificate(instance) + return r.afterCertificateReady(ctx, instance, &cert, foundCert, foundTLSSecret) + } + } + + r.Log.Info("Reconciling TLS config", "tls", instance.Spec.SolrTLS) + + // cert not found, do full reconcile for TLS ... + var err error + var tlsReady bool + + // First, create the keystore password secret if needed + keystoreSecret := util.GenerateKeystoreSecret(instance) + foundSecret := &corev1.Secret{} + err = r.Get(ctx, types.NamespacedName{Name: keystoreSecret.Name, Namespace: keystoreSecret.Namespace}, foundSecret) + if err != nil && errors.IsNotFound(err) { + r.Log.Info("Creating keystore secret", "namespace", keystoreSecret.Namespace, "name", keystoreSecret.Name) + if err := controllerutil.SetControllerReference(instance, &keystoreSecret, r.scheme); err != nil { + return false, err + } + err = r.Create(ctx, &keystoreSecret) + } + if err != nil { + return false, err + } + + // Create a self-signed cert issuer if no issuerRef provided + if instance.Spec.SolrTLS.AutoCreate.IssuerRef == nil { + issuerName := fmt.Sprintf("%s-selfsigned-issuer", instance.Name) + foundIssuer := &certv1.Issuer{} + err = r.Get(ctx, types.NamespacedName{Name: issuerName, Namespace: instance.Namespace}, foundIssuer) + if err != nil && errors.IsNotFound(err) { + // specified Issuer not found, let's go create a self-signed for this + issuer := util.GenerateSelfSignedIssuer(instance, issuerName) + if err := controllerutil.SetControllerReference(instance, &issuer, r.scheme); err != nil { + return false, err + } + r.Log.Info("Creating Self-signed Certificate Issuer", "issuer", issuer) + err = r.Create(ctx, &issuer) + } else if err == nil { + r.Log.Info("Found Self-signed Certificate Issuer", "issuer", issuerName) + } + if err != nil { + return false, err + } + } else { + // real problems arise if we create the Certificate and the Issuer doesn't exist so make we have a good config here + if instance.Spec.SolrTLS.AutoCreate.IssuerRef.Kind == "Issuer" { + foundIssuer := &certv1.Issuer{} + err = r.Get(ctx, types.NamespacedName{Name: instance.Spec.SolrTLS.AutoCreate.IssuerRef.Name, Namespace: instance.Namespace}, foundIssuer) + if err != nil { + if errors.IsNotFound(err) { + r.Log.Info("cert-manager Issuer not found in namespace, cannot create a TLS certificate without an Issuer", + "issuer", instance.Spec.SolrTLS.AutoCreate.IssuerRef.Name, "ns", instance.Namespace) + } + return false, err + } + } // else assume ClusterIssuer and good luck + } + + // Reconcile the Certificate to use for TLS ... A Certificate is a request to Issue the cert, the + // actual cert lives in a TLS secret created by the Issuer + cert := util.GenerateCertificate(instance) + err = r.Get(ctx, types.NamespacedName{Name: cert.Name, Namespace: cert.Namespace}, foundCert) + if err != nil && errors.IsNotFound(err) { + r.Log.Info("Creating Certificate", "cert", cert) + // Set the operator as the owner of the cert + if err := controllerutil.SetControllerReference(instance, &cert, r.scheme); err != nil { + return false, err + } + // Create the cert + err = r.Create(ctx, &cert) + if err != nil { + return false, err + } + } else if err == nil { + r.Log.Info("Found Certificate, checking if it is ready", "cert", foundCert.Name) + if foundTLSSecret := r.isCertificateReady(ctx, foundCert, instance.Spec.SolrTLS); foundTLSSecret != nil { + tlsReady, err = r.afterCertificateReady(ctx, instance, &cert, foundCert, foundTLSSecret) + if tlsReady { + r.Log.Info("TLS Certificate reconciled.", "cert", foundCert.Name) + } + } else { + r.Log.Info("Certificate not ready, current status", "status", foundCert.Status) + } + } + + if err != nil { + return false, err + } + + return tlsReady, nil +} + +func (r *SolrCloudReconciler) isCertificateReady(ctx context.Context, cert *certv1.Certificate, tlsOpts *solr.SolrTLSOptions) *corev1.Secret { + // Cert is ready, lookup the secret holding the keystore + foundTLSSecret := &corev1.Secret{} + err := r.Get(ctx, types.NamespacedName{Name: cert.Spec.SecretName, Namespace: cert.Namespace}, foundTLSSecret) + if err != nil { + if errors.IsNotFound(err) { + r.Log.Info("TLS secret not found", "name", cert.Spec.SecretName) + } else { + r.Log.Error(err, "TLS secret lookup failed", "name", cert.Spec.SecretName) + } + foundTLSSecret = nil + } + + if foundTLSSecret == nil { + if cert.Status.Conditions != nil { + for _, cond := range cert.Status.Conditions { + if cond.Type == certv1.CertificateConditionIssuing { + r.Log.Info("Certificate is still issuing", "name", cert.Name, "status", cond.Status) + break + } + } + } + } + + // Make sure the secret containing the keystore password exists as well + if foundTLSSecret != nil { + keyStorePasswordSecret := &corev1.Secret{} + err := r.Get(ctx, types.NamespacedName{Name: tlsOpts.KeyStorePasswordSecret.Name, Namespace: foundTLSSecret.Namespace}, keyStorePasswordSecret) + if err != nil { + r.Log.Error(err, "Keystore password secret not found", "name", tlsOpts.KeyStorePasswordSecret.Name) Review comment: The reconcile loop would recover once the user creates the keystore password secret so this just tells them it doesn't exist but will proceed once they create it. What alternative solution do you suggest for dealing with required user input that the user didn't provide? ---------------------------------------------------------------- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. For queries about this service, please contact Infrastructure at: us...@infra.apache.org --------------------------------------------------------------------- To unsubscribe, e-mail: issues-unsubscr...@lucene.apache.org For additional commands, e-mail: issues-h...@lucene.apache.org