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 }