From 2cd400eb2d8474a5cb34c29c6c0d5e07751cb981 Mon Sep 17 00:00:00 2001 From: savitaashture Date: Mon, 20 Sep 2021 11:01:36 +0530 Subject: [PATCH] Option to disable the default creation of RBAC resources --- .../all/operator_v1alpha1_config_cr.yaml | 3 + pkg/apis/operator/v1alpha1/common.go | 4 +- .../operator/v1alpha1/tektonconfig_types.go | 7 + .../v1alpha1/zz_generated.deepcopy.go | 12 ++ .../openshift/tektonconfig/extension.go | 147 +++++++++++++++++- pkg/reconciler/openshift/tektonconfig/rbac.go | 34 +++- .../golang-lru/simplelru/lru_interface.go | 4 +- 7 files changed, 199 insertions(+), 12 deletions(-) diff --git a/config/crs/openshift/config/all/operator_v1alpha1_config_cr.yaml b/config/crs/openshift/config/all/operator_v1alpha1_config_cr.yaml index 38432513c6..cfa439a020 100644 --- a/config/crs/openshift/config/all/operator_v1alpha1_config_cr.yaml +++ b/config/crs/openshift/config/all/operator_v1alpha1_config_cr.yaml @@ -25,3 +25,6 @@ spec: value: "true" - name: pipelineTemplates value: "true" + params: + - name: createRbacResource + value: "true" diff --git a/pkg/apis/operator/v1alpha1/common.go b/pkg/apis/operator/v1alpha1/common.go index 0ed757868b..a17fb7312c 100644 --- a/pkg/apis/operator/v1alpha1/common.go +++ b/pkg/apis/operator/v1alpha1/common.go @@ -94,8 +94,8 @@ func (c *CommonSpec) GetTargetNamespace() string { // Param declares an string value to use for the parameter called name. type Param struct { - Name string `json:"name"` - Value string `json:"value"` + Name string `json:"name,omitempty"` + Value string `json:"value,omitempty"` } // ParamValue defines a default value and possible values for a param diff --git a/pkg/apis/operator/v1alpha1/tektonconfig_types.go b/pkg/apis/operator/v1alpha1/tektonconfig_types.go index 4e47d4ef81..10a9e206fc 100644 --- a/pkg/apis/operator/v1alpha1/tektonconfig_types.go +++ b/pkg/apis/operator/v1alpha1/tektonconfig_types.go @@ -84,6 +84,9 @@ type TektonConfigSpec struct { // Dashboard holds the customizable options for dashboards component // +optional Dashboard Dashboard `json:"dashboard,omitempty"` + // Params is the list of params passed for all platforms + // +optional + Params []Param `json:"params,omitempty"` } // TektonConfigStatus defines the observed state of TektonConfig @@ -97,6 +100,10 @@ type TektonConfigStatus struct { // The version of the installed release // +optional Version string `json:"version,omitempty"` + + // The current installer set name + // +optional + TektonInstallerSet map[string]string `json:"tektonInstallerSets,omitempty"` } // TektonConfigList contains a list of TektonConfig diff --git a/pkg/apis/operator/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/operator/v1alpha1/zz_generated.deepcopy.go index d059aa1dea..c153665edd 100644 --- a/pkg/apis/operator/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/operator/v1alpha1/zz_generated.deepcopy.go @@ -505,6 +505,11 @@ func (in *TektonConfigSpec) DeepCopyInto(out *TektonConfigSpec) { in.Pipeline.DeepCopyInto(&out.Pipeline) out.Trigger = in.Trigger out.Dashboard = in.Dashboard + if in.Params != nil { + in, out := &in.Params, &out.Params + *out = make([]Param, len(*in)) + copy(*out, *in) + } return } @@ -522,6 +527,13 @@ func (in *TektonConfigSpec) DeepCopy() *TektonConfigSpec { func (in *TektonConfigStatus) DeepCopyInto(out *TektonConfigStatus) { *out = *in in.Status.DeepCopyInto(&out.Status) + if in.TektonInstallerSet != nil { + in, out := &in.TektonInstallerSet, &out.TektonInstallerSet + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } return } diff --git a/pkg/reconciler/openshift/tektonconfig/extension.go b/pkg/reconciler/openshift/tektonconfig/extension.go index 7cf46571f1..cd971813ec 100644 --- a/pkg/reconciler/openshift/tektonconfig/extension.go +++ b/pkg/reconciler/openshift/tektonconfig/extension.go @@ -28,6 +28,7 @@ import ( "github.com/tektoncd/operator/pkg/reconciler/openshift/tektonconfig/extension" openshiftPipeline "github.com/tektoncd/operator/pkg/reconciler/openshift/tektonpipeline" openshiftTrigger "github.com/tektoncd/operator/pkg/reconciler/openshift/tektontrigger" + "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" @@ -67,10 +68,37 @@ func (oe openshiftExtension) PreReconcile(ctx context.Context, tc v1alpha1.Tekto r := rbac{ kubeClientSet: oe.kubeClientSet, operatorClientSet: oe.operatorClientSet, - ownerRef: configOwnerRef(tc), version: os.Getenv(versionKey), + tektonConfig: config, } - return r.createResources(ctx) + + var createResourceError error + if len(config.Spec.Params) == 0 { + createResourceError = r.createResources(ctx) + } + for _, v := range config.Spec.Params { + // check for param name and if its matches to createRbacResource + // then disable auto creation of RBAC resources by deleting installerSet + if v.Name == "createRbacResource" { + if v.Value == "false" { + if err := r.deleteInstallerSet(ctx, r.tektonConfig, componentName); err != nil { + return err + } + // remove openshift-pipelines.tekton.dev/namespace-reconcile-version label from namespaces while deleting RBAC resources. + if err := r.cleanUp(ctx); err != nil { + return err + } + + } else { + createResourceError = r.createResources(ctx) + } + } else { + createResourceError = r.createResources(ctx) + } + continue + } + + return createResourceError } func (oe openshiftExtension) PostReconcile(ctx context.Context, comp v1alpha1.TektonComponent) error { configInstance := comp.(*v1alpha1.TektonConfig) @@ -103,6 +131,117 @@ func (oe openshiftExtension) Finalize(ctx context.Context, comp v1alpha1.TektonC } // configOwnerRef returns owner reference pointing to passed instance -func configOwnerRef(tc v1alpha1.TektonComponent) metav1.OwnerReference { - return *metav1.NewControllerRef(tc, tc.GroupVersionKind()) +func configOwnerRef(tc v1alpha1.TektonInstallerSet) metav1.OwnerReference { + return *metav1.NewControllerRef(&tc, tc.GetGroupVersionKind()) +} + +func createInstallerSet(ctx context.Context, oc versioned.Interface, ta *v1alpha1.TektonConfig, + releaseVersion, component, installerSetName string) error { + + is := makeInstallerSet(ta, installerSetName, releaseVersion) + + createdIs, err := oc.OperatorV1alpha1().TektonInstallerSets(). + Create(ctx, is, metav1.CreateOptions{}) + if err != nil && !errors.IsAlreadyExists(err) { + return err + } + + if len(ta.Status.TektonInstallerSet) == 0 { + ta.Status.TektonInstallerSet = map[string]string{} + } + + // Update the status of addon with created installerSet name + ta.Status.TektonInstallerSet[component] = createdIs.Name + ta.Status.SetVersion(releaseVersion) + + _, err = oc.OperatorV1alpha1().TektonConfigs(). + UpdateStatus(ctx, ta, metav1.UpdateOptions{}) + + return err +} + +func makeInstallerSet(tc *v1alpha1.TektonConfig, name, releaseVersion string) *v1alpha1.TektonInstallerSet { + ownerRef := *metav1.NewControllerRef(tc, tc.GetGroupVersionKind()) + return &v1alpha1.TektonInstallerSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Labels: map[string]string{ + createdByKey: createdByValue, + }, + Annotations: map[string]string{ + releaseVersionKey: releaseVersion, + targetNamespaceKey: tc.Spec.TargetNamespace, + }, + OwnerReferences: []metav1.OwnerReference{ownerRef}, + }, + } +} + +func (r *rbac) deleteInstallerSet(ctx context.Context, tc *v1alpha1.TektonConfig, component string) error { + + compInstallerSet, ok := tc.Status.TektonInstallerSet[component] + if !ok { + return nil + } + + if compInstallerSet != "" { + // delete the installer set + err := r.operatorClientSet.OperatorV1alpha1().TektonInstallerSets(). + Delete(ctx, tc.Status.TektonInstallerSet[component], metav1.DeleteOptions{}) + if err != nil && !errors.IsNotFound(err) { + return err + } + + // clear the name of installer set from TektonAddon status + delete(tc.Status.TektonInstallerSet, component) + _, err = r.operatorClientSet.OperatorV1alpha1().TektonConfigs(). + UpdateStatus(ctx, tc, metav1.UpdateOptions{}) + if err != nil && !errors.IsNotFound(err) { + 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 versioned.Interface, relVersion string, + tc *v1alpha1.TektonConfig, component string) (bool, error) { + + // Check if installer set is already created + compInstallerSet, ok := tc.Status.TektonInstallerSet[component] + if !ok { + return false, nil + } + + if compInstallerSet != "" { + // if already created then check which version it is + ctIs, err := oc.OperatorV1alpha1().TektonInstallerSets(). + Get(ctx, compInstallerSet, metav1.GetOptions{}) + if err != nil { + if errors.IsNotFound(err) { + return false, nil + } + return false, err + } + + if version, ok := ctIs.Annotations[releaseVersionKey]; 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 + + err = oc.OperatorV1alpha1().TektonInstallerSets(). + Delete(ctx, compInstallerSet, metav1.DeleteOptions{}) + if err != nil { + return false, err + } + } + + return false, nil } diff --git a/pkg/reconciler/openshift/tektonconfig/rbac.go b/pkg/reconciler/openshift/tektonconfig/rbac.go index 7b1816930f..bdaf09aa18 100644 --- a/pkg/reconciler/openshift/tektonconfig/rbac.go +++ b/pkg/reconciler/openshift/tektonconfig/rbac.go @@ -21,6 +21,7 @@ import ( "fmt" "regexp" + "github.com/tektoncd/operator/pkg/apis/operator/v1alpha1" clientset "github.com/tektoncd/operator/pkg/client/clientset/versioned" "github.com/tektoncd/operator/pkg/reconciler/common" corev1 "k8s.io/api/core/v1" @@ -41,6 +42,11 @@ const ( trustedCABundleConfigMap = "config-trusted-cabundle" clusterInterceptors = "openshift-pipelines-clusterinterceptors" namespaceVersionLabel = "openshift-pipelines.tekton.dev/namespace-reconcile-version" + createdByKey = "operator.tekton.dev/created-by" + createdByValue = "RBAC" + releaseVersionKey = "operator.tekton.dev/release-version" + targetNamespaceKey = "operator.tekton.dev/target-namespace" + componentName = "rbacTaskInstallerSet" ) // Namespace Regex to ignore the namespace for creating rbac resources. @@ -51,12 +57,13 @@ type rbac struct { operatorClientSet clientset.Interface ownerRef metav1.OwnerReference version string + tektonConfig *v1alpha1.TektonConfig } func (r *rbac) cleanUp(ctx context.Context) error { // fetch the list of all namespaces which have label - // `openshift-pipelines.tekton.dev/namespace-ready: ` + // `openshift-pipelines.tekton.dev/namespace-reconcile-version: ` namespaces, err := r.kubeClientSet.CoreV1().Namespaces().List(ctx, metav1.ListOptions{ LabelSelector: fmt.Sprintf("%s = %s", namespaceVersionLabel, r.version), }) @@ -80,7 +87,7 @@ func (r *rbac) createResources(ctx context.Context) error { logger := logging.FromContext(ctx) // fetch the list of all namespaces which doesn't have label - // `openshift-pipelines.tekton.dev/namespace-ready: ` + // `openshift-pipelines.tekton.dev/namespace-reconcile-version: ` namespaces, err := r.kubeClientSet.CoreV1().Namespaces().List(ctx, metav1.ListOptions{ LabelSelector: fmt.Sprintf("%s != %s", namespaceVersionLabel, r.version), }) @@ -104,6 +111,25 @@ func (r *rbac) createResources(ctx context.Context) error { return nil } + exist, err := checkIfInstallerSetExist(ctx, r.operatorClientSet, r.version, r.tektonConfig, componentName) + if err != nil { + return err + } + if !exist { + if err := createInstallerSet(ctx, r.operatorClientSet, r.tektonConfig, + r.version, componentName, "rbac-resources"); err != nil { + return err + } + } + + getdIs, err := r.operatorClientSet.OperatorV1alpha1().TektonInstallerSets(). + Get(ctx, "rbac-resources", metav1.GetOptions{}) + if err != nil { + return err + } + + r.ownerRef = configOwnerRef(*getdIs) + // Maintaining a separate cluster role for the scc declaration. // to assist us in managing this the scc association in a // granular way. @@ -136,7 +162,7 @@ func (r *rbac) createResources(ctx context.Context) error { return err } - // Add `openshift-pipelines.tekton.dev/namespace-ready` label to namespace + // Add `openshift-pipelines.tekton.dev/namespace-reconcile-version` label to namespace // so that rbac won't loop on it again nsLabels := n.GetLabels() if len(nsLabels) == 0 { @@ -384,7 +410,7 @@ func (r *rbac) updateRoleBinding(ctx context.Context, rb *rbacv1.RoleBinding, sa rb.SetOwnerReferences(ownerRef) if hasSubject && (len(ownerRef) != 0) { - logger.Info("rolebinding is up to date", "action", "none") + logger.Info("rolebinding is up to date ", "action ", "none") return nil } diff --git a/third_party/github.com/hashicorp/golang-lru/simplelru/lru_interface.go b/third_party/github.com/hashicorp/golang-lru/simplelru/lru_interface.go index a0b97e3f77..92d70934d6 100644 --- a/third_party/github.com/hashicorp/golang-lru/simplelru/lru_interface.go +++ b/third_party/github.com/hashicorp/golang-lru/simplelru/lru_interface.go @@ -34,6 +34,6 @@ type LRUCache interface { // Clears all cache entries. Purge() - // Resizes cache, returning number evicted - Resize(int) int + // Resizes cache, returning number evicted + Resize(int) int }