From 5af093663de9889ced5e81d7a709befe22a7329c Mon Sep 17 00:00:00 2001 From: Shivam Mukhade Date: Thu, 24 Feb 2022 13:11:54 +0530 Subject: [PATCH] [OpenShift] Refactors TektonAddon reconciler This refactors addon reconciler and moves all components to individual file rather than keeping everything at single place to improve readability. Signed-off-by: Shivam Mukhade --- .../openshift/tektonaddon/clustertTask.go | 166 ++++++ .../tektonaddon/clustertTaskVersioned.go | 105 ++++ .../openshift/tektonaddon/installerset.go | 160 ++++++ .../openshift/tektonaddon/pipelinesascode.go | 83 +++ .../openshift/tektonaddon/pipelinetemplate.go | 101 ++++ .../openshift/tektonaddon/tektonaddon.go | 483 +----------------- .../openshift/tektonaddon/triggers.go | 77 +++ 7 files changed, 697 insertions(+), 478 deletions(-) create mode 100644 pkg/reconciler/openshift/tektonaddon/clustertTask.go create mode 100644 pkg/reconciler/openshift/tektonaddon/clustertTaskVersioned.go create mode 100644 pkg/reconciler/openshift/tektonaddon/installerset.go create mode 100644 pkg/reconciler/openshift/tektonaddon/pipelinesascode.go create mode 100644 pkg/reconciler/openshift/tektonaddon/pipelinetemplate.go create mode 100644 pkg/reconciler/openshift/tektonaddon/triggers.go diff --git a/pkg/reconciler/openshift/tektonaddon/clustertTask.go b/pkg/reconciler/openshift/tektonaddon/clustertTask.go new file mode 100644 index 0000000000..cecd8143fd --- /dev/null +++ b/pkg/reconciler/openshift/tektonaddon/clustertTask.go @@ -0,0 +1,166 @@ +/* +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 tektonaddon + +import ( + "context" + "fmt" + "strings" + + mf "github.com/manifestival/manifestival" + "github.com/tektoncd/operator/pkg/apis/operator/v1alpha1" + "github.com/tektoncd/operator/pkg/reconciler/common" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "knative.dev/pkg/logging" +) + +var clusterTaskLS = metav1.LabelSelector{ + MatchLabels: map[string]string{ + v1alpha1.InstallerSetType: ClusterTaskInstallerSet, + }, +} + +// byContains returns resources with specific string in name +func byContains(name string) mf.Predicate { + return func(u *unstructured.Unstructured) bool { + return strings.Contains(u.GetName(), name) + } +} + +var communityResourceURLs = []string{ + "https://raw.githubusercontent.com/tektoncd/catalog/master/task/jib-maven/0.4/jib-maven.yaml", + "https://raw.githubusercontent.com/tektoncd/catalog/master/task/maven/0.2/maven.yaml", + "https://raw.githubusercontent.com/tektoncd/catalog/master/task/helm-upgrade-from-source/0.3/helm-upgrade-from-source.yaml", + "https://raw.githubusercontent.com/tektoncd/catalog/master/task/helm-upgrade-from-repo/0.2/helm-upgrade-from-repo.yaml", + "https://raw.githubusercontent.com/tektoncd/catalog/master/task/trigger-jenkins-job/0.1/trigger-jenkins-job.yaml", + "https://raw.githubusercontent.com/tektoncd/catalog/master/task/git-cli/0.3/git-cli.yaml", + "https://raw.githubusercontent.com/tektoncd/catalog/master/task/pull-request/0.1/pull-request.yaml", + "https://raw.githubusercontent.com/tektoncd/catalog/master/task/kubeconfig-creator/0.1/kubeconfig-creator.yaml", + "https://raw.githubusercontent.com/tektoncd/catalog/main/task/argocd-task-sync-and-wait/0.1/argocd-task-sync-and-wait.yaml", +} + +func (r *Reconciler) EnsureClusterTask(ctx context.Context, enable string, ta *v1alpha1.TektonAddon) error { + + clusterTaskLabelSelector, err := common.LabelSelector(clusterTaskLS) + if err != nil { + return err + } + + if enable == "true" { + + exist, err := checkIfInstallerSetExist(ctx, r.operatorClientSet, r.operatorVersion, clusterTaskLabelSelector) + if err != nil { + return err + } + + if !exist { + msg := fmt.Sprintf("%s being created/upgraded", ClusterTaskInstallerSet) + ta.Status.MarkInstallerSetNotReady(msg) + return r.ensureClusterTasks(ctx, ta) + } + + if err := r.checkComponentStatus(ctx, clusterTaskLabelSelector); err != nil { + ta.Status.MarkInstallerSetNotReady(err.Error()) + return nil + } + + } else { + // if disabled then delete the installer Set if exist + if err := r.deleteInstallerSet(ctx, clusterTaskLabelSelector); err != nil { + return err + } + } + + return nil +} + +// installerset for non versioned clustertask like buildah and community clustertask +func (r *Reconciler) ensureClusterTasks(ctx context.Context, ta *v1alpha1.TektonAddon) error { + clusterTaskManifest := mf.Manifest{} + // Read clusterTasks from ko data + if err := applyAddons(&clusterTaskManifest, "02-clustertasks"); err != nil { + return err + } + // Run transformers + if err := r.addonTransform(ctx, &clusterTaskManifest, ta); err != nil { + return err + } + + clusterTaskManifest = clusterTaskManifest.Filter( + mf.Not(byContains(getFormattedVersion(r.operatorVersion))), + ) + + communityClusterTaskManifest := r.manifest + if err := r.appendCommunityTarget(ctx, &communityClusterTaskManifest, ta); err != nil { + // Continue if failed to resolve community task URL. + // (Ex: on disconnected cluster community tasks won't be reachable because of proxy). + logging.FromContext(ctx).Error("Failed to get community task: Skipping community tasks installation ", err) + } else { + if err := r.communityTransform(ctx, &communityClusterTaskManifest, ta); err != nil { + return err + } + + clusterTaskManifest = clusterTaskManifest.Append(communityClusterTaskManifest) + } + + if err := createInstallerSet(ctx, r.operatorClientSet, ta, clusterTaskManifest, + r.operatorVersion, ClusterTaskInstallerSet, "addon-clustertasks"); err != nil { + return err + } + + return nil +} + +// appendCommunityTarget mutates the passed manifest by appending one +// appropriate for the passed TektonComponent +func (r *Reconciler) appendCommunityTarget(ctx context.Context, manifest *mf.Manifest, comp v1alpha1.TektonComponent) error { + urls := strings.Join(communityResourceURLs, ",") + m, err := mf.ManifestFrom(mf.Path(urls)) + if err != nil { + return err + } + *manifest = manifest.Append(m) + return nil +} + +// communityTransform mutates the passed manifest to one with common component +// and platform transformations applied +func (r *Reconciler) communityTransform(ctx context.Context, manifest *mf.Manifest, comp v1alpha1.TektonComponent) error { + instance := comp.(*v1alpha1.TektonAddon) + extra := []mf.Transformer{ + replaceKind("Task", "ClusterTask"), + injectLabel(labelProviderType, providerTypeCommunity, overwrite, "ClusterTask"), + } + extra = append(extra, r.extension.Transformers(instance)...) + return common.Transform(ctx, manifest, instance, extra...) +} + +// To get the version in the format as in clustertask name i.e. 1-6 +func getFormattedVersion(version string) string { + version = strings.TrimPrefix(getPatchVersionTrimmed(version), "v") + return strings.Replace(version, ".", "-", -1) +} + +// To get the minor major version for label i.e. v1.6 +func getPatchVersionTrimmed(version string) string { + endIndex := strings.LastIndex(version, ".") + if endIndex != -1 { + version = version[:endIndex] + } + return version +} diff --git a/pkg/reconciler/openshift/tektonaddon/clustertTaskVersioned.go b/pkg/reconciler/openshift/tektonaddon/clustertTaskVersioned.go new file mode 100644 index 0000000000..86dbd4d094 --- /dev/null +++ b/pkg/reconciler/openshift/tektonaddon/clustertTaskVersioned.go @@ -0,0 +1,105 @@ +/* +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 tektonaddon + +import ( + "context" + "fmt" + + mf "github.com/manifestival/manifestival" + "github.com/tektoncd/operator/pkg/apis/operator/v1alpha1" + "github.com/tektoncd/operator/pkg/reconciler/common" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func (r *Reconciler) EnsureVersionedClusterTask(ctx context.Context, enable string, ta *v1alpha1.TektonAddon) error { + + versionedClusterTaskLS := metav1.LabelSelector{ + MatchLabels: map[string]string{ + v1alpha1.InstallerSetType: VersionedClusterTaskInstallerSet, + v1alpha1.ReleaseMinorVersionKey: getPatchVersionTrimmed(r.operatorVersion), + }, + } + + versionedClusterTaskLabelSelector, err := common.LabelSelector(versionedClusterTaskLS) + if err != nil { + return err + } + + if enable == "true" { + + // here pass two labels one for type and other for minor release version to remove the previous minor release installerset only not all + exist, err := checkIfInstallerSetExist(ctx, r.operatorClientSet, r.operatorVersion, versionedClusterTaskLabelSelector) + if err != nil { + return err + } + + if !exist { + msg := fmt.Sprintf("%s being created/upgraded", VersionedClusterTaskInstallerSet) + ta.Status.MarkInstallerSetNotReady(msg) + return r.ensureVersionedClusterTasks(ctx, ta) + } + + // here pass two labels one for type and other for operator release version to get the latest installerset of current version + vClusterTaskLS := metav1.LabelSelector{ + MatchLabels: map[string]string{ + v1alpha1.InstallerSetType: VersionedClusterTaskInstallerSet, + v1alpha1.ReleaseVersionKey: r.operatorVersion, + }, + } + vClusterTaskLabelSelector, err := common.LabelSelector(vClusterTaskLS) + if err != nil { + return err + } + if err := r.checkComponentStatus(ctx, vClusterTaskLabelSelector); err != nil { + ta.Status.MarkInstallerSetNotReady(err.Error()) + return nil + } + + } else { + // if disabled then delete the installer Set if exist + if err := r.deleteInstallerSet(ctx, versionedClusterTaskLabelSelector); err != nil { + return err + } + } + + return nil +} + +// installerset for versioned clustertask like buildah-1-6-0 +func (r *Reconciler) ensureVersionedClusterTasks(ctx context.Context, ta *v1alpha1.TektonAddon) error { + clusterTaskManifest := mf.Manifest{} + // Read clusterTasks from ko data + if err := applyAddons(&clusterTaskManifest, "02-clustertasks"); err != nil { + return err + } + // Run transformers + if err := r.addonTransform(ctx, &clusterTaskManifest, ta); err != nil { + return err + } + + clusterTaskManifest = clusterTaskManifest.Filter( + byContains(getFormattedVersion(r.operatorVersion)), + ) + + if err := createInstallerSet(ctx, r.operatorClientSet, ta, clusterTaskManifest, + r.operatorVersion, VersionedClusterTaskInstallerSet, "addon-versioned-clustertasks"); err != nil { + return err + } + + return nil +} diff --git a/pkg/reconciler/openshift/tektonaddon/installerset.go b/pkg/reconciler/openshift/tektonaddon/installerset.go new file mode 100644 index 0000000000..5ef93c9259 --- /dev/null +++ b/pkg/reconciler/openshift/tektonaddon/installerset.go @@ -0,0 +1,160 @@ +/* +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 tektonaddon + +import ( + "context" + "fmt" + + mf "github.com/manifestival/manifestival" + "github.com/tektoncd/operator/pkg/apis/operator/v1alpha1" + clientset "github.com/tektoncd/operator/pkg/client/clientset/versioned" + "github.com/tektoncd/operator/pkg/reconciler/shared/hash" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "knative.dev/pkg/apis" +) + +// checkIfInstallerSetExist checks if installer set exists for a component and return true/false based on it +// and if installer set which already exist is of older version then it deletes and return false to create a new +// installer set +func checkIfInstallerSetExist(ctx context.Context, oc clientset.Interface, relVersion string, + labelSelector string) (bool, error) { + + installerSets, err := oc.OperatorV1alpha1().TektonInstallerSets(). + List(ctx, metav1.ListOptions{ + LabelSelector: labelSelector, + }) + if err != nil { + return false, err + } + + if len(installerSets.Items) == 0 { + return false, nil + } + + if len(installerSets.Items) == 1 { + // if already created then check which version it is + version, ok := installerSets.Items[0].Labels[v1alpha1.ReleaseVersionKey] + if ok && version == relVersion { + // if installer set already exist and release version is same + // then ignore and move on + return true, nil + } + } + + // release version doesn't exist or is different from expected + // deleted existing InstallerSet and create a new one + // or there is more than one installerset (unexpected) + if err = oc.OperatorV1alpha1().TektonInstallerSets(). + DeleteCollection(ctx, metav1.DeleteOptions{}, metav1.ListOptions{ + LabelSelector: labelSelector, + }); err != nil { + return false, err + } + + return false, v1alpha1.RECONCILE_AGAIN_ERR +} + +func createInstallerSet(ctx context.Context, oc clientset.Interface, ta *v1alpha1.TektonAddon, + manifest mf.Manifest, releaseVersion, component, installerSetPrefix string) error { + + specHash, err := hash.Compute(ta.Spec) + if err != nil { + return err + } + + is := makeInstallerSet(ta, manifest, installerSetPrefix, releaseVersion, component, specHash) + + if _, err := oc.OperatorV1alpha1().TektonInstallerSets(). + Create(ctx, is, metav1.CreateOptions{}); err != nil { + return err + } + + return v1alpha1.RECONCILE_AGAIN_ERR +} + +func makeInstallerSet(ta *v1alpha1.TektonAddon, manifest mf.Manifest, prefix, releaseVersion, component, specHash string) *v1alpha1.TektonInstallerSet { + ownerRef := *metav1.NewControllerRef(ta, ta.GetGroupVersionKind()) + labels := map[string]string{ + v1alpha1.CreatedByKey: CreatedByValue, + v1alpha1.InstallerSetType: component, + v1alpha1.ReleaseVersionKey: releaseVersion, + } + namePrefix := fmt.Sprintf("%s-", prefix) + // special label to make sure no two versioned clustertask installerset exist + // for all patch releases + if component == VersionedClusterTaskInstallerSet { + labels[v1alpha1.ReleaseMinorVersionKey] = getPatchVersionTrimmed(releaseVersion) + namePrefix = fmt.Sprintf("%s%s-", namePrefix, getFormattedVersion(releaseVersion)) + } + return &v1alpha1.TektonInstallerSet{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: namePrefix, + Labels: labels, + Annotations: map[string]string{ + v1alpha1.TargetNamespaceKey: ta.Spec.TargetNamespace, + v1alpha1.LastAppliedHashKey: specHash, + }, + OwnerReferences: []metav1.OwnerReference{ownerRef}, + }, + Spec: v1alpha1.TektonInstallerSetSpec{ + Manifests: manifest.Resources(), + }, + } +} + +func (r *Reconciler) checkComponentStatus(ctx context.Context, labelSelector string) error { + + // Check if installer set is already created + installerSets, err := r.operatorClientSet.OperatorV1alpha1().TektonInstallerSets(). + List(ctx, metav1.ListOptions{ + LabelSelector: labelSelector, + }) + + if err != nil { + if errors.IsNotFound(err) { + return nil + } + return err + } + + // To make sure there won't be duplicate installersets. + if len(installerSets.Items) == 1 { + ready := installerSets.Items[0].Status.GetCondition(apis.ConditionReady) + if ready == nil || ready.Status == corev1.ConditionUnknown { + return fmt.Errorf("InstallerSet %s: waiting for installation", installerSets.Items[0].Name) + } else if ready.Status == corev1.ConditionFalse { + return fmt.Errorf("InstallerSet %s: ", ready.Message) + } + } + return nil +} + +func (r *Reconciler) deleteInstallerSet(ctx context.Context, labelSelector string) error { + + err := r.operatorClientSet.OperatorV1alpha1().TektonInstallerSets(). + DeleteCollection(ctx, metav1.DeleteOptions{}, metav1.ListOptions{ + LabelSelector: labelSelector, + }) + if err != nil && !errors.IsNotFound(err) { + return err + } + + return nil +} diff --git a/pkg/reconciler/openshift/tektonaddon/pipelinesascode.go b/pkg/reconciler/openshift/tektonaddon/pipelinesascode.go new file mode 100644 index 0000000000..9489a50a9d --- /dev/null +++ b/pkg/reconciler/openshift/tektonaddon/pipelinesascode.go @@ -0,0 +1,83 @@ +/* +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 tektonaddon + +import ( + "context" + "os" + "path/filepath" + + mf "github.com/manifestival/manifestival" + "github.com/tektoncd/operator/pkg/apis/operator/v1alpha1" + "github.com/tektoncd/operator/pkg/reconciler/common" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +var pacLS = metav1.LabelSelector{ + MatchLabels: map[string]string{ + v1alpha1.InstallerSetType: PACInstallerSet, + }, +} + +func (r *Reconciler) EnsurePipelinesAsCode(ctx context.Context, ta *v1alpha1.TektonAddon) error { + + pacLabelSelector, err := common.LabelSelector(pacLS) + if err != nil { + return err + } + + if *ta.Spec.EnablePAC { + + exist, err := checkIfInstallerSetExist(ctx, r.operatorClientSet, r.operatorVersion, pacLabelSelector) + if err != nil { + return err + } + if !exist { + return r.ensurePAC(ctx, ta) + } + + } else { + // if disabled then delete the installer Set if exist + if err := r.deleteInstallerSet(ctx, pacLabelSelector); err != nil { + return err + } + } + + return nil +} + +func (r *Reconciler) ensurePAC(ctx context.Context, ta *v1alpha1.TektonAddon) error { + pacManifest := mf.Manifest{} + + koDataDir := os.Getenv(common.KoEnvKey) + pacLocation := filepath.Join(koDataDir, "tekton-addon", "pipelines-as-code") + if err := common.AppendManifest(&pacManifest, pacLocation); err != nil { + return err + } + + // Run transformers + if err := r.addonTransform(ctx, &pacManifest, ta); err != nil { + return err + } + + if err := createInstallerSet(ctx, r.operatorClientSet, ta, pacManifest, r.operatorVersion, + PACInstallerSet, "addon-pac"); err != nil { + return err + } + + return nil +} diff --git a/pkg/reconciler/openshift/tektonaddon/pipelinetemplate.go b/pkg/reconciler/openshift/tektonaddon/pipelinetemplate.go new file mode 100644 index 0000000000..777b00e5dd --- /dev/null +++ b/pkg/reconciler/openshift/tektonaddon/pipelinetemplate.go @@ -0,0 +1,101 @@ +/* +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 tektonaddon + +import ( + "context" + "fmt" + "os" + "path/filepath" + + mf "github.com/manifestival/manifestival" + "github.com/tektoncd/operator/pkg/apis/operator/v1alpha1" + "github.com/tektoncd/operator/pkg/reconciler/common" + tektonaddon "github.com/tektoncd/operator/pkg/reconciler/openshift/tektonaddon/pipelinetemplates" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +var pipelineTemplateLS = metav1.LabelSelector{ + MatchLabels: map[string]string{ + v1alpha1.InstallerSetType: PipelinesTemplateInstallerSet, + }, +} + +func (r *Reconciler) EnsurePipelineTemplates(ctx context.Context, enable string, ta *v1alpha1.TektonAddon) error { + + pipelineTemplateLSLabelSelector, err := common.LabelSelector(pipelineTemplateLS) + if err != nil { + return err + } + if enable == "true" { + + exist, err := checkIfInstallerSetExist(ctx, r.operatorClientSet, r.operatorVersion, pipelineTemplateLSLabelSelector) + if err != nil { + return err + } + if !exist { + msg := fmt.Sprintf("%s being created/upgraded", PipelinesTemplateInstallerSet) + ta.Status.MarkInstallerSetNotReady(msg) + return r.ensurePipelineTemplates(ctx, ta) + } + + if err := r.checkComponentStatus(ctx, pipelineTemplateLSLabelSelector); err != nil { + ta.Status.MarkInstallerSetNotReady(err.Error()) + return nil + } + + } else { + // if disabled then delete the installer Set if exist + if err := r.deleteInstallerSet(ctx, pipelineTemplateLSLabelSelector); err != nil { + return err + } + } + + return nil +} + +func (r *Reconciler) ensurePipelineTemplates(ctx context.Context, ta *v1alpha1.TektonAddon) error { + pipelineTemplateManifest := mf.Manifest{} + + // Read pipeline template manifest from kodata + if err := applyAddons(&pipelineTemplateManifest, "03-pipelines"); err != nil { + return err + } + + // generate pipeline templates + if err := addPipelineTemplates(&pipelineTemplateManifest); err != nil { + return err + } + + // Run transformers + if err := r.addonTransform(ctx, &pipelineTemplateManifest, ta); err != nil { + return err + } + + if err := createInstallerSet(ctx, r.operatorClientSet, ta, pipelineTemplateManifest, r.operatorVersion, + PipelinesTemplateInstallerSet, "addon-pipelines"); err != nil { + return err + } + + return nil +} + +func addPipelineTemplates(manifest *mf.Manifest) error { + koDataDir := os.Getenv(common.KoEnvKey) + addonLocation := filepath.Join(koDataDir, "tekton-addon", "tekton-pipeline-template") + return tektonaddon.GeneratePipelineTemplates(addonLocation, manifest) +} diff --git a/pkg/reconciler/openshift/tektonaddon/tektonaddon.go b/pkg/reconciler/openshift/tektonaddon/tektonaddon.go index f6dca75d25..2afa508cba 100644 --- a/pkg/reconciler/openshift/tektonaddon/tektonaddon.go +++ b/pkg/reconciler/openshift/tektonaddon/tektonaddon.go @@ -21,7 +21,6 @@ import ( "fmt" "os" "path/filepath" - "strings" "time" mf "github.com/manifestival/manifestival" @@ -31,13 +30,8 @@ import ( tektonaddonreconciler "github.com/tektoncd/operator/pkg/client/injection/reconciler/operator/v1alpha1/tektonaddon" "github.com/tektoncd/operator/pkg/reconciler/common" "github.com/tektoncd/operator/pkg/reconciler/kubernetes/tektoninstallerset" - tektonaddon "github.com/tektoncd/operator/pkg/reconciler/openshift/tektonaddon/pipelinetemplates" - "github.com/tektoncd/operator/pkg/reconciler/shared/hash" - corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "knative.dev/pkg/apis" "knative.dev/pkg/logging" pkgreconciler "knative.dev/pkg/reconciler" ) @@ -70,17 +64,6 @@ const ( var _ tektonaddonreconciler.Interface = (*Reconciler)(nil) var _ tektonaddonreconciler.Finalizer = (*Reconciler)(nil) -var communityResourceURLs = []string{ - "https://raw.githubusercontent.com/tektoncd/catalog/master/task/jib-maven/0.4/jib-maven.yaml", - "https://raw.githubusercontent.com/tektoncd/catalog/master/task/helm-upgrade-from-source/0.3/helm-upgrade-from-source.yaml", - "https://raw.githubusercontent.com/tektoncd/catalog/master/task/helm-upgrade-from-repo/0.2/helm-upgrade-from-repo.yaml", - "https://raw.githubusercontent.com/tektoncd/catalog/master/task/trigger-jenkins-job/0.1/trigger-jenkins-job.yaml", - "https://raw.githubusercontent.com/tektoncd/catalog/master/task/git-cli/0.3/git-cli.yaml", - "https://raw.githubusercontent.com/tektoncd/catalog/master/task/pull-request/0.1/pull-request.yaml", - "https://raw.githubusercontent.com/tektoncd/catalog/master/task/kubeconfig-creator/0.1/kubeconfig-creator.yaml", - "https://raw.githubusercontent.com/tektoncd/catalog/main/task/argocd-task-sync-and-wait/0.1/argocd-task-sync-and-wait.yaml", -} - // FinalizeKind removes all resources after deletion of a TektonTriggers. func (r *Reconciler) FinalizeKind(ctx context.Context, original *v1alpha1.TektonAddon) pkgreconciler.Event { logger := logging.FromContext(ctx) @@ -177,172 +160,25 @@ func (r *Reconciler) ReconcileKind(ctx context.Context, ta *v1alpha1.TektonAddon ta.Status.MarkPreReconcilerComplete() - // If clusterTasks are enabled then create an InstallerSet - // with their manifest - clusterTaskLS := metav1.LabelSelector{ - MatchLabels: map[string]string{ - v1alpha1.InstallerSetType: ClusterTaskInstallerSet, - }, - } - clusterTaskLabelSelector, err := common.LabelSelector(clusterTaskLS) - if err != nil { + if err := r.EnsureClusterTask(ctx, ctVal, ta); err != nil { return err } - if ctVal == "true" { - - exist, err := checkIfInstallerSetExist(ctx, r.operatorClientSet, r.operatorVersion, clusterTaskLabelSelector) - if err != nil { - return err - } - - if !exist { - msg := fmt.Sprintf("%s being created/upgraded", ClusterTaskInstallerSet) - ta.Status.MarkInstallerSetNotReady(msg) - return r.ensureClusterTasks(ctx, ta) - } - - if err := r.checkComponentStatus(ctx, clusterTaskLabelSelector); err != nil { - ta.Status.MarkInstallerSetNotReady(err.Error()) - return nil - } - - } else { - // if disabled then delete the installer Set if exist - if err := r.deleteInstallerSet(ctx, clusterTaskLabelSelector); err != nil { - return err - } - } - - // If clusterTasks are enabled then create an InstallerSet - // with the versioned clustertask manifest - versionedClusterTaskLS := metav1.LabelSelector{ - MatchLabels: map[string]string{ - v1alpha1.InstallerSetType: VersionedClusterTaskInstallerSet, - v1alpha1.ReleaseMinorVersionKey: getPatchVersionTrimmed(r.operatorVersion), - }, - } - versionedClusterTaskLabelSelector, err := common.LabelSelector(versionedClusterTaskLS) - if err != nil { + if err := r.EnsureVersionedClusterTask(ctx, ctVal, ta); err != nil { return err } - if ctVal == "true" { - - // here pass two labels one for type and other for minor release version to remove the previous minor release installerset only not all - exist, err := checkIfInstallerSetExist(ctx, r.operatorClientSet, r.operatorVersion, versionedClusterTaskLabelSelector) - if err != nil { - return err - } - - if !exist { - msg := fmt.Sprintf("%s being created/upgraded", VersionedClusterTaskInstallerSet) - ta.Status.MarkInstallerSetNotReady(msg) - return r.ensureVersionedClusterTasks(ctx, ta) - } - } else { - // if disabled then delete the installer Set if exist - if err := r.deleteInstallerSet(ctx, versionedClusterTaskLabelSelector); err != nil { - return err - } - } - // here pass two labels one for type and other for operator release version to get the latest installerset of current version - vClusterTaskLS := metav1.LabelSelector{ - MatchLabels: map[string]string{ - v1alpha1.InstallerSetType: VersionedClusterTaskInstallerSet, - v1alpha1.ReleaseVersionKey: r.operatorVersion, - }, - } - vClusterTaskLabelSelector, err := common.LabelSelector(vClusterTaskLS) - if err != nil { + if err := r.EnsurePipelineTemplates(ctx, ptVal, ta); err != nil { return err } - if err := r.checkComponentStatus(ctx, vClusterTaskLabelSelector); err != nil { - ta.Status.MarkInstallerSetNotReady(err.Error()) - return nil - } - // If pipeline templates are enabled then create an InstallerSet - // with their manifest - pipelineTemplateLS := metav1.LabelSelector{ - MatchLabels: map[string]string{ - v1alpha1.InstallerSetType: PipelinesTemplateInstallerSet, - }, - } - pipelineTemplateLSLabelSelector, err := common.LabelSelector(pipelineTemplateLS) - if err != nil { + if err := r.EnsureTriggersResources(ctx, ta); err != nil { return err } - if ptVal == "true" { - exist, err := checkIfInstallerSetExist(ctx, r.operatorClientSet, r.operatorVersion, pipelineTemplateLSLabelSelector) - if err != nil { - return err - } - if !exist { - msg := fmt.Sprintf("%s being created/upgraded", PipelinesTemplateInstallerSet) - ta.Status.MarkInstallerSetNotReady(msg) - return r.ensurePipelineTemplates(ctx, ta) - } - - if err := r.checkComponentStatus(ctx, pipelineTemplateLSLabelSelector); err != nil { - ta.Status.MarkInstallerSetNotReady(err.Error()) - return nil - } - - } else { - // if disabled then delete the installer Set if exist - if err := r.deleteInstallerSet(ctx, pipelineTemplateLSLabelSelector); err != nil { - return err - } - } - - // Ensure Triggers resources - triggerResourceLS := metav1.LabelSelector{ - MatchLabels: map[string]string{ - v1alpha1.InstallerSetType: TriggersResourcesInstallerSet, - }, - } - triggerResourceLabelSelector, err := common.LabelSelector(triggerResourceLS) - if err != nil { + if err := r.EnsurePipelinesAsCode(ctx, ta); err != nil { return err } - exist, err := checkIfInstallerSetExist(ctx, r.operatorClientSet, r.operatorVersion, triggerResourceLabelSelector) - if err != nil { - return err - } - if !exist { - msg := fmt.Sprintf("%s being created/upgraded", TriggersResourcesInstallerSet) - ta.Status.MarkInstallerSetNotReady(msg) - return r.ensureTriggerResources(ctx, ta) - } - - err = r.checkComponentStatus(ctx, triggerResourceLabelSelector) - if err != nil { - ta.Status.MarkInstallerSetNotReady(err.Error()) - return nil - } - - // Check if PAC is enabled - - if *ta.Spec.EnablePAC { - - // make sure pac is installed - exist, err := checkIfInstallerSetExist(ctx, r.operatorClientSet, r.operatorVersion, - fmt.Sprintf("%s=%s", v1alpha1.InstallerSetType, PACInstallerSet)) - if err != nil { - return err - } - if !exist { - return r.ensurePAC(ctx, ta) - } - - } else { - // if disabled then delete the installer Set if exist - if err := r.deleteInstallerSet(ctx, fmt.Sprintf("%s=%s", v1alpha1.InstallerSetType, PACInstallerSet)); err != nil { - return err - } - } ta.Status.MarkInstallerSetReady() @@ -362,287 +198,12 @@ func (r *Reconciler) ReconcileKind(ctx context.Context, ta *v1alpha1.TektonAddon return nil } -func (r *Reconciler) checkComponentStatus(ctx context.Context, labelSelector string) error { - - // Check if installer set is already created - installerSets, err := r.operatorClientSet.OperatorV1alpha1().TektonInstallerSets(). - List(ctx, metav1.ListOptions{ - LabelSelector: labelSelector, - }) - - if err != nil { - if errors.IsNotFound(err) { - return nil - } - return err - } - - // To make sure there won't be duplicate installersets. - if len(installerSets.Items) == 1 { - ready := installerSets.Items[0].Status.GetCondition(apis.ConditionReady) - if ready == nil || ready.Status == corev1.ConditionUnknown { - return fmt.Errorf("InstallerSet %s: waiting for installation", installerSets.Items[0].Name) - } else if ready.Status == corev1.ConditionFalse { - return fmt.Errorf("InstallerSet %s: ", ready.Message) - } - } - return nil -} - -func (r *Reconciler) ensurePAC(ctx context.Context, ta *v1alpha1.TektonAddon) error { - pacManifest := mf.Manifest{} - - koDataDir := os.Getenv(common.KoEnvKey) - pacLocation := filepath.Join(koDataDir, "tekton-addon", "pipelines-as-code") - if err := common.AppendManifest(&pacManifest, pacLocation); err != nil { - return err - } - - // Run transformers - if err := r.addonTransform(ctx, &pacManifest, ta); err != nil { - return err - } - - if err := createInstallerSet(ctx, r.operatorClientSet, ta, pacManifest, r.operatorVersion, - PACInstallerSet, "addon-pac"); err != nil { - return err - } - - return nil -} - -func (r *Reconciler) ensureTriggerResources(ctx context.Context, ta *v1alpha1.TektonAddon) error { - triggerResourcesManifest := mf.Manifest{} - - if err := applyAddons(&triggerResourcesManifest, "01-clustertriggerbindings"); err != nil { - return err - } - // Run transformers - if err := r.addonTransform(ctx, &triggerResourcesManifest, ta); err != nil { - return err - } - - if err := createInstallerSet(ctx, r.operatorClientSet, ta, triggerResourcesManifest, r.operatorVersion, - TriggersResourcesInstallerSet, "addon-triggers"); err != nil { - return err - } - - return nil -} - -func (r *Reconciler) ensurePipelineTemplates(ctx context.Context, ta *v1alpha1.TektonAddon) error { - pipelineTemplateManifest := mf.Manifest{} - - // Read pipeline template manifest from kodata - if err := applyAddons(&pipelineTemplateManifest, "03-pipelines"); err != nil { - return err - } - - // generate pipeline templates - if err := addPipelineTemplates(&pipelineTemplateManifest); err != nil { - return err - } - - // Run transformers - if err := r.addonTransform(ctx, &pipelineTemplateManifest, ta); err != nil { - return err - } - - if err := createInstallerSet(ctx, r.operatorClientSet, ta, pipelineTemplateManifest, r.operatorVersion, - PipelinesTemplateInstallerSet, "addon-pipelines"); err != nil { - return err - } - - return nil -} - -// installerset for non versioned clustertask like buildah and community clustertask -func (r *Reconciler) ensureClusterTasks(ctx context.Context, ta *v1alpha1.TektonAddon) error { - clusterTaskManifest := mf.Manifest{} - // Read clusterTasks from ko data - if err := applyAddons(&clusterTaskManifest, "02-clustertasks"); err != nil { - return err - } - // Run transformers - if err := r.addonTransform(ctx, &clusterTaskManifest, ta); err != nil { - return err - } - - clusterTaskManifest = clusterTaskManifest.Filter( - mf.Not(byContains(getFormattedVersion(r.operatorVersion))), - ) - - communityClusterTaskManifest := r.manifest - if err := r.appendCommunityTarget(ctx, &communityClusterTaskManifest, ta); err != nil { - // Continue if failed to resolve community task URL. - // (Ex: on disconnected cluster community tasks won't be reachable because of proxy). - logging.FromContext(ctx).Error("Failed to get community task: Skipping community tasks installation ", err) - } else { - if err := r.communityTransform(ctx, &communityClusterTaskManifest, ta); err != nil { - return err - } - - clusterTaskManifest = clusterTaskManifest.Append(communityClusterTaskManifest) - } - - if err := createInstallerSet(ctx, r.operatorClientSet, ta, clusterTaskManifest, - r.operatorVersion, ClusterTaskInstallerSet, "addon-clustertasks"); err != nil { - return err - } - - return nil -} - -// installerset for versioned clustertask like buildah-1-6-0 -func (r *Reconciler) ensureVersionedClusterTasks(ctx context.Context, ta *v1alpha1.TektonAddon) error { - clusterTaskManifest := mf.Manifest{} - // Read clusterTasks from ko data - if err := applyAddons(&clusterTaskManifest, "02-clustertasks"); err != nil { - return err - } - // Run transformers - if err := r.addonTransform(ctx, &clusterTaskManifest, ta); err != nil { - return err - } - - clusterTaskManifest = clusterTaskManifest.Filter( - byContains(getFormattedVersion(r.operatorVersion)), - ) - - if err := createInstallerSet(ctx, r.operatorClientSet, ta, clusterTaskManifest, - r.operatorVersion, VersionedClusterTaskInstallerSet, "addon-versioned-clustertasks"); err != nil { - return err - } - - return nil -} - -// checkIfInstallerSetExist checks if installer set exists for a component and return true/false based on it -// and if installer set which already exist is of older version then it deletes and return false to create a new -// installer set -func checkIfInstallerSetExist(ctx context.Context, oc clientset.Interface, relVersion string, - labelSelector string) (bool, error) { - - installerSets, err := oc.OperatorV1alpha1().TektonInstallerSets(). - List(ctx, metav1.ListOptions{ - LabelSelector: labelSelector, - }) - if err != nil { - return false, err - } - - if len(installerSets.Items) == 0 { - return false, nil - } - - if len(installerSets.Items) == 1 { - // if already created then check which version it is - version, ok := installerSets.Items[0].Labels[v1alpha1.ReleaseVersionKey] - if ok && version == relVersion { - // if installer set already exist and release version is same - // then ignore and move on - return true, nil - } - } - - // release version doesn't exist or is different from expected - // deleted existing InstallerSet and create a new one - // or there is more than one installerset (unexpected) - if err = oc.OperatorV1alpha1().TektonInstallerSets(). - DeleteCollection(ctx, metav1.DeleteOptions{}, metav1.ListOptions{ - LabelSelector: labelSelector, - }); err != nil { - return false, err - } - - return false, v1alpha1.RECONCILE_AGAIN_ERR -} - -func createInstallerSet(ctx context.Context, oc clientset.Interface, ta *v1alpha1.TektonAddon, - manifest mf.Manifest, releaseVersion, component, installerSetPrefix string) error { - - specHash, err := hash.Compute(ta.Spec) - if err != nil { - return err - } - - is := makeInstallerSet(ta, manifest, installerSetPrefix, releaseVersion, component, specHash) - - if _, err := oc.OperatorV1alpha1().TektonInstallerSets(). - Create(ctx, is, metav1.CreateOptions{}); err != nil { - return err - } - - return v1alpha1.RECONCILE_AGAIN_ERR -} - -func makeInstallerSet(ta *v1alpha1.TektonAddon, manifest mf.Manifest, prefix, releaseVersion, component, specHash string) *v1alpha1.TektonInstallerSet { - ownerRef := *metav1.NewControllerRef(ta, ta.GetGroupVersionKind()) - labels := map[string]string{ - v1alpha1.CreatedByKey: CreatedByValue, - v1alpha1.InstallerSetType: component, - v1alpha1.ReleaseVersionKey: releaseVersion, - } - namePrefix := fmt.Sprintf("%s-", prefix) - // special label to make sure no two versioned clustertask installerset exist - // for all patch releases - if component == VersionedClusterTaskInstallerSet { - labels[v1alpha1.ReleaseMinorVersionKey] = getPatchVersionTrimmed(releaseVersion) - namePrefix = fmt.Sprintf("%s%s-", namePrefix, getFormattedVersion(releaseVersion)) - } - return &v1alpha1.TektonInstallerSet{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: namePrefix, - Labels: labels, - Annotations: map[string]string{ - v1alpha1.TargetNamespaceKey: ta.Spec.TargetNamespace, - v1alpha1.LastAppliedHashKey: specHash, - }, - OwnerReferences: []metav1.OwnerReference{ownerRef}, - }, - Spec: v1alpha1.TektonInstallerSetSpec{ - Manifests: manifest.Resources(), - }, - } -} - -func (r *Reconciler) deleteInstallerSet(ctx context.Context, labelSelector string) error { - - err := r.operatorClientSet.OperatorV1alpha1().TektonInstallerSets(). - DeleteCollection(ctx, metav1.DeleteOptions{}, metav1.ListOptions{ - LabelSelector: labelSelector, - }) - if err != nil && !errors.IsNotFound(err) { - return err - } - - return nil -} - -func addPipelineTemplates(manifest *mf.Manifest) error { - koDataDir := os.Getenv(common.KoEnvKey) - addonLocation := filepath.Join(koDataDir, "tekton-addon", "tekton-pipeline-template") - return tektonaddon.GeneratePipelineTemplates(addonLocation, manifest) -} - func applyAddons(manifest *mf.Manifest, subpath string) error { koDataDir := os.Getenv(common.KoEnvKey) addonLocation := filepath.Join(koDataDir, "tekton-addon", "addons", subpath) return common.AppendManifest(manifest, addonLocation) } -// appendCommunityTarget mutates the passed manifest by appending one -// appropriate for the passed TektonComponent -func (r *Reconciler) appendCommunityTarget(ctx context.Context, manifest *mf.Manifest, comp v1alpha1.TektonComponent) error { - urls := strings.Join(communityResourceURLs, ",") - m, err := mf.ManifestFrom(mf.Path(urls)) - if err != nil { - return err - } - *manifest = manifest.Append(m) - return nil -} - // addonTransform mutates the passed manifest to one with common, component // and platform transformations applied func (r *Reconciler) addonTransform(ctx context.Context, manifest *mf.Manifest, comp v1alpha1.TektonComponent) error { @@ -654,18 +215,6 @@ func (r *Reconciler) addonTransform(ctx context.Context, manifest *mf.Manifest, return common.Transform(ctx, manifest, instance, extra...) } -// communityTransform mutates the passed manifest to one with common component -// and platform transformations applied -func (r *Reconciler) communityTransform(ctx context.Context, manifest *mf.Manifest, comp v1alpha1.TektonComponent) error { - instance := comp.(*v1alpha1.TektonAddon) - extra := []mf.Transformer{ - replaceKind("Task", "ClusterTask"), - injectLabel(labelProviderType, providerTypeCommunity, overwrite, "ClusterTask"), - } - extra = append(extra, r.extension.Transformers(instance)...) - return common.Transform(ctx, manifest, instance, extra...) -} - func findValue(params []v1alpha1.Param, name string) (string, bool) { for _, p := range params { if p.Name == name { @@ -675,28 +224,6 @@ func findValue(params []v1alpha1.Param, name string) (string, bool) { return "", false } -// byContains returns resources with specific string in name -func byContains(name string) mf.Predicate { - return func(u *unstructured.Unstructured) bool { - return strings.Contains(u.GetName(), name) - } -} - -// To get the version in the format as in clustertask name i.e. 1-6 -func getFormattedVersion(version string) string { - version = strings.TrimPrefix(getPatchVersionTrimmed(version), "v") - return strings.Replace(version, ".", "-", -1) -} - -// To get the minor major version for label i.e. v1.6 -func getPatchVersionTrimmed(version string) string { - endIndex := strings.LastIndex(version, ".") - if endIndex != -1 { - version = version[:endIndex] - } - return version -} - func (r *Reconciler) markUpgrade(ctx context.Context, ta *v1alpha1.TektonAddon) error { labels := ta.GetLabels() ver, ok := labels[v1alpha1.ReleaseVersionKey] diff --git a/pkg/reconciler/openshift/tektonaddon/triggers.go b/pkg/reconciler/openshift/tektonaddon/triggers.go new file mode 100644 index 0000000000..9503926cde --- /dev/null +++ b/pkg/reconciler/openshift/tektonaddon/triggers.go @@ -0,0 +1,77 @@ +/* +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 tektonaddon + +import ( + "context" + "fmt" + + mf "github.com/manifestival/manifestival" + "github.com/tektoncd/operator/pkg/apis/operator/v1alpha1" + "github.com/tektoncd/operator/pkg/reconciler/common" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +var triggerResourceLS = metav1.LabelSelector{ + MatchLabels: map[string]string{ + v1alpha1.InstallerSetType: TriggersResourcesInstallerSet, + }, +} + +func (r *Reconciler) EnsureTriggersResources(ctx context.Context, ta *v1alpha1.TektonAddon) error { + + triggerResourceLabelSelector, err := common.LabelSelector(triggerResourceLS) + if err != nil { + return err + } + exist, err := checkIfInstallerSetExist(ctx, r.operatorClientSet, r.operatorVersion, triggerResourceLabelSelector) + if err != nil { + return err + } + if !exist { + msg := fmt.Sprintf("%s being created/upgraded", TriggersResourcesInstallerSet) + ta.Status.MarkInstallerSetNotReady(msg) + return r.ensureTriggerResources(ctx, ta) + } + + err = r.checkComponentStatus(ctx, triggerResourceLabelSelector) + if err != nil { + ta.Status.MarkInstallerSetNotReady(err.Error()) + return nil + } + + return nil +} + +func (r *Reconciler) ensureTriggerResources(ctx context.Context, ta *v1alpha1.TektonAddon) error { + triggerResourcesManifest := mf.Manifest{} + + if err := applyAddons(&triggerResourcesManifest, "01-clustertriggerbindings"); err != nil { + return err + } + // Run transformers + if err := r.addonTransform(ctx, &triggerResourcesManifest, ta); err != nil { + return err + } + + if err := createInstallerSet(ctx, r.operatorClientSet, ta, triggerResourcesManifest, r.operatorVersion, + TriggersResourcesInstallerSet, "addon-triggers"); err != nil { + return err + } + + return nil +}