diff --git a/pkg/apis/operator/v1alpha1/common.go b/pkg/apis/operator/v1alpha1/common.go index b774381dda..5388b5a6df 100644 --- a/pkg/apis/operator/v1alpha1/common.go +++ b/pkg/apis/operator/v1alpha1/common.go @@ -97,8 +97,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 ba7dac5db5..99127e14cf 100644 --- a/pkg/apis/operator/v1alpha1/tektonconfig_types.go +++ b/pkg/apis/operator/v1alpha1/tektonconfig_types.go @@ -89,6 +89,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 diff --git a/pkg/apis/operator/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/operator/v1alpha1/zz_generated.deepcopy.go index e7e8d54687..1936b95633 100644 --- a/pkg/apis/operator/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/operator/v1alpha1/zz_generated.deepcopy.go @@ -470,6 +470,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 } diff --git a/pkg/reconciler/openshift/tektonconfig/extension.go b/pkg/reconciler/openshift/tektonconfig/extension.go index d6491c3b20..960099de8a 100644 --- a/pkg/reconciler/openshift/tektonconfig/extension.go +++ b/pkg/reconciler/openshift/tektonconfig/extension.go @@ -86,11 +86,12 @@ func (oe openshiftExtension) PreReconcile(ctx context.Context, tc v1alpha1.Tekto } r := rbac{ - kubeClientSet: oe.kubeClientSet, - operatorClientSet: oe.operatorClientSet, - manifest: oe.manifest, - ownerRef: configOwnerRef(tc), - version: os.Getenv(versionKey), + kubeClientSet: oe.kubeClientSet, + operatorClientSet: oe.operatorClientSet, + manifest: oe.manifest, + ownerRef: configOwnerRef(tc), + version: os.Getenv(versionKey), + disableRbacResourcesForOpenshift: config.Spec.Params, } return r.createResources(ctx) } diff --git a/pkg/reconciler/openshift/tektonconfig/rbac.go b/pkg/reconciler/openshift/tektonconfig/rbac.go index 1d36831e8c..d7c5dc6afb 100644 --- a/pkg/reconciler/openshift/tektonconfig/rbac.go +++ b/pkg/reconciler/openshift/tektonconfig/rbac.go @@ -22,6 +22,7 @@ import ( "regexp" 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/common" corev1 "k8s.io/api/core/v1" @@ -48,17 +49,18 @@ const ( var nsRegex = regexp.MustCompile(common.NamespaceIgnorePattern) type rbac struct { - kubeClientSet kubernetes.Interface - operatorClientSet clientset.Interface - manifest mf.Manifest - ownerRef metav1.OwnerReference - version string + kubeClientSet kubernetes.Interface + operatorClientSet clientset.Interface + manifest mf.Manifest + ownerRef metav1.OwnerReference + version string + disableRbacResourcesForOpenshift []v1alpha1.Param } 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), }) @@ -81,25 +83,71 @@ 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: ` - namespaces, err := r.kubeClientSet.CoreV1().Namespaces().List(ctx, metav1.ListOptions{ - LabelSelector: fmt.Sprintf("%s != %s", namespaceVersionLabel, r.version), - }) + // list of namespaces rbac resources need to be created + var rbacNamespaces []corev1.Namespace + // list of namespaces rbac resources need not to be created + var nonRbacNamespaces []corev1.Namespace + + // list all namespaces + allNamespaces, err := r.kubeClientSet.CoreV1().Namespaces().List(ctx, metav1.ListOptions{}) if err != nil { return err } - // list of namespaces rbac resources need to be created - var rbacNamespaces []corev1.Namespace - - // filter namespaces: - // ignore ns with name passing regex `^(openshift|kube)-` - for _, n := range namespaces.Items { + for _, n := range allNamespaces.Items { if ignore := nsRegex.MatchString(n.GetName()); ignore { continue } - rbacNamespaces = append(rbacNamespaces, n) + if len(r.disableRbacResourcesForOpenshift) == 0 { + rbacNamespaces = append(rbacNamespaces, n) + } + + for _, v := range r.disableRbacResourcesForOpenshift { + // check for param name and if its matches to createRbacResource then disable auto creation of RBAC resources + if v.Name == "createRbacResource" { + if v.Value == "false" { + nonRbacNamespaces = append(nonRbacNamespaces, n) + } else { + rbacNamespaces = append(rbacNamespaces, n) + } + continue + } else { + rbacNamespaces = append(rbacNamespaces, n) + } + } + } + + for _, n := range nonRbacNamespaces { + + logger.Infow("Remove CA bundle configmap in ", "Namespace if any", n.GetName()) + if err := r.deleteCABundles(ctx, &n); err != nil { + return err + } + + logger.Infow("Remove Default SA in ", "Namespace", n.GetName()) + if err := r.removeSA(ctx, &n); err != nil { + return err + } + + if err := r.removePipelinesSCCRoleBinding(ctx, &n); err != nil { + return err + } + + if err := r.removeRoleBindings(ctx, &n); err != nil { + return err + } + + // 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 { + nsLabels = map[string]string{} + } + nsLabels[namespaceVersionLabel] = r.version + n.SetLabels(nsLabels) + if _, err := r.kubeClientSet.CoreV1().Namespaces().Update(ctx, &n, metav1.UpdateOptions{}); err != nil { + return err + } } if len(rbacNamespaces) == 0 { @@ -138,7 +186,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 { @@ -154,6 +202,37 @@ func (r *rbac) createResources(ctx context.Context) error { return nil } +func (r *rbac) deleteCABundles(ctx context.Context, ns *corev1.Namespace) error { + logger := logging.FromContext(ctx) + cfgInterface := r.kubeClientSet.CoreV1().ConfigMaps(ns.Name) + + // Delete trusted CA bundle + logger.Infof("finding configmap: %s/%s", ns.Name, trustedCABundleConfigMap) + if _, err := cfgInterface.Get(ctx, trustedCABundleConfigMap, metav1.GetOptions{}); err != nil { + if errors.IsNotFound(err) { + logger.Infof("configmap %s in %s namespace does not exist", trustedCABundleConfigMap, ns.Name) + return nil + } else { + return err + } + } + if err := cfgInterface.Delete(ctx, trustedCABundleConfigMap, metav1.DeleteOptions{}); err != nil { + return err + } + + // Delete service CA bundle + logger.Infof("finding configmap: %s/%s", ns.Name, serviceCABundleConfigMap) + if _, err := cfgInterface.Get(ctx, serviceCABundleConfigMap, metav1.GetOptions{}); err != nil { + if errors.IsNotFound(err) { + logger.Infof("configmap %s in %s namespace does not exist", serviceCABundleConfigMap, ns.Name) + return nil + } else { + return err + } + } + return cfgInterface.Delete(ctx, serviceCABundleConfigMap, metav1.DeleteOptions{}) +} + func (r *rbac) ensureCABundles(ctx context.Context, ns *corev1.Namespace) error { logger := logging.FromContext(ctx) cfgInterface := r.kubeClientSet.CoreV1().ConfigMaps(ns.Name) @@ -245,6 +324,28 @@ func createServiceCABundleConfigMap(ctx context.Context, cfgInterface v1.ConfigM return nil } +func (r *rbac) removeSA(ctx context.Context, ns *corev1.Namespace) error { + logger := logging.FromContext(ctx) + logger.Infof("finding sa: %s/%s", ns.Name, "pipeline") + saInterface := r.kubeClientSet.CoreV1().ServiceAccounts(ns.Name) + + serviceAccount, err := saInterface.Get(ctx, pipelineSA, metav1.GetOptions{}) + if err != nil { + if errors.IsNotFound(err) { + logger.Infof("serviceaccount %s in %s namespace does not exist", pipelineSA, ns.Name) + return nil + } else { + return err + } + } + + if err := r.removeSAFromClusterRoleBindings(ctx, serviceAccount); err != nil { + return err + } + + return saInterface.Delete(ctx, pipelineSA, metav1.DeleteOptions{}) +} + func (r *rbac) ensureSA(ctx context.Context, ns *corev1.Namespace) (*corev1.ServiceAccount, error) { logger := logging.FromContext(ctx) logger.Infof("finding sa: %s/%s", ns.Name, "pipeline") @@ -325,6 +426,24 @@ func (r *rbac) ensurePipelinesSCClusterRole(ctx context.Context) error { return err } +func (r *rbac) removePipelinesSCCRoleBinding(ctx context.Context, ns *corev1.Namespace) error { + logger := logging.FromContext(ctx) + rbacClient := r.kubeClientSet.RbacV1() + + logger.Info("finding role-binding", pipelinesSCCRoleBinding) + if _, rbErr := rbacClient.RoleBindings(ns.Name).Get(ctx, pipelinesSCCRoleBinding, metav1.GetOptions{}); rbErr != nil { + if errors.IsNotFound(rbErr) { + logger.Infof("rolebinding %s in %s namespace does not exist", pipelinesSCCRoleBinding, ns.Name) + return nil + } else { + logger.Error(rbErr, "rbac get error", pipelinesSCCRoleBinding) + return rbErr + } + } + + return rbacClient.RoleBindings(ns.Name).Delete(ctx, pipelinesSCCRoleBinding, metav1.DeleteOptions{}) +} + func (r *rbac) ensurePipelinesSCCRoleBinding(ctx context.Context, sa *corev1.ServiceAccount) error { logger := logging.FromContext(ctx) rbacClient := r.kubeClientSet.RbacV1() @@ -386,7 +505,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 } @@ -411,6 +530,25 @@ func hasSubject(subjects []rbacv1.Subject, x rbacv1.Subject) bool { return false } +func (r *rbac) removeRoleBindings(ctx context.Context, ns *corev1.Namespace) error { + logger := logging.FromContext(ctx) + + logger.Info("finding role-binding edit") + rbacClient := r.kubeClientSet.RbacV1() + + if _, err := rbacClient.RoleBindings(ns.Name).Get(ctx, "edit", metav1.GetOptions{}); err != nil { + if errors.IsNotFound(err) { + logger.Infof("rolebinding edit in %s namespace does not exist", ns.Name) + return nil + } else { + logger.Error(err, "rbac get error") + return err + } + } + + return rbacClient.RoleBindings(ns.Name).Delete(ctx, "edit", metav1.DeleteOptions{}) +} + func (r *rbac) ensureRoleBindings(ctx context.Context, sa *corev1.ServiceAccount) error { logger := logging.FromContext(ctx) @@ -460,6 +598,23 @@ func (r *rbac) createRoleBinding(ctx context.Context, sa *corev1.ServiceAccount) return nil } +func (r *rbac) removeSAFromClusterRoleBindings(ctx context.Context, sa *corev1.ServiceAccount) error { + logger := logging.FromContext(ctx) + + rbacClient := r.kubeClientSet.RbacV1() + + logger.Info("finding cluster-role-binding ", clusterInterceptors) + + viewCRB, err := rbacClient.ClusterRoleBindings().Get(ctx, clusterInterceptors, metav1.GetOptions{}) + + if err == nil { + logger.Infof("found clusterrolebinding %s", viewCRB.Name) + return r.updateClusterRoleBindingToRemoveSubjects(ctx, viewCRB, sa) + } + + return err +} + func (r *rbac) ensureClusterRoleBindings(ctx context.Context, sa *corev1.ServiceAccount) error { logger := logging.FromContext(ctx) @@ -485,6 +640,32 @@ func (r *rbac) ensureClusterRoleBindings(ctx context.Context, sa *corev1.Service return err } +func (r *rbac) updateClusterRoleBindingToRemoveSubjects(ctx context.Context, rb *rbacv1.ClusterRoleBinding, sa *corev1.ServiceAccount) error { + logger := logging.FromContext(ctx) + + subject := rbacv1.Subject{Kind: rbacv1.ServiceAccountKind, Name: sa.Name, Namespace: sa.Namespace} + + hasSubject := hasSubject(rb.Subjects, subject) + if hasSubject { + for i, v := range rb.Subjects { + if v == subject { + rb.Subjects = append(rb.Subjects[:i], rb.Subjects[i+1:]...) + break + } + } + } + + logger.Info("update existing clusterrolebinding ", clusterInterceptors) + rbacClient := r.kubeClientSet.RbacV1() + + if _, err := rbacClient.ClusterRoleBindings().Update(ctx, rb, metav1.UpdateOptions{}); err != nil { + logger.Error(err, "failed to update "+clusterInterceptors+" crb") + return err + } + logger.Info("successfully removed subject from ", clusterInterceptors) + return nil +} + func (r *rbac) updateClusterRoleBinding(ctx context.Context, rb *rbacv1.ClusterRoleBinding, sa *corev1.ServiceAccount) error { logger := logging.FromContext(ctx) 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 }