@@ -18,8 +18,17 @@ package tektonresult
1818
1919import (
2020 "context"
21+ "crypto/ecdsa"
22+ "crypto/elliptic"
23+ "crypto/rand"
24+ "crypto/x509"
25+ "crypto/x509/pkix"
26+ "encoding/base64"
27+ "encoding/pem"
2128 "errors"
2229 "fmt"
30+ "math/big"
31+ "time"
2332
2433 apierrors "k8s.io/apimachinery/pkg/api/errors"
2534 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -40,8 +49,11 @@ import (
4049)
4150
4251const (
43- DbSecretName = "tekton-results-postgres"
44- TlsSecretName = "tekton-results-tls"
52+ DbSecretName = "tekton-results-postgres"
53+ TlsSecretName = "tekton-results-tls"
54+ CertificateBlockType = "CERTIFICATE"
55+ PostgresUser = "result"
56+ ECPrivateKeyBlockType = "EC PRIVATE KEY"
4557)
4658
4759// Reconciler implements controller.Reconciler for TektonResult resources.
@@ -145,12 +157,24 @@ func (r *Reconciler) ReconcileKind(ctx context.Context, tr *v1alpha1.TektonResul
145157 return errors .New (errMsg )
146158 }
147159
148- // check if the secrets are created
149- // TODO: Create secret automatically if they don't exist
150- // TODO: And remove this check in future release.
151- if err := r .validateSecretsAreCreated (ctx , tr ); err != nil {
152- return err
160+ // If the external database is disabled, create a default database and a TLS secret.
161+ // Otherwise, verify if the default database secret is already created, and ensure the TLS secret is also created.
162+ if ! tr .Spec .IsExternalDB {
163+ if err := r .createDBSecret (ctx , tr ); err != nil {
164+ return err
165+ }
166+ if err := r .createTLSSecret (ctx , tr ); err != nil {
167+ return err
168+ }
169+ } else {
170+ if err := r .validateSecretsAreCreated (ctx , tr , DbSecretName ); err != nil {
171+ return err
172+ }
173+ if err := r .createTLSSecret (ctx , tr ); err != nil {
174+ return err
175+ }
153176 }
177+
154178 tr .Status .MarkDependenciesInstalled ()
155179
156180 if err := r .extension .PreReconcile (ctx , tr ); err != nil {
@@ -314,17 +338,186 @@ func (r *Reconciler) updateTektonResultsStatus(ctx context.Context, tr *v1alpha1
314338}
315339
316340// TektonResults expects secrets to be created before installing
317- func (r * Reconciler ) validateSecretsAreCreated (ctx context.Context , tr * v1alpha1.TektonResult ) error {
341+ func (r * Reconciler ) validateSecretsAreCreated (ctx context.Context , tr * v1alpha1.TektonResult , secretName string ) error {
318342 logger := logging .FromContext (ctx )
319- _ , err := r .kubeClientSet .CoreV1 ().Secrets (tr .Spec .TargetNamespace ).Get (ctx , DbSecretName , metav1.GetOptions {})
343+ _ , err := r .kubeClientSet .CoreV1 ().Secrets (tr .Spec .TargetNamespace ).Get (ctx , secretName , metav1.GetOptions {})
320344 if err != nil {
321345 if apierrors .IsNotFound (err ) {
322346 logger .Error (err )
323- tr .Status .MarkDependencyMissing (fmt .Sprintf ("%s secret is missing" , DbSecretName ))
347+ tr .Status .MarkDependencyMissing (fmt .Sprintf ("%s secret is missing" , secretName ))
324348 return err
325349 }
326350 logger .Error (err )
327351 return err
328352 }
329353 return nil
330354}
355+
356+ // Generate the DB secret
357+ func (r * Reconciler ) generateDBSecret (name string , namespace string , tr * v1alpha1.TektonResult ) (* corev1.Secret , error ) {
358+ s := & corev1.Secret {
359+ ObjectMeta : metav1.ObjectMeta {
360+ Name : name ,
361+ Namespace : namespace ,
362+ OwnerReferences : []metav1.OwnerReference {getOwnerRef (tr )},
363+ },
364+ Type : corev1 .SecretTypeOpaque ,
365+ StringData : map [string ]string {},
366+ }
367+ password , err := generateRandomBaseString (20 )
368+ if err != nil {
369+ return nil , err
370+ }
371+ s .StringData ["POSTGRES_PASSWORD" ] = password
372+ s .StringData ["POSTGRES_USER" ] = PostgresUser
373+ return s , nil
374+ }
375+
376+ // Create Result default database secret
377+ func (r * Reconciler ) createDBSecret (ctx context.Context , tr * v1alpha1.TektonResult ) error {
378+ logger := logging .FromContext (ctx )
379+
380+ // Get the DB secret, if not found then create the DB secret
381+ _ , err := r .kubeClientSet .CoreV1 ().Secrets (tr .Spec .TargetNamespace ).Get (ctx , DbSecretName , metav1.GetOptions {})
382+ if err == nil {
383+ return nil
384+ }
385+ if ! apierrors .IsNotFound (err ) {
386+ logger .Errorf ("failed to find default TektonResult database secret %s in namespace %s: %v" , DbSecretName , tr .Spec .TargetNamespace , err )
387+ return err
388+ }
389+ newDBSecret , err := r .generateDBSecret (DbSecretName , tr .Spec .TargetNamespace , tr )
390+ if err != nil {
391+ logger .Errorf ("failed to generate default TektonResult database secret %s: %s" , DbSecretName , err )
392+ return err
393+ }
394+ _ , err = r .kubeClientSet .CoreV1 ().Secrets (tr .Spec .TargetNamespace ).Create (ctx , newDBSecret , metav1.CreateOptions {})
395+ if err != nil {
396+ logger .Errorf ("failed to create default TektonResult database secret %s in namespace %s: %v" , DbSecretName , tr .Spec .TargetNamespace , err )
397+ tr .Status .MarkDependencyMissing (fmt .Sprintf ("Default db %s creation is failing" , DbSecretName ))
398+ return err
399+ }
400+ return nil
401+ }
402+
403+ // Create default TLS certificates for the database
404+ func (r * Reconciler ) createTLSSecret (ctx context.Context , tr * v1alpha1.TektonResult ) error {
405+ logger := logging .FromContext (ctx )
406+
407+ _ , err := r .kubeClientSet .CoreV1 ().Secrets (tr .Spec .TargetNamespace ).Get (ctx , TlsSecretName , metav1.GetOptions {})
408+ if err == nil {
409+ return nil
410+ }
411+ if ! apierrors .IsNotFound (err ) {
412+ logger .Errorf ("failed to find default TektonResult TLS secret %s in namespace %s: %v" , TlsSecretName , tr .Spec .TargetNamespace , err )
413+ return err
414+ }
415+ certPEM , keyPEM , err := generateTLSCertificate (tr .Spec .TargetNamespace )
416+ if err != nil {
417+ logger .Errorf ("failed to generate default TektonResult TLS certificate: %v" , err )
418+ return err
419+ }
420+ // Create Kubernetes TLS secret
421+ err = r .createKubernetesTLSSecret (ctx , tr .Spec .TargetNamespace , TlsSecretName , certPEM , keyPEM , tr )
422+ if err != nil {
423+ logger .Errorf ("failed to create TLS secret %s in namespace %s: %v" , TlsSecretName , tr .Spec .TargetNamespace , err )
424+
425+ }
426+ return nil
427+ }
428+
429+ // Get an owner reference of Tekton Result
430+ func getOwnerRef (tr * v1alpha1.TektonResult ) metav1.OwnerReference {
431+ return * metav1 .NewControllerRef (tr , tr .GroupVersionKind ())
432+ }
433+
434+ func generateRandomBaseString (size int ) (string , error ) {
435+ bytes := make ([]byte , size )
436+
437+ // Generate random bytes
438+ _ , err := rand .Read (bytes )
439+ if err != nil {
440+ return "" , err
441+ }
442+ // Encode the random bytes into a Base64 string
443+ base64String := base64 .StdEncoding .EncodeToString (bytes )
444+
445+ return base64String , nil
446+ }
447+
448+ // generateTLSCertificate generates a self-signed TLS certificate and private key.
449+ func generateTLSCertificate (targetNS string ) (certPEM , keyPEM []byte , err error ) {
450+
451+ // Define subject and DNS names
452+ dnsName := fmt .Sprintf ("tekton-results-api-service.%s.svc.cluster.local" , targetNS )
453+
454+ priv , err := ecdsa .GenerateKey (elliptic .P256 (), rand .Reader )
455+ if err != nil {
456+ return nil , nil , err
457+ }
458+
459+ notBefore := time .Now ()
460+ notAfter := notBefore .Add (365 * 24 * time .Hour )
461+
462+ serialNumber , err := rand .Int (rand .Reader , new (big.Int ).Lsh (big .NewInt (1 ), 128 ))
463+ if err != nil {
464+ return nil , nil , err
465+ }
466+
467+ template := x509.Certificate {
468+ SerialNumber : serialNumber ,
469+ Issuer : pkix.Name {},
470+ Subject : pkix.Name {
471+ CommonName : dnsName ,
472+ },
473+ DNSNames : []string {dnsName },
474+ NotBefore : notBefore ,
475+ NotAfter : notAfter ,
476+ KeyUsage : x509 .KeyUsageKeyEncipherment | x509 .KeyUsageDigitalSignature ,
477+ ExtKeyUsage : []x509.ExtKeyUsage {x509 .ExtKeyUsageServerAuth },
478+ BasicConstraintsValid : true ,
479+ }
480+
481+ certDER , err := x509 .CreateCertificate (rand .Reader , & template , & template , & priv .PublicKey , priv )
482+ if err != nil {
483+ return nil , nil , err
484+ }
485+
486+ certPEM = pem .EncodeToMemory (& pem.Block {Type : CertificateBlockType , Bytes : certDER })
487+
488+ privBytes , err := x509 .MarshalECPrivateKey (priv )
489+ if err != nil {
490+ return nil , nil , err
491+ }
492+ keyPEM = pem .EncodeToMemory (& pem.Block {Type : ECPrivateKeyBlockType , Bytes : privBytes })
493+
494+ return certPEM , keyPEM , nil
495+ }
496+
497+ // createKubernetesSecret creates a Kubernetes TLS secret with the given cert and key.
498+ func (r * Reconciler ) createKubernetesTLSSecret (ctx context.Context , namespace , secretName string , certPEM , keyPEM []byte , tr * v1alpha1.TektonResult ) error {
499+
500+ // Define the secret
501+ logger := logging .FromContext (ctx )
502+ secret := & corev1.Secret {
503+ ObjectMeta : metav1.ObjectMeta {
504+ Name : secretName ,
505+ Namespace : namespace ,
506+ },
507+ Type : corev1 .SecretTypeTLS ,
508+ Data : map [string ][]byte {
509+ corev1 .TLSCertKey : certPEM ,
510+ corev1 .TLSPrivateKeyKey : keyPEM ,
511+ },
512+ }
513+
514+ _ , err := r .kubeClientSet .CoreV1 ().Secrets (tr .Spec .TargetNamespace ).Create (ctx , secret , metav1.CreateOptions {})
515+ if err != nil {
516+ logger .Errorf ("failed to create TLS secret %s in namespace %s: %v" , secretName , namespace , err )
517+ tr .Status .MarkDependencyMissing (fmt .Sprintf ("Default TLS Secret %s creation is failing" , secretName ))
518+ return err
519+ }
520+
521+ logger .Infof ("Secret '%s' created successfully in namespace '%s'\n " , secretName , namespace )
522+ return nil
523+ }
0 commit comments