Skip to content

Commit 50060e9

Browse files
committed
Add Results configuration in tekton config
This will add the Results configuration to the tekton config and will be installed by default through tekton config Signed-off-by: Shiv Verma <[email protected]>
1 parent 7b93ebe commit 50060e9

File tree

13 files changed

+610
-14
lines changed

13 files changed

+610
-14
lines changed

pkg/apis/operator/v1alpha1/tektonconfig_defaults.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ func (tc *TektonConfig) SetDefaults(ctx context.Context) {
3232
tc.Spec.Pipeline.setDefaults()
3333
tc.Spec.Trigger.setDefaults()
3434
tc.Spec.Chain.setDefaults()
35+
tc.Spec.Result.setDefaults()
3536

3637
if IsOpenShiftPlatform() {
3738
if tc.Spec.Platforms.OpenShift.PipelinesAsCode == nil {

pkg/apis/operator/v1alpha1/tektonconfig_types.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,9 @@ type TektonConfigSpec struct {
105105
// Chain holds the customizable option for chains component
106106
// +optional
107107
Chain Chain `json:"chain,omitempty"`
108+
// Result holds the customize option for results component
109+
// +optional
110+
Result Result `json:"result,omitempty"`
108111
// Dashboard holds the customizable options for dashboards component
109112
// +optional
110113
Dashboard Dashboard `json:"dashboard,omitempty"`

pkg/apis/operator/v1alpha1/tektonconfig_validation.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ func (tc *TektonConfig) Validate(ctx context.Context) (errs *apis.FieldError) {
120120
errs = errs.Also(tc.Spec.Dashboard.Options.validate("spec.dashboard.options"))
121121
errs = errs.Also(tc.Spec.Chain.Options.validate("spec.chain.options"))
122122
errs = errs.Also(tc.Spec.Trigger.Options.validate("spec.trigger.options"))
123+
errs = errs.Also(tc.Spec.Result.Options.validate("spec.result.options"))
123124

124125
return errs.Also(tc.Spec.Trigger.TriggersProperties.validate("spec.trigger"))
125126
}

pkg/apis/operator/v1alpha1/tektonresult_defaults.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,8 @@ func (tp *TektonResult) SetDefaults(ctx context.Context) {
2626
tp.Spec.TLSHostnameOverride = ""
2727
}
2828
}
29+
30+
// Sets default values of Result
31+
func (c *Result) setDefaults() {
32+
// TODO: Set the other default values for Result
33+
}

pkg/apis/operator/v1alpha1/tektonresult_types.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,15 @@ type LokiStackProperties struct {
6161
LokiStackNamespace string `json:"loki_stack_namespace,omitempty"`
6262
}
6363

64+
// Result defines the field to customize Result component
65+
type Result struct {
66+
// enable or disable Result Component
67+
Disabled bool `json:"disabled"`
68+
TektonResultSpec `json:",inline"`
69+
// Options holds additions fields and these fields will be updated on the manifests
70+
Options AdditionalOptions `json:"options"`
71+
}
72+
6473
// ResultsAPIProperties defines the fields which are configurable for
6574
// Results API server config
6675
type ResultsAPIProperties struct {

pkg/apis/operator/v1alpha1/zz_generated.deepcopy.go

Lines changed: 19 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/reconciler/kubernetes/tektoninstallerset/client/list.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,18 @@ func (i *InstallerSetClient) ListCustomSet(ctx context.Context, labelSelector st
3737
}
3838
return is, nil
3939
}
40+
41+
// ListPreSet return the lists of Pre sets with the provided labelSelector
42+
func (i *InstallerSetClient) ListPreSet(ctx context.Context, labelSelector string) (*v1alpha1.TektonInstallerSetList, error) {
43+
logger := logging.FromContext(ctx)
44+
logger.Debugf("%v: checking installer sets with labels: %v", i.resourceKind, labelSelector)
45+
46+
is, err := i.clientSet.List(ctx, v1.ListOptions{LabelSelector: labelSelector})
47+
if err != nil {
48+
return nil, err
49+
}
50+
if len(is.Items) == 0 {
51+
logger.Debugf("%v: no installer sets found with labels: %v", i.resourceKind, labelSelector)
52+
}
53+
return is, nil
54+
}

pkg/reconciler/kubernetes/tektonresult/tektonresult.go

Lines changed: 203 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,17 @@ package tektonresult
1818

1919
import (
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

4251
const (
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+
}

pkg/reconciler/openshift/tektonresult/extension.go

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ func OpenShiftExtension(ctx context.Context) common.Extension {
7272
logger.Fatalf("Failed to fetch logs RBAC manifest: %v", err)
7373
}
7474

75-
ext := openshiftExtension{
75+
ext := &openshiftExtension{
7676
installerSetClient: client.NewInstallerSetClient(operatorclient.Get(ctx).OperatorV1alpha1().TektonInstallerSets(),
7777
version, "results-ext", v1alpha1.KindTektonResult, nil),
7878
internalDBManifest: internalDBManifest,
@@ -87,6 +87,7 @@ type openshiftExtension struct {
8787
routeManifest *mf.Manifest
8888
internalDBManifest *mf.Manifest
8989
logsRBACManifest *mf.Manifest
90+
removePreset bool
9091
}
9192

9293
func (oe openshiftExtension) Transformers(comp v1alpha1.TektonComponent) []mf.Transformer {
@@ -102,14 +103,20 @@ func (oe openshiftExtension) Transformers(comp v1alpha1.TektonComponent) []mf.Tr
102103
}
103104
}
104105

105-
func (oe openshiftExtension) PreReconcile(ctx context.Context, tc v1alpha1.TektonComponent) error {
106+
func (oe *openshiftExtension) PreReconcile(ctx context.Context, tc v1alpha1.TektonComponent) error {
106107
result := tc.(*v1alpha1.TektonResult)
107-
108108
mf := mf.Manifest{}
109+
109110
if !result.Spec.IsExternalDB {
110111
mf = *oe.internalDBManifest
112+
oe.removePreset = true
113+
}
114+
if result.Spec.IsExternalDB && oe.removePreset {
115+
if err := oe.installerSetClient.CleanupPreSet(ctx); err != nil {
116+
return err
117+
}
118+
oe.removePreset = false
111119
}
112-
113120
if (result.Spec.LokiStackName != "" && result.Spec.LokiStackNamespace != "") ||
114121
strings.EqualFold(result.Spec.LogsType, "LOKI") {
115122
mf = mf.Append(*oe.logsRBACManifest)

0 commit comments

Comments
 (0)