Skip to content

Commit

Permalink
Add Results configuration in tekton config
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
pratap0007 committed Dec 16, 2024
1 parent 541032c commit 00bbc52
Show file tree
Hide file tree
Showing 13 changed files with 602 additions and 12 deletions.
1 change: 1 addition & 0 deletions pkg/apis/operator/v1alpha1/tektonconfig_defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ func (tc *TektonConfig) SetDefaults(ctx context.Context) {
tc.Spec.Pipeline.setDefaults()
tc.Spec.Trigger.setDefaults()
tc.Spec.Chain.setDefaults()
tc.Spec.Result.setDefaults()

if IsOpenShiftPlatform() {
if tc.Spec.Platforms.OpenShift.PipelinesAsCode == nil {
Expand Down
3 changes: 3 additions & 0 deletions pkg/apis/operator/v1alpha1/tektonconfig_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ type TektonConfigSpec struct {
// Chain holds the customizable option for chains component
// +optional
Chain Chain `json:"chain,omitempty"`
// Result holds the customize option for results component
// +optional
Result Result `json:"result,omitempty"`
// Dashboard holds the customizable options for dashboards component
// +optional
Dashboard Dashboard `json:"dashboard,omitempty"`
Expand Down
1 change: 1 addition & 0 deletions pkg/apis/operator/v1alpha1/tektonconfig_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ func (tc *TektonConfig) Validate(ctx context.Context) (errs *apis.FieldError) {
errs = errs.Also(tc.Spec.Dashboard.Options.validate("spec.dashboard.options"))
errs = errs.Also(tc.Spec.Chain.Options.validate("spec.chain.options"))
errs = errs.Also(tc.Spec.Trigger.Options.validate("spec.trigger.options"))
errs = errs.Also(tc.Spec.Result.Options.validate("spec.result.options"))

return errs.Also(tc.Spec.Trigger.TriggersProperties.validate("spec.trigger"))
}
Expand Down
5 changes: 5 additions & 0 deletions pkg/apis/operator/v1alpha1/tektonresult_defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,8 @@ func (tp *TektonResult) SetDefaults(ctx context.Context) {
tp.Spec.TLSHostnameOverride = ""
}
}

// Sets default values of Result
func (c *Result) setDefaults() {
// TODO: Set the other default values for Result
}
9 changes: 9 additions & 0 deletions pkg/apis/operator/v1alpha1/tektonresult_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,15 @@ type LokiStackProperties struct {
LokiStackNamespace string `json:"loki_stack_namespace,omitempty"`
}

// Result defines the field to customize Result component
type Result struct {
// enable or disable Result Component
Disabled bool `json:"disabled"`
TektonResultSpec `json:",inline"`
// Options holds additions fields and these fields will be updated on the manifests
Options AdditionalOptions `json:"options"`
}

// ResultsAPIProperties defines the fields which are configurable for
// Results API server config
type ResultsAPIProperties struct {
Expand Down
19 changes: 19 additions & 0 deletions pkg/apis/operator/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions pkg/reconciler/kubernetes/tektoninstallerset/client/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,18 @@ func (i *InstallerSetClient) ListCustomSet(ctx context.Context, labelSelector st
}
return is, nil
}

// ListPreSet return the lists of Pre sets with the provided labelSelector
func (i *InstallerSetClient) ListPreSet(ctx context.Context, labelSelector string) (*v1alpha1.TektonInstallerSetList, error) {
logger := logging.FromContext(ctx)
logger.Debugf("%v: checking installer sets with labels: %v", i.resourceKind, labelSelector)

is, err := i.clientSet.List(ctx, v1.ListOptions{LabelSelector: labelSelector})
if err != nil {
return nil, err
}
if len(is.Items) == 0 {
logger.Debugf("%v: no installer sets found with labels: %v", i.resourceKind, labelSelector)
}
return is, nil
}
207 changes: 199 additions & 8 deletions pkg/reconciler/kubernetes/tektonresult/tektonresult.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,19 @@ package tektonresult

import (
"context"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"encoding/base64"
"encoding/pem"
"errors"
"fmt"
"log"
"math/big"
"os"
"time"

apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down Expand Up @@ -145,12 +156,24 @@ func (r *Reconciler) ReconcileKind(ctx context.Context, tr *v1alpha1.TektonResul
return errors.New(errMsg)
}

// check if the secrets are created
// TODO: Create secret automatically if they don't exist
// TODO: And remove this check in future release.
if err := r.validateSecretsAreCreated(ctx, tr); err != nil {
return err
// If external database is disable then create default database and tls secret
// otherwise checks database and secret are created or not
if !tr.Spec.IsExternalDB {
if err := r.createDBSecret(ctx, tr); err != nil {
return err
}
if err := r.createTLSSecret(ctx, tr); err != nil {
return err
}
} else {
if err := r.validateSecretsAreCreated(ctx, tr, DbSecretName); err != nil {
return err
}
if err := r.validateSecretsAreCreated(ctx, tr, TlsSecretName); err != nil {
return err
}
}

tr.Status.MarkDependenciesInstalled()

if err := r.extension.PreReconcile(ctx, tr); err != nil {
Expand Down Expand Up @@ -314,17 +337,185 @@ func (r *Reconciler) updateTektonResultsStatus(ctx context.Context, tr *v1alpha1
}

// TektonResults expects secrets to be created before installing
func (r *Reconciler) validateSecretsAreCreated(ctx context.Context, tr *v1alpha1.TektonResult) error {
func (r *Reconciler) validateSecretsAreCreated(ctx context.Context, tr *v1alpha1.TektonResult, secretName string) error {
logger := logging.FromContext(ctx)
_, err := r.kubeClientSet.CoreV1().Secrets(tr.Spec.TargetNamespace).Get(ctx, DbSecretName, metav1.GetOptions{})
_, err := r.kubeClientSet.CoreV1().Secrets(tr.Spec.TargetNamespace).Get(ctx, secretName, metav1.GetOptions{})
if err != nil {
if apierrors.IsNotFound(err) {
logger.Error(err)
tr.Status.MarkDependencyMissing(fmt.Sprintf("%s secret is missing", DbSecretName))
tr.Status.MarkDependencyMissing(fmt.Sprintf("%s secret is missing", secretName))
return err
}
logger.Error(err)
return err
}
return nil
}

// Generate the DB secret
func (r *Reconciler) getDBSecret(name string, namespace string, tr *v1alpha1.TektonResult) *corev1.Secret {
s := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
OwnerReferences: []metav1.OwnerReference{getOwnerRef(tr)},
},
Type: corev1.SecretTypeOpaque,
StringData: map[string]string{},
}
password, _ := generateRandomBaseString(20)
s.StringData["POSTGRES_PASSWORD"] = password
s.StringData["POSTGRES_USER"] = "result"
return s
}

// Create Result default database
func (r *Reconciler) createDBSecret(ctx context.Context, tr *v1alpha1.TektonResult) error {
logger := logging.FromContext(ctx)

// Get the DB secret, if not found then create the DB secret
_, err := r.kubeClientSet.CoreV1().Secrets(tr.Spec.TargetNamespace).Get(ctx, DbSecretName, metav1.GetOptions{})
if err != nil {
if apierrors.IsNotFound(err) {
// If not found then create DB secret with default data
newDBSecret := r.getDBSecret(DbSecretName, tr.Spec.TargetNamespace, tr)
_, err := r.kubeClientSet.CoreV1().Secrets(tr.Spec.TargetNamespace).Create(ctx, newDBSecret, metav1.CreateOptions{})
if err != nil {
logger.Error(err)
tr.Status.MarkDependencyMissing(fmt.Sprintf("Default db %s creation is failing", DbSecretName))
return err
}
}
}
return nil
}

// Create TLS certificates for the database
func (r *Reconciler) createTLSSecret(ctx context.Context, tr *v1alpha1.TektonResult) error {
logger := logging.FromContext(ctx)

_, err := r.kubeClientSet.CoreV1().Secrets(tr.Spec.TargetNamespace).Get(ctx, TlsSecretName, metav1.GetOptions{})
if err != nil {
if apierrors.IsNotFound(err) {

certPEM, keyPEM, err := generateTLSCertificate()
if err != nil {
logger.Errorf("Error generating TLS certificate: %v", err)
}
err = os.WriteFile("cert.pem", certPEM, 0644)
if err != nil {
logger.Errorf("Error writing cert.pem: %v", err)
}
err = os.WriteFile("key.pem", keyPEM, 0600)
if err != nil {
logger.Errorf("Error writing key.pem: %v", err)
}

// Create Kubernetes secret
err = r.createKubernetesTLSSecret(ctx, tr.Spec.TargetNamespace, TlsSecretName, certPEM, keyPEM, tr)
if err != nil {
log.Fatalf("Error creating Kubernetes secret: %v", err)
}

}
logger.Error(err)
return err
}
return nil
}

// Get an owner reference of Tekton Result
func getOwnerRef(tr *v1alpha1.TektonResult) metav1.OwnerReference {
return *metav1.NewControllerRef(tr, tr.GroupVersionKind())
}

func generateRandomBaseString(size int) (string, error) {
bytes := make([]byte, size)

// Generate random bytes
_, err := rand.Read(bytes)
if err != nil {
return "", err
}
// Encode the random bytes into a Base64 string
base64String := base64.StdEncoding.EncodeToString(bytes)

return base64String, nil
}

// generateTLSCertificate generates a self-signed TLS certificate and private key.
func generateTLSCertificate() (certPEM, keyPEM []byte, err error) {

// Define subject and DNS names
dnsName := fmt.Sprintf("tekton-results-api-service.%s.svc.cluster.local", "tekton-pipelines")

priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return nil, nil, err
}

notBefore := time.Now()
notAfter := notBefore.Add(365 * 24 * time.Hour)

serialNumber, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
if err != nil {
return nil, nil, err
}

template := x509.Certificate{
SerialNumber: serialNumber,
Issuer: pkix.Name{},
Subject: pkix.Name{
CommonName: dnsName,
},
DNSNames: []string{dnsName},
NotBefore: notBefore,
NotAfter: notAfter,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
}

certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
if err != nil {
return nil, nil, err
}

certPEM = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER})

privBytes, err := x509.MarshalECPrivateKey(priv)
if err != nil {
return nil, nil, err
}
keyPEM = pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: privBytes})

return certPEM, keyPEM, nil
}

