This is an automated email from the ASF dual-hosted git repository.

tsato pushed a commit to branch release-1.8.x
in repository https://gitbox.apache.org/repos/asf/camel-k.git

commit f841aa6d9489c83963fc0e8da28b22329a2a9936
Author: Antonin Stefanutti <anto...@stefanutti.fr>
AuthorDate: Wed Jun 1 16:10:58 2022 +0200

    feat(gc): Use SelfSubjectRulesReview to scan for garbage collectable 
resources
    
    (cherry picked from commit 8caeee372112d2dd60e9f5121af84e52de5355e4)
---
 pkg/trait/gc.go | 100 +++++++++++++++++++++++++++++++++-----------------------
 1 file changed, 60 insertions(+), 40 deletions(-)

diff --git a/pkg/trait/gc.go b/pkg/trait/gc.go
index 1ff3204f5..b11802900 100644
--- a/pkg/trait/gc.go
+++ b/pkg/trait/gc.go
@@ -19,6 +19,7 @@ package trait
 
 import (
        "context"
+       "fmt"
        "path/filepath"
        "regexp"
        "strconv"
@@ -26,6 +27,8 @@ import (
        "sync"
        "time"
 
+       "github.com/apache/camel-k/pkg/util"
+       authorization "k8s.io/api/authorization/v1"
        k8serrors "k8s.io/apimachinery/pkg/api/errors"
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
        "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -35,7 +38,6 @@ import (
        "k8s.io/client-go/discovery"
        "k8s.io/client-go/discovery/cached/disk"
        "k8s.io/client-go/discovery/cached/memory"
-
        ctrl "sigs.k8s.io/controller-runtime/pkg/client"
 
        v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
@@ -81,8 +83,7 @@ func (t *garbageCollectorTrait) Configure(e *Environment) 
(bool, error) {
                t.DiscoveryCache = &s
        }
 
-       return e.IntegrationInPhase(v1.IntegrationPhaseInitialization) || 
e.IntegrationInRunningPhases(),
-               nil
+       return e.IntegrationInPhase(v1.IntegrationPhaseInitialization) || 
e.IntegrationInRunningPhases(), nil
 }
 
 func (t *garbageCollectorTrait) Apply(e *Environment) error {
@@ -92,12 +93,9 @@ func (t *garbageCollectorTrait) Apply(e *Environment) error {
                // Register a post action that deletes the existing resources 
that are labelled
                // with the previous integration generations.
                // TODO: this should be refined so that it's run when all the 
replicas for the newer generation
-               // are ready. This is to be added when the integration scale 
status is refined with ready replicas
+               // are ready.
                e.PostActions = append(e.PostActions, func(env *Environment) 
error {
-                       // The collection and deletion are performed 
asynchronously to avoid blocking
-                       // the reconciliation loop.
-                       go t.garbageCollectResources(env)
-                       return nil
+                       return t.garbageCollectResources(env)
                })
 
                fallthrough
@@ -121,43 +119,40 @@ func (t *garbageCollectorTrait) Apply(e *Environment) 
error {
        return nil
 }
 
-func (t *garbageCollectorTrait) garbageCollectResources(e *Environment) {
+func (t *garbageCollectorTrait) garbageCollectResources(e *Environment) error {
+       deletableGVKs, err := t.getDeletableTypes(e)
+       if err != nil {
+               return fmt.Errorf("cannot discover GVK types: %v", err)
+       }
+
        integration, _ := labels.NewRequirement(v1.IntegrationLabel, 
selection.Equals, []string{e.Integration.Name})
        generation, err := labels.NewRequirement("camel.apache.org/generation", 
selection.LessThan, []string{strconv.FormatInt(e.Integration.GetGeneration(), 
10)})
        if err != nil {
-               t.L.ForIntegration(e.Integration).Errorf(err, "cannot determine 
generation requirement")
-               return
+               return fmt.Errorf("cannot determine generation requirement: 
%v", err)
        }
        selector := labels.NewSelector().
                Add(*integration).
                Add(*generation)
 
-       deletableGVKs, err := t.getDeletableTypes(e)
-       if err != nil {
-               t.L.ForIntegration(e.Integration).Errorf(err, "cannot discover 
GVK types")
-               return
-       }
-
-       t.deleteEachOf(deletableGVKs, e, selector)
+       return t.deleteEachOf(e.Ctx, deletableGVKs, e, selector)
 }
 
-func (t *garbageCollectorTrait) deleteEachOf(gvks 
map[schema.GroupVersionKind]struct{}, e *Environment, selector labels.Selector) 
{
-       for gvk := range gvks {
+func (t *garbageCollectorTrait) deleteEachOf(ctx context.Context, GVKs 
map[schema.GroupVersionKind]struct{}, e *Environment, selector labels.Selector) 
error {
+       for GVK := range GVKs {
                resources := unstructured.UnstructuredList{
                        Object: map[string]interface{}{
-                               "apiVersion": gvk.GroupVersion().String(),
-                               "kind":       gvk.Kind,
+                               "apiVersion": GVK.GroupVersion().String(),
+                               "kind":       GVK.Kind,
                        },
                }
                options := []ctrl.ListOption{
                        ctrl.InNamespace(e.Integration.Namespace),
                        ctrl.MatchingLabelsSelector{Selector: selector},
                }
-               if err := t.Client.List(context.TODO(), &resources, 
options...); err != nil {
-                       if !k8serrors.IsNotFound(err) && 
!k8serrors.IsForbidden(err) {
-                               t.L.ForIntegration(e.Integration).Errorf(err, 
"cannot list child resources: %v", gvk)
+               if err := t.Client.List(ctx, &resources, options...); err != 
nil {
+                       if !k8serrors.IsNotFound(err) {
+                               return fmt.Errorf("cannot list child resources: 
%v", err)
                        }
-
                        continue
                }
 
@@ -166,7 +161,7 @@ func (t *garbageCollectorTrait) deleteEachOf(gvks 
map[schema.GroupVersionKind]st
                        if !t.canBeDeleted(e, r) {
                                continue
                        }
-                       err := t.Client.Delete(context.TODO(), &r, 
ctrl.PropagationPolicy(metav1.DeletePropagationBackground))
+                       err := t.Client.Delete(ctx, &r, 
ctrl.PropagationPolicy(metav1.DeletePropagationBackground))
                        if err != nil {
                                // The resource may have already been deleted
                                if !k8serrors.IsNotFound(err) {
@@ -177,6 +172,8 @@ func (t *garbageCollectorTrait) deleteEachOf(gvks 
map[schema.GroupVersionKind]st
                        }
                }
        }
+
+       return nil
 }
 
 func (t *garbageCollectorTrait) canBeDeleted(e *Environment, u 
unstructured.Unstructured) bool {
@@ -192,7 +189,7 @@ func (t *garbageCollectorTrait) canBeDeleted(e 
*Environment, u unstructured.Unst
 func (t *garbageCollectorTrait) getDeletableTypes(e *Environment) 
(map[schema.GroupVersionKind]struct{}, error) {
        // We rely on the discovery API to retrieve all the resources GVK,
        // that results in an unbounded set that can impact garbage collection 
latency when scaling up.
-       discoveryClient, err := t.discoveryClient(e)
+       discoveryClient, err := t.discoveryClient()
        if err != nil {
                return nil, err
        }
@@ -206,21 +203,45 @@ func (t *garbageCollectorTrait) getDeletableTypes(e 
*Environment) (map[schema.Gr
 
        // We only take types that support the "delete" verb,
        // to prevents from performing queries that we know are going to return 
"MethodNotAllowed".
-       return 
groupVersionKinds(discovery.FilteredBy(discovery.SupportsAllVerbs{Verbs: 
[]string{"delete"}}, resources)),
-               nil
-}
+       APIResourceLists := 
discovery.FilteredBy(discovery.SupportsAllVerbs{Verbs: []string{"delete"}}, 
resources)
+
+       // Retrieve the permissions granted to the operator service account.
+       // We assume the operator has only to garbage collect the resources it 
has created.
+       srr := &authorization.SelfSubjectRulesReview{
+               Spec: authorization.SelfSubjectRulesReviewSpec{
+                       Namespace: e.Integration.Namespace,
+               },
+       }
+       res, err := 
e.Client.AuthorizationV1().SelfSubjectRulesReviews().Create(e.Ctx, srr, 
metav1.CreateOptions{})
+       if err != nil {
+               return nil, err
+       }
 
-func groupVersionKinds(rls []*metav1.APIResourceList) 
map[schema.GroupVersionKind]struct{} {
-       GVKs := map[schema.GroupVersionKind]struct{}{}
-       for _, rl := range rls {
-               for _, r := range rl.APIResources {
-                       GVKs[schema.FromAPIVersionAndKind(rl.GroupVersion, 
r.Kind)] = struct{}{}
+       GVKs := make(map[schema.GroupVersionKind]struct{})
+       for _, APIResourceList := range APIResourceLists {
+               for _, resource := range APIResourceList.APIResources {
+               rule:
+                       for _, rule := range res.Status.ResourceRules {
+                               if !util.StringSliceContainsAnyOf(rule.Verbs, 
"delete", "*") {
+                                       continue
+                               }
+                               for _, group := range rule.APIGroups {
+                                       for _, name := range rule.Resources {
+                                               if (resource.Group == group || 
group == "*") && (resource.Name == name || name == "*") {
+                                                       GVK := 
schema.FromAPIVersionAndKind(APIResourceList.GroupVersion, resource.Kind)
+                                                       GVKs[GVK] = struct{}{}
+                                                       break rule
+                                               }
+                                       }
+                               }
+                       }
                }
        }
-       return GVKs
+
+       return GVKs, nil
 }
 
-func (t *garbageCollectorTrait) discoveryClient(e *Environment) 
(discovery.DiscoveryInterface, error) {
+func (t *garbageCollectorTrait) discoveryClient() 
(discovery.DiscoveryInterface, error) {
        discoveryClientLock.Lock()
        defer discoveryClientLock.Unlock()
 
@@ -247,7 +268,6 @@ func (t *garbageCollectorTrait) discoveryClient(e 
*Environment) (discovery.Disco
                return t.Client.Discovery(), nil
 
        default:
-               t.L.ForIntegration(e.Integration).Infof("unsupported discovery 
cache type: %s", *t.DiscoveryCache)
-               return t.Client.Discovery(), nil
+               return nil, fmt.Errorf("unsupported discovery cache type: %s", 
*t.DiscoveryCache)
        }
 }

Reply via email to