github.com/oam-dev/kubevela@v1.9.11/pkg/resourcekeeper/cache.go (about)

     1  /*
     2  Copyright 2021 The KubeVela Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  	http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package resourcekeeper
    18  
    19  import (
    20  	"context"
    21  
    22  	pkgmaps "github.com/kubevela/pkg/util/maps"
    23  	"github.com/pkg/errors"
    24  	"k8s.io/apimachinery/pkg/api/meta"
    25  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    26  	"k8s.io/apimachinery/pkg/runtime"
    27  	"sigs.k8s.io/controller-runtime/pkg/client"
    28  
    29  	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
    30  	"github.com/oam-dev/kubevela/pkg/multicluster"
    31  	"github.com/oam-dev/kubevela/pkg/oam"
    32  	"github.com/oam-dev/kubevela/pkg/utils/apply"
    33  )
    34  
    35  type resourceCacheEntry struct {
    36  	exists         bool
    37  	err            error
    38  	obj            *unstructured.Unstructured
    39  	mr             v1beta1.ManagedResource
    40  	loaded         bool
    41  	usedBy         []*v1beta1.ResourceTracker
    42  	latestActiveRT *v1beta1.ResourceTracker
    43  	gcExecutorRT   *v1beta1.ResourceTracker
    44  }
    45  
    46  type resourceCache struct {
    47  	app *v1beta1.Application
    48  	cli client.Client
    49  	m   *pkgmaps.SyncMap[string, *resourceCacheEntry]
    50  }
    51  
    52  func newResourceCache(cli client.Client, app *v1beta1.Application) *resourceCache {
    53  	return &resourceCache{
    54  		app: app,
    55  		cli: cli,
    56  		m:   pkgmaps.NewSyncMap[string, *resourceCacheEntry](),
    57  	}
    58  }
    59  
    60  func (cache *resourceCache) registerResourceTrackers(rts ...*v1beta1.ResourceTracker) {
    61  	for _, rt := range rts {
    62  		if rt == nil {
    63  			continue
    64  		}
    65  		for _, mr := range rt.Spec.ManagedResources {
    66  			key := mr.ResourceKey()
    67  			entry, cached := cache.m.Get(key)
    68  			if !cached {
    69  				entry = &resourceCacheEntry{obj: mr.ToUnstructured(), mr: mr}
    70  				cache.m.Set(key, entry)
    71  			}
    72  			entry.usedBy = append(entry.usedBy, rt)
    73  		}
    74  	}
    75  	for _, entry := range cache.m.Data() {
    76  		for i := len(entry.usedBy) - 1; i >= 0; i-- {
    77  			if entry.usedBy[i].GetDeletionTimestamp() == nil {
    78  				entry.latestActiveRT = entry.usedBy[i]
    79  				break
    80  			}
    81  		}
    82  		if entry.latestActiveRT != nil {
    83  			entry.gcExecutorRT = entry.latestActiveRT
    84  		} else if len(entry.usedBy) > 0 {
    85  			entry.gcExecutorRT = entry.usedBy[len(entry.usedBy)-1]
    86  		}
    87  	}
    88  }
    89  
    90  func (cache *resourceCache) get(ctx context.Context, mr v1beta1.ManagedResource) *resourceCacheEntry {
    91  	key := mr.ResourceKey()
    92  	entry, cached := cache.m.Get(key)
    93  	if !cached {
    94  		entry = &resourceCacheEntry{obj: mr.ToUnstructured(), mr: mr}
    95  		cache.m.Set(key, entry)
    96  	}
    97  	if !entry.loaded {
    98  		if err := cache.cli.Get(multicluster.ContextWithClusterName(ctx, mr.Cluster), mr.NamespacedName(), entry.obj); err != nil {
    99  			if multicluster.IsNotFoundOrClusterNotExists(err) || meta.IsNoMatchError(err) || runtime.IsNotRegisteredError(err) {
   100  				entry.exists = false
   101  			} else {
   102  				entry.err = errors.Wrapf(err, "failed to get resource %s", key)
   103  			}
   104  		} else {
   105  			entry.exists = cache.exists(entry.obj)
   106  		}
   107  		entry.loaded = true
   108  	}
   109  	return entry
   110  }
   111  
   112  func (cache *resourceCache) exists(manifest *unstructured.Unstructured) bool {
   113  	if cache.app == nil {
   114  		return true
   115  	}
   116  	return IsResourceManagedByApplication(manifest, cache.app)
   117  }
   118  
   119  // IsResourceManagedByApplication check if resource is managed by application
   120  // If the resource has no ResourceVersion, always return true.
   121  // If the owner label of the resource equals the given app, return true.
   122  // If the sharer label of the resource contains the given app, return true.
   123  // Otherwise, return false.
   124  func IsResourceManagedByApplication(manifest *unstructured.Unstructured, app *v1beta1.Application) bool {
   125  	appKey, controlledBy := apply.GetAppKey(app), apply.GetControlledBy(manifest)
   126  	if appKey == controlledBy || (manifest.GetResourceVersion() == "" && !hasOrphanFinalizer(app)) {
   127  		return true
   128  	}
   129  	annotations := manifest.GetAnnotations()
   130  	if annotations == nil || annotations[oam.AnnotationAppSharedBy] == "" {
   131  		return false
   132  	}
   133  	return apply.ContainsSharer(annotations[oam.AnnotationAppSharedBy], app)
   134  }