// createKubernetesSecret creates a Kubernetes TLS secret with the given cert and key.
func (r *Reconciler) createKubernetesTLSSecret(ctx context.Context, namespace, secretName string, certPEM, keyPEM []byte, tr *v1alpha1.TektonResult) error {

// Define the secret
logger := logging.FromContext(ctx)
secret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: secretName,
Namespace: namespace,
},
Type: corev1.SecretTypeTLS,
Data: map[string][]byte{
corev1.TLSCertKey: certPEM,
corev1.TLSPrivateKeyKey: keyPEM,
},
}

_, err := r.kubeClientSet.CoreV1().Secrets(tr.Spec.TargetNamespace).Create(ctx, secret, metav1.CreateOptions{})
if err != nil {
logger.Error(err)
tr.Status.MarkDependencyMissing(fmt.Sprintf("Default db %s creation is failing", DbSecretName))
return err
}

log.Printf("Secret '%s' created successfully in namespace '%s'\n", secretName, namespace)
return nil
}
15 changes: 11 additions & 4 deletions pkg/reconciler/openshift/tektonresult/extension.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ func OpenShiftExtension(ctx context.Context) common.Extension {
logger.Fatalf("Failed to fetch logs RBAC manifest: %v", err)
}

ext := openshiftExtension{
ext := &openshiftExtension{
installerSetClient: client.NewInstallerSetClient(operatorclient.Get(ctx).OperatorV1alpha1().TektonInstallerSets(),
version, "results-ext", v1alpha1.KindTektonResult, nil),
internalDBManifest: internalDBManifest,
Expand All @@ -87,6 +87,7 @@ type openshiftExtension struct {
routeManifest *mf.Manifest
internalDBManifest *mf.Manifest
logsRBACManifest *mf.Manifest
removePreset bool
}

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

func (oe openshiftExtension) PreReconcile(ctx context.Context, tc v1alpha1.TektonComponent) error {
func (oe *openshiftExtension) PreReconcile(ctx context.Context, tc v1alpha1.TektonComponent) error {
result := tc.(*v1alpha1.TektonResult)

mf := mf.Manifest{}

if !result.Spec.IsExternalDB {
mf = *oe.internalDBManifest
oe.removePreset = true
}
if result.Spec.IsExternalDB && oe.removePreset {
if err := oe.installerSetClient.CleanupPreSet(ctx); err != nil {
return err
}
oe.removePreset = false
}

if (result.Spec.LokiStackName != "" && result.Spec.LokiStackNamespace != "") ||
strings.EqualFold(result.Spec.LogsType, "LOKI") {
mf = mf.Append(*oe.logsRBACManifest)
Expand Down
Loading

0 comments on commit 00bbc52

Please sign in to comment.