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..9cbaf7fa56 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:"tektonInstallerSet,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..942299b695 100644 --- a/pkg/reconciler/openshift/tektonconfig/extension.go +++ b/pkg/reconciler/openshift/tektonconfig/extension.go @@ -65,10 +65,11 @@ 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), + kubeClientSet: oe.kubeClientSet, + operatorClientSet: oe.operatorClientSet, + version: os.Getenv(versionKey), + disableRbacResourcesForOpenshift: config.Spec.Params, + tektonConfig: config, } return r.createResources(ctx) } @@ -103,6 +104,6 @@ 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()) } diff --git a/pkg/reconciler/openshift/tektonconfig/rbac.go b/pkg/reconciler/openshift/tektonconfig/rbac.go index 7b1816930f..0b99def68b 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,22 +42,29 @@ 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. var nsRegex = regexp.MustCompile(common.NamespaceIgnorePattern) type rbac struct { - kubeClientSet kubernetes.Interface - operatorClientSet clientset.Interface - ownerRef metav1.OwnerReference - version string + kubeClientSet kubernetes.Interface + operatorClientSet clientset.Interface + ownerRef metav1.OwnerReference + version string + disableRbacResourcesForOpenshift []v1alpha1.Param + 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), }) @@ -75,12 +83,35 @@ func (r *rbac) cleanUp(ctx context.Context) error { return nil } +// getRbacResource returns list of namespaces where rbac resources need to be created. +func (r *rbac) getRbacResource(ctx context.Context, n corev1.Namespace, rbacNamespaces []corev1.Namespace) ([]corev1.Namespace, error) { + 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 by deleting installerSet + if v.Name == "createRbacResource" { + if v.Value == "false" { + if err := r.deleteInstallerSet(ctx, r.tektonConfig, componentName); err != nil { + return nil, err + } + } else { + rbacNamespaces = append(rbacNamespaces, n) + } + } else { + rbacNamespaces = append(rbacNamespaces, n) + } + continue + } + return rbacNamespaces, nil +} + 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), }) @@ -88,22 +119,58 @@ func (r *rbac) createResources(ctx context.Context) error { return err } + namespacesWithVersionLabel, err := r.kubeClientSet.CoreV1().Namespaces().List(ctx, metav1.ListOptions{ + LabelSelector: fmt.Sprintf("%s = %s", namespaceVersionLabel, r.version), + }) + if err != nil { + return err + } + // list of namespaces rbac resources need to be created var rbacNamespaces []corev1.Namespace + for _, n := range namespacesWithVersionLabel.Items { + rbacNamespaces, err = r.getRbacResource(ctx, n, rbacNamespaces) + if err != nil { + return err + } + } + // filter namespaces: // ignore ns with name passing regex `^(openshift|kube)-` for _, n := range namespaces.Items { if ignore := nsRegex.MatchString(n.GetName()); ignore { continue } - rbacNamespaces = append(rbacNamespaces, n) + rbacNamespaces, err = r.getRbacResource(ctx, n, rbacNamespaces) + if err != nil { + return err + } } if len(rbacNamespaces) == 0 { 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 +203,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 +451,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 } @@ -586,3 +653,114 @@ func (r *rbac) updateOwnerRefs(ownerRef []metav1.OwnerReference) []metav1.OwnerR } return ownerRef } + +func createInstallerSet(ctx context.Context, oc clientset.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 clientset.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/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 }