github.com/oam-dev/kubevela@v1.9.11/pkg/resourcekeeper/gc_test.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 "fmt" 22 "testing" 23 24 workflowv1alpha1 "github.com/kubevela/workflow/api/v1alpha1" 25 "github.com/stretchr/testify/require" 26 appsv1 "k8s.io/api/apps/v1" 27 corev1 "k8s.io/api/core/v1" 28 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 29 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 30 "k8s.io/apimachinery/pkg/runtime" 31 "sigs.k8s.io/controller-runtime/pkg/client" 32 "sigs.k8s.io/controller-runtime/pkg/client/fake" 33 34 apicommon "github.com/oam-dev/kubevela/apis/core.oam.dev/common" 35 "github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha1" 36 "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" 37 "github.com/oam-dev/kubevela/pkg/oam" 38 "github.com/oam-dev/kubevela/pkg/resourcetracker" 39 "github.com/oam-dev/kubevela/pkg/utils/common" 40 ) 41 42 func TestResourceKeeperGarbageCollect(t *testing.T) { 43 MarkWithProbability = 1.0 44 r := require.New(t) 45 cli := fake.NewClientBuilder().WithScheme(common.Scheme).Build() 46 ctx := context.Background() 47 48 rtMaps := map[int64]*v1beta1.ResourceTracker{} 49 cmMaps := map[int]*unstructured.Unstructured{} 50 crMaps := map[int]*appsv1.ControllerRevision{} 51 52 crRT := &v1beta1.ResourceTracker{ 53 ObjectMeta: metav1.ObjectMeta{Name: "app-comp-rev", Labels: map[string]string{ 54 oam.LabelAppName: "app", 55 oam.LabelAppNamespace: "default", 56 oam.LabelAppUID: "uid", 57 }, Finalizers: []string{resourcetracker.Finalizer}}, 58 Spec: v1beta1.ResourceTrackerSpec{ 59 Type: v1beta1.ResourceTrackerTypeComponentRevision, 60 }, 61 } 62 r.NoError(cli.Create(ctx, crRT)) 63 64 createRT := func(gen int64) { 65 _rt := &v1beta1.ResourceTracker{ 66 ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("app-v%d", gen), Labels: map[string]string{ 67 oam.LabelAppName: "app", 68 oam.LabelAppNamespace: "default", 69 oam.LabelAppUID: "uid", 70 }, Finalizers: []string{resourcetracker.Finalizer}}, 71 Spec: v1beta1.ResourceTrackerSpec{ 72 Type: v1beta1.ResourceTrackerTypeVersioned, 73 ApplicationGeneration: gen, 74 }, 75 } 76 r.NoError(cli.Create(ctx, _rt)) 77 rtMaps[gen] = _rt 78 } 79 80 addConfigMapToRT := func(i int, gen int64, compID int) { 81 _rt := rtMaps[gen] 82 if _, exists := cmMaps[i]; !exists { 83 cm := &unstructured.Unstructured{} 84 cm.SetName(fmt.Sprintf("cm-%d", i)) 85 cm.SetNamespace("default") 86 cm.SetGroupVersionKind(corev1.SchemeGroupVersion.WithKind("ConfigMap")) 87 cm.SetLabels(map[string]string{ 88 oam.LabelAppComponent: fmt.Sprintf("comp-%d", compID), 89 oam.LabelAppNamespace: "default", 90 oam.LabelAppName: "app", 91 }) 92 r.NoError(cli.Create(ctx, cm)) 93 cmMaps[i] = cm 94 } 95 if _, exists := crMaps[compID]; !exists { 96 cr := &appsv1.ControllerRevision{Data: runtime.RawExtension{Raw: []byte(`{}`)}} 97 cr.SetName(fmt.Sprintf("cr-comp-%d", compID)) 98 cr.SetNamespace("default") 99 cr.SetLabels(map[string]string{ 100 oam.LabelAppComponent: fmt.Sprintf("comp-%d", compID), 101 }) 102 r.NoError(cli.Create(ctx, cr)) 103 crMaps[compID] = cr 104 obj := &unstructured.Unstructured{} 105 obj.SetName(cr.GetName()) 106 obj.SetNamespace(cr.GetNamespace()) 107 obj.SetLabels(cr.GetLabels()) 108 r.NoError(resourcetracker.RecordManifestsInResourceTracker(ctx, cli, crRT, []*unstructured.Unstructured{obj}, true, false, "")) 109 } 110 r.NoError(resourcetracker.RecordManifestsInResourceTracker(ctx, cli, _rt, []*unstructured.Unstructured{cmMaps[i]}, true, false, "")) 111 } 112 113 checkCount := func(cmCount, rtCount int, crCount int) { 114 n := 0 115 for _, v := range cmMaps { 116 o := &unstructured.Unstructured{} 117 o.SetGroupVersionKind(corev1.SchemeGroupVersion.WithKind("ConfigMap")) 118 err := cli.Get(ctx, client.ObjectKeyFromObject(v), o) 119 if err == nil { 120 n += 1 121 } 122 } 123 r.Equal(cmCount, n) 124 _rts := &v1beta1.ResourceTrackerList{} 125 r.NoError(cli.List(ctx, _rts)) 126 r.Equal(rtCount, len(_rts.Items)) 127 _crs := &appsv1.ControllerRevisionList{} 128 r.NoError(cli.List(ctx, _crs)) 129 r.Equal(crCount, len(_crs.Items)) 130 } 131 132 createRK := func(gen int64, keepLegacy bool, order v1alpha1.GarbageCollectOrder, components ...apicommon.ApplicationComponent) *resourceKeeper { 133 _rk, err := NewResourceKeeper(ctx, cli, &v1beta1.Application{ 134 ObjectMeta: metav1.ObjectMeta{Name: "app", Namespace: "default", UID: "uid", Generation: gen}, 135 Spec: v1beta1.ApplicationSpec{Components: components}, 136 }) 137 r.NoError(err) 138 rk := _rk.(*resourceKeeper) 139 rk.garbageCollectPolicy = &v1alpha1.GarbageCollectPolicySpec{ 140 Order: order, 141 KeepLegacyResource: keepLegacy, 142 } 143 return rk 144 } 145 146 createRT(1) 147 addConfigMapToRT(1, 1, 1) 148 addConfigMapToRT(2, 1, 2) 149 createRT(2) 150 addConfigMapToRT(1, 2, 1) 151 addConfigMapToRT(3, 2, 3) 152 createRT(3) 153 addConfigMapToRT(4, 3, 3) 154 createRT(4) 155 addConfigMapToRT(5, 4, 4) 156 addConfigMapToRT(6, 4, 5) 157 addConfigMapToRT(7, 4, 6) 158 checkCount(7, 5, 6) 159 160 opts := []GCOption{DisableLegacyGCOption{}} 161 // no need to gc 162 rk := createRK(4, true, "") 163 finished, _, err := rk.GarbageCollect(ctx, opts...) 164 r.NoError(err) 165 r.True(finished) 166 checkCount(7, 5, 6) 167 168 // delete rt2, trigger gc for cm3 169 dt := metav1.Now() 170 rtMaps[2].SetDeletionTimestamp(&dt) 171 r.NoError(cli.Update(ctx, rtMaps[2])) 172 rk = createRK(4, true, "") 173 finished, _, err = rk.GarbageCollect(ctx, opts...) 174 r.NoError(err) 175 r.False(finished) 176 rk = createRK(4, true, "") 177 finished, _, err = rk.GarbageCollect(ctx, opts...) 178 r.NoError(err) 179 r.True(finished) 180 checkCount(6, 4, 6) 181 182 // delete cm4, trigger gc for rt3, comp-3 no use 183 r.NoError(cli.Delete(ctx, cmMaps[4])) 184 rk = createRK(5, true, "") 185 finished, _, err = rk.GarbageCollect(ctx, opts...) 186 r.NoError(err) 187 r.True(finished) 188 checkCount(5, 3, 5) 189 190 // upgrade and gc legacy rt1 191 rk = createRK(4, false, "") 192 finished, _, err = rk.GarbageCollect(ctx, opts...) 193 r.NoError(err) 194 r.False(finished) 195 rk = createRK(4, false, "") 196 finished, _, err = rk.GarbageCollect(ctx, opts...) 197 r.NoError(err) 198 r.True(finished) 199 checkCount(3, 2, 3) 200 201 // delete with sequential 202 comps := []apicommon.ApplicationComponent{ 203 { 204 Name: "comp-5", 205 DependsOn: []string{ 206 "comp-6", 207 }, 208 }, 209 { 210 Name: "comp-6", 211 DependsOn: []string{ 212 "comp-7", 213 }, 214 }, 215 { 216 Name: "comp-7", 217 }, 218 } 219 rk = createRK(5, false, v1alpha1.OrderDependency, comps...) 220 rtMaps[3].SetDeletionTimestamp(&dt) 221 finished, _, err = rk.GarbageCollect(ctx, opts...) 222 r.NoError(err) 223 r.False(finished) 224 rk = createRK(5, false, v1alpha1.OrderDependency, comps...) 225 finished, _, err = rk.GarbageCollect(ctx, opts...) 226 r.NoError(err) 227 r.False(finished) 228 rk = createRK(5, false, v1alpha1.OrderDependency, comps...) 229 finished, _, err = rk.GarbageCollect(ctx, opts...) 230 r.NoError(err) 231 r.True(finished) 232 233 r.NoError(cli.Get(ctx, client.ObjectKeyFromObject(crRT), crRT)) 234 // recreate rt, delete app, gc all 235 createRT(5) 236 addConfigMapToRT(8, 5, 8) 237 addConfigMapToRT(9, 5, 8) 238 createRT(6) 239 addConfigMapToRT(9, 6, 8) 240 addConfigMapToRT(10, 6, 8) 241 checkCount(3, 3, 1) 242 243 rk = createRK(6, false, "") 244 rk.app.SetDeletionTimestamp(&dt) 245 finished, _, err = rk.GarbageCollect(ctx, opts...) 246 r.NoError(err) 247 r.False(finished) 248 rk = createRK(6, false, "") 249 finished, _, err = rk.GarbageCollect(ctx, opts...) 250 r.NoError(err) 251 r.True(finished) 252 checkCount(0, 0, 0) 253 254 rk = createRK(7, false, "") 255 finished, _, err = rk.GarbageCollect(ctx, opts...) 256 r.NoError(err) 257 r.True(finished) 258 } 259 260 func TestCheckDependentComponent(t *testing.T) { 261 rk := &resourceKeeper{ 262 app: &v1beta1.Application{ 263 Spec: v1beta1.ApplicationSpec{ 264 Components: []apicommon.ApplicationComponent{ 265 { 266 Name: "comp-1", 267 Outputs: workflowv1alpha1.StepOutputs{ 268 { 269 Name: "output-1", 270 }, 271 }, 272 }, 273 { 274 Name: "comp-2", 275 Outputs: workflowv1alpha1.StepOutputs{ 276 { 277 Name: "output-2", 278 }, 279 }, 280 }, 281 { 282 Name: "comp-3", 283 Inputs: workflowv1alpha1.StepInputs{ 284 { 285 From: "output-1", 286 }, 287 { 288 From: "output-2", 289 }, 290 }, 291 }, 292 { 293 Name: "comp-4", 294 DependsOn: []string{"comp-3"}, 295 }, 296 { 297 Name: "comp-5", 298 DependsOn: []string{"comp-4", "comp-3"}, 299 }, 300 }, 301 }, 302 }, 303 } 304 testCases := []struct { 305 comp string 306 result []string 307 }{ 308 { 309 comp: "comp-1", 310 result: []string{"comp-3"}, 311 }, 312 { 313 comp: "comp-2", 314 result: []string{"comp-3"}, 315 }, 316 { 317 comp: "comp-3", 318 result: []string{"comp-4", "comp-5"}, 319 }, 320 { 321 comp: "comp-4", 322 result: []string{"comp-5"}, 323 }, 324 { 325 comp: "comp-5", 326 result: []string{}, 327 }, 328 } 329 gcHandler := &gcHandler{ 330 resourceKeeper: rk, 331 } 332 r := require.New(t) 333 for _, tc := range testCases { 334 mr := v1beta1.ManagedResource{ 335 OAMObjectReference: apicommon.OAMObjectReference{ 336 Component: tc.comp, 337 }, 338 } 339 r.Equal(gcHandler.checkDependentComponent(mr), tc.result) 340 } 341 } 342 343 func TestEnableMarkStageGCOnWorkflowFailure(t *testing.T) { 344 h := &resourceKeeper{garbageCollectPolicy: &v1alpha1.GarbageCollectPolicySpec{ContinueOnFailure: true}} 345 options := []GCOption{DisableMarkStageGCOption{}} 346 cfg := h.buildGCConfig(context.Background(), options...) 347 require.True(t, cfg.disableMark) 348 cfg = h.buildGCConfig(WithPhase(context.Background(), apicommon.ApplicationWorkflowFailed), options...) 349 require.False(t, cfg.disableMark) 350 }