Skip to content

Commit

Permalink
Merge pull request #1049 from njhale/clean-orefs-4.1
Browse files Browse the repository at this point in the history
[release-4.1] Bug 1755646: Cleanup leftover cross-namespace OwnerReferences
  • Loading branch information
openshift-merge-robot authored Oct 1, 2019
2 parents dacfea7 + 0b8c7c8 commit d7e24ee
Show file tree
Hide file tree
Showing 6 changed files with 794 additions and 1 deletion.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

SHELL := /bin/bash
PKG := github.com/operator-framework/operator-lifecycle-manager
MOD_FLAGS := $(shell (go version | grep -q -E "1\.(11|12)") && echo -mod=vendor)
MOD_FLAGS := $(shell (go version | grep -q -E "1\.(11|12|13)") && echo -mod=vendor)
CMDS := $(addprefix bin/, $(shell go list $(MOD_FLAGS) ./cmd/... | xargs -I{} basename {}))
CODEGEN := ./vendor/k8s.io/code-generator/generate_groups.sh
CODEGEN_INTERNAL := ./vendor/k8s.io/code-generator/generate_internal_groups.sh
Expand Down
182 changes: 182 additions & 0 deletions cmd/olm/cleanup.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
package main

import (
"time"

"github.com/sirupsen/logrus"
rbacv1 "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/util/retry"

"github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
"github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned"
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient"
)

const (
pollInterval = 1 * time.Second
pollDuration = 5 * time.Minute
)

type checkResourceFunc func() error
type deleteResourceFunc func() error
type mutateMeta func(obj metav1.Object) (mutated bool)

func cleanup(logger *logrus.Logger, c operatorclient.ClientInterface, crc versioned.Interface) {
if err := cleanupOwnerReferences(c, crc); err != nil {
logger.WithError(err).Fatal("couldn't cleanup cross-namespace ownerreferences")
}

if err := waitForDelete(checkSubscription(crc, "packageserver"), deleteSubscription(crc, "packageserver")); err != nil {
logger.WithError(err).Fatal("couldn't clean previous release")
}
}

func waitForDelete(checkResource checkResourceFunc, deleteResource deleteResourceFunc) error {
if err := checkResource(); err != nil && errors.IsNotFound(err) {
return nil
}
if err := deleteResource(); err != nil {
return err
}
err := wait.Poll(pollInterval, pollDuration, func() (bool, error) {
err := checkResource()
if errors.IsNotFound(err) {
return true, nil
}
if err != nil {
return false, err
}
return false, nil
})

return err
}

func checkSubscription(crc versioned.Interface, name string) checkResourceFunc {
return func() error {
_, err := crc.OperatorsV1alpha1().Subscriptions(*namespace).Get(name, metav1.GetOptions{})
return err
}
}

func deleteSubscription(crc versioned.Interface, name string) deleteResourceFunc {
return func() error {
return crc.OperatorsV1alpha1().Subscriptions(*namespace).Delete(name, metav1.NewDeleteOptions(0))
}
}

// cleanupOwnerReferences cleans up inter-namespace and cluster-to-namespace scoped OwnerReferences to ClusterServiceVersions.
//
// Cross-namespace and cluster-to-namespace scoped OwnerReferences may cause sibling resources in the owner namespace to be
// deleted sporadically (see CVE-2019-3884 https://access.redhat.com/security/cve/cve-2019-3884). Older versions of OLM use both types of
// OwnerReference, and in cases where OLM is updated, they must be removed to prevent erroneous deletion of OLM's self-hosted components.
func cleanupOwnerReferences(c operatorclient.ClientInterface, crc versioned.Interface) error {
listOpts := metav1.ListOptions{}
csvs, err := crc.OperatorsV1alpha1().ClusterServiceVersions(metav1.NamespaceAll).List(listOpts)
if err != nil {
return err
}

uidNamespaces := map[types.UID]string{}
for _, csv := range csvs.Items {
uidNamespaces[csv.GetUID()] = csv.GetNamespace()
}
removeBadRefs := crossNamespaceOwnerReferenceRemoval(v1alpha1.ClusterServiceVersionKind, uidNamespaces)

// Cleanup cross-namespace OwnerReferences on CSVs, ClusterRoles/Bindings, and Roles/Bindings
var objs []metav1.Object
for _, obj := range csvs.Items {
objs = append(objs, &obj)
}

clusterRoles, _ := c.KubernetesInterface().RbacV1().ClusterRoles().List(listOpts)
for _, obj := range clusterRoles.Items {
objs = append(objs, &obj)
}

clusterRoleBindings, _ := c.KubernetesInterface().RbacV1().ClusterRoleBindings().List(listOpts)
for _, obj := range clusterRoleBindings.Items {
objs = append(objs, &obj)
}

roles, _ := c.KubernetesInterface().RbacV1().Roles(metav1.NamespaceAll).List(listOpts)
for _, obj := range roles.Items {
objs = append(objs, &obj)
}
roleBindings, _ := c.KubernetesInterface().RbacV1().RoleBindings(metav1.NamespaceAll).List(listOpts)
for _, obj := range roleBindings.Items {
objs = append(objs, &obj)
}

for _, obj := range objs {
if !removeBadRefs(obj) {
continue
}

update := func() error {
// If this is not a type we care about, do nothing
return nil
}
switch v := obj.(type) {
case *v1alpha1.ClusterServiceVersion:
update = func() error {
_, err := crc.OperatorsV1alpha1().ClusterServiceVersions(v.GetNamespace()).Update(v)
return err
}
case *rbacv1.ClusterRole:
update = func() error {
_, err = c.KubernetesInterface().RbacV1().ClusterRoles().Update(v)
return err
}
case *rbacv1.ClusterRoleBinding:
update = func() error {
_, err = c.KubernetesInterface().RbacV1().ClusterRoleBindings().Update(v)
return err
}
case *rbacv1.Role:
update = func() error {
_, err = c.KubernetesInterface().RbacV1().Roles(v.GetNamespace()).Update(v)
return err
}
case *rbacv1.RoleBinding:
update = func() error {
_, err = c.KubernetesInterface().RbacV1().RoleBindings(v.GetNamespace()).Update(v)
return err
}
}

if err := retry.RetryOnConflict(retry.DefaultBackoff, update); err != nil {
return err
}
}

return nil
}

func crossNamespaceOwnerReferenceRemoval(kind string, uidNamespaces map[types.UID]string) mutateMeta {
return func(obj metav1.Object) (mutated bool) {
var cleanRefs []metav1.OwnerReference
objNamespace := obj.GetNamespace()
for _, ref := range obj.GetOwnerReferences() {
if ref.Kind == kind {
refNamespace, ok := uidNamespaces[ref.UID]
if !ok || (refNamespace != metav1.NamespaceAll && refNamespace != objNamespace) {
mutated = true
continue
}
}

cleanRefs = append(cleanRefs, ref)
}

if mutated {
obj.SetOwnerReferences(cleanRefs)
}

return
}
}
Loading

0 comments on commit d7e24ee

Please sign in to comment.