Skip to content

Commit

Permalink
Automate the results installation process
Browse files Browse the repository at this point in the history
- Results will be installed by default and it can be
  enable or disable the result from tektonconfig
- For kubernetes platform user need to create the tls secret
  before installing the result
Todo:
- Statefulset of DB should be deleted on change
- Recreate API server if config map changes
  • Loading branch information
pratap0007 committed Nov 25, 2024
1 parent 2cb98fc commit dbb72d6
Show file tree
Hide file tree
Showing 13 changed files with 484 additions and 9 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
1 change: 0 additions & 1 deletion 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
}
87 changes: 79 additions & 8 deletions pkg/reconciler/kubernetes/tektonresult/tektonresult.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ package tektonresult

import (
"context"
"crypto/rand"
"encoding/base64"
"errors"
"fmt"

Expand Down Expand Up @@ -145,12 +147,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 not set then create default DB otherwise validate it
if !tr.Spec.IsExternalDB {
if err := r.createDBSecret(ctx, tr); err != nil {
return err
}
} else {
if err := r.validateSecretsAreCreated(ctx, tr, DbSecretName); err != nil {
return err
}
}

// Validated TLS Secret for kubernetes platform
if !v1alpha1.IsOpenShiftPlatform() {
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 +328,74 @@ 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
}

// 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
}
32 changes: 32 additions & 0 deletions pkg/reconciler/openshift/tektonresult/extension.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,10 @@ import (
"github.com/tektoncd/operator/pkg/reconciler/common"
"github.com/tektoncd/operator/pkg/reconciler/kubernetes/tektoninstallerset/client"
occommon "github.com/tektoncd/operator/pkg/reconciler/openshift/common"
"github.com/tektoncd/operator/pkg/reconciler/shared/hash"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
k8sruntime "k8s.io/apimachinery/pkg/runtime"
"knative.dev/pkg/logging"
Expand Down Expand Up @@ -110,6 +112,36 @@ func (oe openshiftExtension) PreReconcile(ctx context.Context, tc v1alpha1.Tekto
mf = *oe.internalDBManifest
}

preSetLabel := metav1.LabelSelector{
MatchLabels: map[string]string{
v1alpha1.CreatedByKey: "TektonResult",
v1alpha1.InstallerSetType: "pre",
},
}
preSetLabelSelector, err := common.LabelSelector(preSetLabel)
if err != nil {
return err
}
preSetList, err := oe.installerSetClient.ListCustomSet(ctx, preSetLabelSelector)
if err != nil {
return err
}
for _, is := range preSetList.Items {
// compute TektonResult Spec
expectedSpecHash, err := hash.Compute(result.Spec)
if err != nil {
return err
}
// delete the preset installersets if spec hash been changed
if expectedSpecHash != is.Annotations[v1alpha1.LastAppliedHashKey] {
if err := oe.installerSetClient.CleanupPreSet(ctx); err != nil {
return err
}

}

}

if (result.Spec.LokiStackName != "" && result.Spec.LokiStackNamespace != "") ||
strings.EqualFold(result.Spec.LogsType, "LOKI") {
mf = mf.Append(*oe.logsRBACManifest)
Expand Down
8 changes: 8 additions & 0 deletions pkg/reconciler/shared/tektonconfig/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
tektonConfiginformer "github.com/tektoncd/operator/pkg/client/injection/informers/operator/v1alpha1/tektonconfig"
tektonInstallerinformer "github.com/tektoncd/operator/pkg/client/injection/informers/operator/v1alpha1/tektoninstallerset"
tektonPipelineinformer "github.com/tektoncd/operator/pkg/client/injection/informers/operator/v1alpha1/tektonpipeline"
tektonResultinformer "github.com/tektoncd/operator/pkg/client/injection/informers/operator/v1alpha1/tektonresult"
tektonTriggerinformer "github.com/tektoncd/operator/pkg/client/injection/informers/operator/v1alpha1/tektontrigger"
tektonConfigreconciler "github.com/tektoncd/operator/pkg/client/injection/reconciler/operator/v1alpha1/tektonconfig"
"github.com/tektoncd/operator/pkg/reconciler/common"
Expand Down Expand Up @@ -105,6 +106,13 @@ func NewExtensibleController(generator common.ExtensionGenerator) injection.Cont
logger.Panicf("Couldn't register TektonChain informer event handler: %w", err)
}

if _, err := tektonResultinformer.Get(ctx).Informer().AddEventHandler(cache.FilteringResourceEventHandler{
FilterFunc: controller.FilterController(&v1alpha1.TektonConfig{}),
Handler: controller.HandleAll(impl.EnqueueControllerOf),
}); err != nil {
logger.Panicf("Couldn't register TektonResult informer event handler: %w", err)
}

if _, err := tektonInstallerinformer.Get(ctx).Informer().AddEventHandler(cache.FilteringResourceEventHandler{
FilterFunc: controller.FilterController(&v1alpha1.TektonConfig{}),
Handler: controller.HandleAll(impl.EnqueueControllerOf),
Expand Down
Loading

0 comments on commit dbb72d6

Please sign in to comment.