diff --git a/pkg/apis/operator/v1alpha1/tektonresult_lifecycle.go b/pkg/apis/operator/v1alpha1/tektonresult_lifecycle.go index b1b2fed5ca..369b13262d 100644 --- a/pkg/apis/operator/v1alpha1/tektonresult_lifecycle.go +++ b/pkg/apis/operator/v1alpha1/tektonresult_lifecycle.go @@ -25,8 +25,8 @@ var ( _ TektonComponentStatus = (*TektonResultStatus)(nil) resultsCondSet = apis.NewLivingConditionSet( DependenciesInstalled, - DeploymentsAvailable, - InstallSucceeded, + InstallerSetAvailable, + InstallerSetReady, ) ) @@ -35,6 +35,10 @@ func (tr *TektonResult) GroupVersionKind() schema.GroupVersionKind { return SchemeGroupVersion.WithKind(KindTektonResult) } +func (tr *TektonResult) GetGroupVersionKind() schema.GroupVersionKind { + return SchemeGroupVersion.WithKind(KindTektonResult) +} + // GetCondition returns the current condition of a given condition type func (trs *TektonResultStatus) GetCondition(t apis.ConditionType) *apis.Condition { return resultsCondSet.Manage(trs).GetCondition(t) @@ -50,36 +54,35 @@ func (trs *TektonResultStatus) IsReady() bool { return resultsCondSet.Manage(trs).IsHappy() } -// MarkInstallSucceeded marks the InstallationSucceeded status as true. -func (trs *TektonResultStatus) MarkInstallSucceeded() { - resultsCondSet.Manage(trs).MarkTrue(InstallSucceeded) - if trs.GetCondition(DependenciesInstalled).IsUnknown() { - // Assume deps are installed if we're not sure - trs.MarkDependenciesInstalled() - } +func (trs *TektonResultStatus) MarkNotReady(msg string) { + resultsCondSet.Manage(trs).MarkFalse( + apis.ConditionReady, + "Error", + "Ready: %s", msg) } -// MarkInstallFailed marks the InstallationSucceeded status as false with the given -// message. -func (trs *TektonResultStatus) MarkInstallFailed(msg string) { +func (trs *TektonResultStatus) MarkInstallerSetAvailable() { + resultsCondSet.Manage(trs).MarkTrue(InstallerSetAvailable) +} + +func (trs *TektonResultStatus) MarkInstallerSetNotAvailable(msg string) { + trs.MarkNotReady("TektonInstallerSet not ready") resultsCondSet.Manage(trs).MarkFalse( - InstallSucceeded, + InstallerSetAvailable, "Error", - "Install failed with message: %s", msg) + "Installer set not ready: %s", msg) } -// MarkDeploymentsAvailable marks the DeploymentsAvailable status as true. -func (trs *TektonResultStatus) MarkDeploymentsAvailable() { - resultsCondSet.Manage(trs).MarkTrue(DeploymentsAvailable) +func (trs *TektonResultStatus) MarkInstallerSetReady() { + resultsCondSet.Manage(trs).MarkTrue(InstallerSetReady) } -// MarkDeploymentsNotReady marks the DeploymentsAvailable status as false and calls out -// it's waiting for deployments. -func (trs *TektonResultStatus) MarkDeploymentsNotReady() { +func (trs *TektonResultStatus) MarkInstallerSetNotReady(msg string) { + trs.MarkNotReady("TektonInstallerSet not ready") resultsCondSet.Manage(trs).MarkFalse( - DeploymentsAvailable, - "NotReady", - "Waiting on deployments") + InstallerSetReady, + "Error", + "Installer set not ready: %s", msg) } // MarkDependenciesInstalled marks the DependenciesInstalled status as true. @@ -105,6 +108,14 @@ func (trs *TektonResultStatus) MarkDependencyMissing(msg string) { "Dependency missing: %s", msg) } +func (trs *TektonResultStatus) GetTektonInstallerSet() string { + return trs.TektonInstallerSet +} + +func (trs *TektonResultStatus) SetTektonInstallerSet(installerSet string) { + trs.TektonInstallerSet = installerSet +} + // GetVersion gets the currently installed version of the component. func (trs *TektonResultStatus) GetVersion() string { return trs.Version @@ -115,12 +126,29 @@ func (trs *TektonResultStatus) SetVersion(version string) { trs.Version = version } -// GetManifests gets the url links of the manifests. -func (trs *TektonResultStatus) GetManifests() []string { - return trs.Manifests +// MarkInstallSucceeded marks the InstallationSucceeded status as true. +func (trs *TektonResultStatus) MarkInstallSucceeded() { + panic("implement me") } -// SetManifests sets the url links of the manifests. -func (trs *TektonResultStatus) SetManifests(manifests []string) { - trs.Manifests = manifests +// MarkInstallFailed marks the InstallationSucceeded status as false with the given +// message. +func (trs *TektonResultStatus) MarkInstallFailed(msg string) { + panic("implement me") +} + +// MarkDeploymentsAvailable marks the DeploymentsAvailable status as true. +func (trs *TektonResultStatus) MarkDeploymentsAvailable() { + panic("implement me") +} + +// MarkDeploymentsNotReady marks the DeploymentsAvailable status as false and calls out +// it's waiting for deployments. +func (trs *TektonResultStatus) MarkDeploymentsNotReady() { + panic("implement me") +} + +// GetManifests gets the url links of the manifests. +func (trs *TektonResultStatus) GetManifests() []string { + panic("Implement me") } diff --git a/pkg/apis/operator/v1alpha1/tektonresult_lifecycle_test.go b/pkg/apis/operator/v1alpha1/tektonresult_lifecycle_test.go index 20c6060bbf..cf4b2925c2 100644 --- a/pkg/apis/operator/v1alpha1/tektonresult_lifecycle_test.go +++ b/pkg/apis/operator/v1alpha1/tektonresult_lifecycle_test.go @@ -40,30 +40,19 @@ func TestTektonResultHappyPath(t *testing.T) { tt.InitializeConditions() apistest.CheckConditionOngoing(tt, DependenciesInstalled, t) - apistest.CheckConditionOngoing(tt, DeploymentsAvailable, t) - apistest.CheckConditionOngoing(tt, InstallSucceeded, t) + apistest.CheckConditionOngoing(tt, InstallerSetAvailable, t) + apistest.CheckConditionOngoing(tt, InstallerSetReady, t) - // Install succeeds. - tt.MarkInstallSucceeded() - // Dependencies are assumed successful too. + // Dependencies installed + tt.MarkDependenciesInstalled() apistest.CheckConditionSucceeded(tt, DependenciesInstalled, t) - apistest.CheckConditionOngoing(tt, DeploymentsAvailable, t) - apistest.CheckConditionSucceeded(tt, InstallSucceeded, t) - // Deployments are not available at first. - tt.MarkDeploymentsNotReady() - apistest.CheckConditionSucceeded(tt, DependenciesInstalled, t) - apistest.CheckConditionFailed(tt, DeploymentsAvailable, t) - apistest.CheckConditionSucceeded(tt, InstallSucceeded, t) - if ready := tt.IsReady(); ready { - t.Errorf("tt.IsReady() = %v, want false", ready) - } + tt.MarkInstallerSetAvailable() + apistest.CheckConditionSucceeded(tt, InstallerSetAvailable, t) + + tt.MarkInstallerSetReady() + apistest.CheckConditionSucceeded(tt, InstallerSetReady, t) - // Deployments become ready and we're good. - tt.MarkDeploymentsAvailable() - apistest.CheckConditionSucceeded(tt, DependenciesInstalled, t) - apistest.CheckConditionSucceeded(tt, DeploymentsAvailable, t) - apistest.CheckConditionSucceeded(tt, InstallSucceeded, t) if ready := tt.IsReady(); !ready { t.Errorf("tt.IsReady() = %v, want true", ready) } @@ -74,44 +63,24 @@ func TestTektonResultErrorPath(t *testing.T) { tt.InitializeConditions() apistest.CheckConditionOngoing(tt, DependenciesInstalled, t) - apistest.CheckConditionOngoing(tt, DeploymentsAvailable, t) - apistest.CheckConditionOngoing(tt, InstallSucceeded, t) + apistest.CheckConditionOngoing(tt, InstallerSetAvailable, t) + apistest.CheckConditionOngoing(tt, InstallerSetReady, t) - // Install fails. - tt.MarkInstallFailed("test") - apistest.CheckConditionOngoing(tt, DependenciesInstalled, t) - apistest.CheckConditionOngoing(tt, DeploymentsAvailable, t) - apistest.CheckConditionFailed(tt, InstallSucceeded, t) + // Dependencies installed + tt.MarkDependenciesInstalled() + apistest.CheckConditionSucceeded(tt, DependenciesInstalled, t) - // Dependencies are installing. - tt.MarkDependencyInstalling("testing") - apistest.CheckConditionFailed(tt, DependenciesInstalled, t) - apistest.CheckConditionOngoing(tt, DeploymentsAvailable, t) - apistest.CheckConditionFailed(tt, InstallSucceeded, t) + tt.MarkInstallerSetAvailable() + apistest.CheckConditionSucceeded(tt, InstallerSetAvailable, t) - // Install now succeeds. - tt.MarkInstallSucceeded() - apistest.CheckConditionFailed(tt, DependenciesInstalled, t) - apistest.CheckConditionOngoing(tt, DeploymentsAvailable, t) - apistest.CheckConditionSucceeded(tt, InstallSucceeded, t) - if ready := tt.IsReady(); ready { - t.Errorf("tt.IsReady() = %v, want false", ready) - } + // InstallerSet is not ready when deployment pods are not up + tt.MarkInstallerSetNotReady("waiting for deployments") + apistest.CheckConditionFailed(tt, InstallerSetReady, t) - // Deployments become ready - tt.MarkDeploymentsAvailable() - apistest.CheckConditionFailed(tt, DependenciesInstalled, t) - apistest.CheckConditionSucceeded(tt, DeploymentsAvailable, t) - apistest.CheckConditionSucceeded(tt, InstallSucceeded, t) - if ready := tt.IsReady(); ready { - t.Errorf("tt.IsReady() = %v, want false", ready) - } + // InstallerSet and then PostReconciler become ready and we're good. + tt.MarkInstallerSetReady() + apistest.CheckConditionSucceeded(tt, InstallerSetReady, t) - // Finally, dependencies become available. - tt.MarkDependenciesInstalled() - apistest.CheckConditionSucceeded(tt, DependenciesInstalled, t) - apistest.CheckConditionSucceeded(tt, DeploymentsAvailable, t) - apistest.CheckConditionSucceeded(tt, InstallSucceeded, t) if ready := tt.IsReady(); !ready { t.Errorf("tt.IsReady() = %v, want true", ready) } @@ -123,16 +92,15 @@ func TestTektonResultExternalDependency(t *testing.T) { // External marks dependency as failed. tt.MarkDependencyMissing("test") + tt.MarkInstallerSetReady() - // Install succeeds. - tt.MarkInstallSucceeded() apistest.CheckConditionFailed(tt, DependenciesInstalled, t) - apistest.CheckConditionOngoing(tt, DeploymentsAvailable, t) - apistest.CheckConditionSucceeded(tt, InstallSucceeded, t) + apistest.CheckConditionOngoing(tt, InstallerSetAvailable, t) + apistest.CheckConditionSucceeded(tt, InstallerSetReady, t) // Dependencies are now ready. tt.MarkDependenciesInstalled() apistest.CheckConditionSucceeded(tt, DependenciesInstalled, t) - apistest.CheckConditionOngoing(tt, DeploymentsAvailable, t) - apistest.CheckConditionSucceeded(tt, InstallSucceeded, t) + apistest.CheckConditionOngoing(tt, InstallerSetAvailable, t) + apistest.CheckConditionSucceeded(tt, InstallerSetReady, t) } diff --git a/pkg/apis/operator/v1alpha1/tektonresult_types.go b/pkg/apis/operator/v1alpha1/tektonresult_types.go index 255ac369a0..6f8d65be82 100644 --- a/pkg/apis/operator/v1alpha1/tektonresult_types.go +++ b/pkg/apis/operator/v1alpha1/tektonresult_types.go @@ -62,9 +62,9 @@ type TektonResultStatus struct { // +optional Version string `json:"version,omitempty"` - // The url links of the manifests, separated by comma + // The current installer set name for TektonResult // +optional - Manifests []string `json:"manifests,omitempty"` + TektonInstallerSet string `json:"tektonInstallerSet,omitempty"` } // TektonResultsList contains a list of TektonResult diff --git a/pkg/apis/operator/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/operator/v1alpha1/zz_generated.deepcopy.go index b4087869db..19d10d773c 100644 --- a/pkg/apis/operator/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/operator/v1alpha1/zz_generated.deepcopy.go @@ -1199,11 +1199,6 @@ func (in *TektonResultSpec) DeepCopy() *TektonResultSpec { func (in *TektonResultStatus) DeepCopyInto(out *TektonResultStatus) { *out = *in in.Status.DeepCopyInto(&out.Status) - if in.Manifests != nil { - in, out := &in.Manifests, &out.Manifests - *out = make([]string, len(*in)) - copy(*out, *in) - } return } diff --git a/pkg/reconciler/kubernetes/tektoninstallerset/install.go b/pkg/reconciler/kubernetes/tektoninstallerset/install.go index e459763d6f..3b987b1bb3 100644 --- a/pkg/reconciler/kubernetes/tektoninstallerset/install.go +++ b/pkg/reconciler/kubernetes/tektoninstallerset/install.go @@ -46,6 +46,7 @@ var ( jobPred = mf.ByKind("Job") secretPred = mf.ByKind("Secret") deploymentPred = mf.ByKind("Deployment") + statefulSetPred = mf.ByKind("StatefulSet") servicePred = mf.ByKind("Service") serviceAccountPred = mf.ByKind("ServiceAccount") cronJobPred = mf.ByKind("CronJob") @@ -153,6 +154,7 @@ func (i *installer) EnsureNamespaceScopedResources() error { triggerTemplatePred, servicePred, routePred, + statefulSetPred, )) return ensureResources(&resourceList) } diff --git a/pkg/reconciler/kubernetes/tektonresult/controller.go b/pkg/reconciler/kubernetes/tektonresult/controller.go index 7d1694a2f6..9a528c4649 100644 --- a/pkg/reconciler/kubernetes/tektonresult/controller.go +++ b/pkg/reconciler/kubernetes/tektonresult/controller.go @@ -24,6 +24,7 @@ import ( mf "github.com/manifestival/manifestival" "github.com/tektoncd/operator/pkg/apis/operator/v1alpha1" operatorclient "github.com/tektoncd/operator/pkg/client/injection/client" + 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" tektonResultReconciler "github.com/tektoncd/operator/pkg/client/injection/reconciler/operator/v1alpha1/tektonresult" @@ -31,7 +32,6 @@ import ( "go.uber.org/zap" "k8s.io/client-go/tools/cache" kubeclient "knative.dev/pkg/client/injection/kube/client" - deploymentInformer "knative.dev/pkg/client/injection/kube/informers/apps/v1/deployment" "knative.dev/pkg/configmap" "knative.dev/pkg/controller" "knative.dev/pkg/injection" @@ -59,21 +59,31 @@ func NewExtendedController(generator common.ExtensionGenerator) injection.Contro logger.Fatalw("Error creating initial manifest", zap.Error(err)) } + operatorVer, err := common.OperatorVersion(ctx) + if err != nil { + logger.Fatal(err) + } + + if err := common.AppendTarget(ctx, &manifest, &v1alpha1.TektonResult{}); err != nil { + logger.Fatalw("Error fetching manifests", zap.Error(err)) + } + c := &Reconciler{ kubeClientSet: kubeclient.Get(ctx), operatorClientSet: operatorclient.Get(ctx), extension: generator(ctx), manifest: manifest, pipelineInformer: tektonPipelineInformer.Get(ctx), + operatorVersion: operatorVer, } impl := tektonResultReconciler.NewImpl(ctx, c) - logger.Info("Setting up event handlers") + logger.Info("Setting up event handlers for tekton-results") tektonResultInformer.Get(ctx).Informer().AddEventHandler(controller.HandleAll(impl.Enqueue)) - deploymentInformer.Get(ctx).Informer().AddEventHandler(cache.FilteringResourceEventHandler{ - FilterFunc: controller.FilterControllerGVK(v1alpha1.SchemeGroupVersion.WithKind("TektonResult")), + tektonInstallerinformer.Get(ctx).Informer().AddEventHandler(cache.FilteringResourceEventHandler{ + FilterFunc: controller.FilterController(&v1alpha1.TektonResult{}), Handler: controller.HandleAll(impl.EnqueueControllerOf), }) diff --git a/pkg/reconciler/kubernetes/tektonresult/installerset.go b/pkg/reconciler/kubernetes/tektonresult/installerset.go new file mode 100644 index 0000000000..1c642d6e88 --- /dev/null +++ b/pkg/reconciler/kubernetes/tektonresult/installerset.go @@ -0,0 +1,75 @@ +/* +Copyright 2022 The Tekton Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tektonresult + +import ( + "context" + "fmt" + + mf "github.com/manifestival/manifestival" + "github.com/tektoncd/operator/pkg/apis/operator/v1alpha1" + "github.com/tektoncd/operator/pkg/reconciler/shared/hash" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func (r *Reconciler) createInstallerSet(ctx context.Context, tr *v1alpha1.TektonResult) (*v1alpha1.TektonInstallerSet, error) { + + if err := r.transform(ctx, &r.manifest, tr); err != nil { + tr.Status.MarkNotReady("transformation failed: " + err.Error()) + return nil, err + } + + // compute the hash of tektonresult spec and store as an annotation + // in further reconciliation we compute hash of td spec and check with + // annotation, if they are same then we skip updating the object + // otherwise we update the manifest + specHash, err := hash.Compute(tr.Spec) + if err != nil { + return nil, err + } + + // create installer set + tis := r.makeInstallerSet(tr, r.manifest, specHash) + createdIs, err := r.operatorClientSet.OperatorV1alpha1().TektonInstallerSets(). + Create(ctx, tis, metav1.CreateOptions{}) + if err != nil { + return nil, err + } + return createdIs, nil +} + +func (r *Reconciler) makeInstallerSet(tr *v1alpha1.TektonResult, manifest mf.Manifest, trSpecHash string) *v1alpha1.TektonInstallerSet { + ownerRef := *metav1.NewControllerRef(tr, tr.GetGroupVersionKind()) + return &v1alpha1.TektonInstallerSet{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: fmt.Sprintf("%s-", v1alpha1.ResultResourceName), + Labels: map[string]string{ + v1alpha1.CreatedByKey: createdByValue, + v1alpha1.InstallerSetType: v1alpha1.ResultResourceName, + v1alpha1.ReleaseVersionKey: r.operatorVersion, + }, + Annotations: map[string]string{ + v1alpha1.TargetNamespaceKey: tr.Spec.TargetNamespace, + v1alpha1.LastAppliedHashKey: trSpecHash, + }, + OwnerReferences: []metav1.OwnerReference{ownerRef}, + }, + Spec: v1alpha1.TektonInstallerSetSpec{ + Manifests: manifest.Resources(), + }, + } +} diff --git a/pkg/reconciler/kubernetes/tektonresult/tektonresult.go b/pkg/reconciler/kubernetes/tektonresult/tektonresult.go index b237bdf3dd..4520c35f47 100644 --- a/pkg/reconciler/kubernetes/tektonresult/tektonresult.go +++ b/pkg/reconciler/kubernetes/tektonresult/tektonresult.go @@ -29,7 +29,11 @@ import ( pipelineInformer "github.com/tektoncd/operator/pkg/client/informers/externalversions/operator/v1alpha1" tektonresultconciler "github.com/tektoncd/operator/pkg/client/injection/reconciler/operator/v1alpha1/tektonresult" "github.com/tektoncd/operator/pkg/reconciler/common" + "github.com/tektoncd/operator/pkg/reconciler/kubernetes/tektoninstallerset" + "github.com/tektoncd/operator/pkg/reconciler/shared/hash" + corev1 "k8s.io/api/core/v1" "k8s.io/client-go/kubernetes" + "knative.dev/pkg/apis" "knative.dev/pkg/logging" pkgreconciler "knative.dev/pkg/reconciler" ) @@ -54,41 +58,44 @@ type Reconciler struct { extension common.Extension pipelineInformer pipelineInformer.TektonPipelineInformer + + operatorVersion string } // Check that our Reconciler implements controller.Reconciler var _ tektonresultconciler.Interface = (*Reconciler)(nil) var _ tektonresultconciler.Finalizer = (*Reconciler)(nil) +var ( + ls = metav1.LabelSelector{ + MatchLabels: map[string]string{ + v1alpha1.CreatedByKey: createdByValue, + v1alpha1.InstallerSetType: v1alpha1.ResultResourceName, + }, + } +) + +const createdByValue = "TektonResult" + // FinalizeKind removes all resources after deletion of a TektonResult. func (r *Reconciler) FinalizeKind(ctx context.Context, original *v1alpha1.TektonResult) pkgreconciler.Event { logger := logging.FromContext(ctx) - // List all TektonResults to determine if cluster-scoped resources should be deleted. - tps, err := r.operatorClientSet.OperatorV1alpha1().TektonResults().List(ctx, metav1.ListOptions{}) + labelSelector, err := common.LabelSelector(ls) if err != nil { - return fmt.Errorf("failed to list all TektonResults: %w", err) + return err } - - for _, tp := range tps.Items { - if tp.GetDeletionTimestamp().IsZero() { - // Not deleting all TektonResults. Nothing to do here. - return nil - } + if err := r.operatorClientSet.OperatorV1alpha1().TektonInstallerSets(). + DeleteCollection(ctx, metav1.DeleteOptions{}, metav1.ListOptions{ + LabelSelector: labelSelector, + }); err != nil { + logger.Error("Failed to delete installer set created by TektonResult", err) + return err } if err := r.extension.Finalize(ctx, original); err != nil { logger.Error("Failed to finalize platform resources", err) } - logger.Info("Deleting cluster-scoped resources") - manifest, err := r.installed(ctx, original) - if err != nil { - logger.Error("Unable to fetch installed manifest; no cluster-scoped resources will be finalized", err) - return nil - } - if err := common.Uninstall(ctx, manifest, nil); err != nil { - logger.Error("Failed to finalize platform resources", err) - } return nil } @@ -136,14 +143,136 @@ func (r *Reconciler) ReconcileKind(ctx context.Context, tr *v1alpha1.TektonResul } tr.Status.MarkDependenciesInstalled() - stages := common.Stages{ - common.AppendTarget, - r.transform, - common.Install, - common.CheckDeployments, + // Check if an tektoninstallerset already exists, if not then create + labelSelector, err := common.LabelSelector(ls) + if err != nil { + return err } - manifest := r.manifest.Append() - return stages.Execute(ctx, &manifest, tr) + existingInstallerSet, err := tektoninstallerset.CurrentInstallerSetName(ctx, r.operatorClientSet, labelSelector) + if err != nil { + return err + } + if existingInstallerSet == "" { + createdIs, err := r.createInstallerSet(ctx, tr) + if err != nil { + return err + } + + return r.updateTektonResultsStatus(ctx, tr, createdIs) + } + + // If exists, then fetch the TektonInstallerSet + installedTIS, err := r.operatorClientSet.OperatorV1alpha1().TektonInstallerSets(). + Get(ctx, existingInstallerSet, metav1.GetOptions{}) + if err != nil { + if apierrors.IsNotFound(err) { + createdIs, err := r.createInstallerSet(ctx, tr) + if err != nil { + return err + } + return r.updateTektonResultsStatus(ctx, tr, createdIs) + } + logger.Error("failed to get InstallerSet: %s", err) + return err + } + + installerSetTargetNamespace := installedTIS.Annotations[v1alpha1.TargetNamespaceKey] + installerSetReleaseVersion := installedTIS.Labels[v1alpha1.ReleaseVersionKey] + + // Check if TargetNamespace of existing TektonInstallerSet is same as expected + // Check if Release Version in TektonInstallerSet is same as expected + // If any of the thing above is not same then delete the existing TektonInstallerSet + // and create a new with expected properties + if installerSetTargetNamespace != tr.Spec.TargetNamespace || installerSetReleaseVersion != r.operatorVersion { + // Delete the existing TektonInstallerSet + err := r.operatorClientSet.OperatorV1alpha1().TektonInstallerSets(). + Delete(ctx, existingInstallerSet, metav1.DeleteOptions{}) + if err != nil { + logger.Error("failed to delete InstallerSet: %s", err) + return err + } + + // Make sure the TektonInstallerSet is deleted + _, err = r.operatorClientSet.OperatorV1alpha1().TektonInstallerSets(). + Get(ctx, existingInstallerSet, metav1.GetOptions{}) + if err == nil { + tr.Status.MarkNotReady("Waiting for previous installer set to get deleted") + return v1alpha1.REQUEUE_EVENT_AFTER + } + if !apierrors.IsNotFound(err) { + logger.Error("failed to get InstallerSet: %s", err) + return err + } + return nil + + } else { + // If target namespace and version are not changed then check if spec + // of TektonResult is changed by checking hash stored as annotation on + // TektonInstallerSet with computing new hash of TektonResult Spec + + // Hash of TektonResult Spec + expectedSpecHash, err := hash.Compute(tr.Spec) + if err != nil { + return err + } + + // spec hash stored on installerSet + lastAppliedHash := installedTIS.GetAnnotations()[v1alpha1.LastAppliedHashKey] + + if lastAppliedHash != expectedSpecHash { + + if err := r.transform(ctx, &r.manifest, tr); err != nil { + logger.Error("manifest transformation failed: ", err) + return err + } + + // Update the spec hash + current := installedTIS.GetAnnotations() + current[v1alpha1.LastAppliedHashKey] = expectedSpecHash + installedTIS.SetAnnotations(current) + + // Update the manifests + installedTIS.Spec.Manifests = r.manifest.Resources() + + if _, err = r.operatorClientSet.OperatorV1alpha1().TektonInstallerSets(). + Update(ctx, installedTIS, metav1.UpdateOptions{}); err != nil { + return err + } + + // after updating installer set enqueue after a duration + // to allow changes to get deployed + return v1alpha1.REQUEUE_EVENT_AFTER + } + } + + // Mark InstallerSetAvailable + tr.Status.MarkInstallerSetAvailable() + + ready := installedTIS.Status.GetCondition(apis.ConditionReady) + if ready == nil { + tr.Status.MarkInstallerSetNotReady("Waiting for installation") + return v1alpha1.REQUEUE_EVENT_AFTER + } + + if ready.Status == corev1.ConditionUnknown { + tr.Status.MarkInstallerSetNotReady("Waiting for installation") + return v1alpha1.REQUEUE_EVENT_AFTER + } else if ready.Status == corev1.ConditionFalse { + tr.Status.MarkInstallerSetNotReady(ready.Message) + return v1alpha1.REQUEUE_EVENT_AFTER + } + + // Mark InstallerSet Ready + tr.Status.MarkInstallerSetReady() + + return nil +} + +func (r *Reconciler) updateTektonResultsStatus(ctx context.Context, tr *v1alpha1.TektonResult, createdIs *v1alpha1.TektonInstallerSet) error { + // update the tr with TektonInstallerSet + tr.Status.SetTektonInstallerSet(createdIs.Name) + + return nil } // TektonResults expects secrets to be created before installing @@ -185,11 +314,3 @@ func (r *Reconciler) transform(ctx context.Context, manifest *mf.Manifest, comp extra = append(extra, r.extension.Transformers(instance)...) return common.Transform(ctx, manifest, instance, extra...) } - -func (r *Reconciler) installed(ctx context.Context, instance v1alpha1.TektonComponent) (*mf.Manifest, error) { - // Create new, empty manifest with valid client and logger - installed := r.manifest.Append() - stages := common.Stages{common.AppendInstalled, r.transform} - err := stages.Execute(ctx, &installed, instance) - return &installed, err -}