github.com/oam-dev/kubevela@v1.9.11/pkg/resourcetracker/app_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 resourcetracker 18 19 import ( 20 "context" 21 "fmt" 22 "math/rand" 23 "testing" 24 25 "github.com/crossplane/crossplane-runtime/pkg/meta" 26 "github.com/stretchr/testify/require" 27 "k8s.io/apimachinery/pkg/api/errors" 28 apimeta "k8s.io/apimachinery/pkg/api/meta" 29 v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 30 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 31 "k8s.io/apimachinery/pkg/runtime/schema" 32 "k8s.io/apimachinery/pkg/types" 33 "sigs.k8s.io/controller-runtime/pkg/client" 34 "sigs.k8s.io/controller-runtime/pkg/client/fake" 35 36 apicommon "github.com/oam-dev/kubevela/apis/core.oam.dev/common" 37 "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" 38 "github.com/oam-dev/kubevela/pkg/oam" 39 "github.com/oam-dev/kubevela/pkg/utils/common" 40 ) 41 42 func TestCreateAndListResourceTrackers(t *testing.T) { 43 r := require.New(t) 44 cli := fake.NewClientBuilder().WithScheme(common.Scheme).Build() 45 app := &v1beta1.Application{ 46 ObjectMeta: v1.ObjectMeta{Name: "app", Namespace: "namespace", UID: types.UID("uid")}, 47 } 48 rootRT, err := CreateRootResourceTracker(context.Background(), cli, app) 49 r.NoError(err) 50 crRT, err := CreateComponentRevisionResourceTracker(context.Background(), cli, app) 51 r.NoError(err) 52 var versionedRTs []*v1beta1.ResourceTracker 53 for i := 0; i < 10; i++ { 54 app.Status.LatestRevision = &apicommon.Revision{Name: fmt.Sprintf("app-v%d", i)} 55 app.Generation = int64(i + 1) 56 currentRT, err := CreateCurrentResourceTracker(context.Background(), cli, app) 57 r.NoError(err) 58 versionedRTs = append(versionedRTs, currentRT) 59 } 60 legacyRT := &v1beta1.ResourceTracker{ 61 ObjectMeta: v1.ObjectMeta{ 62 Name: "legacy-rt", 63 Labels: map[string]string{ 64 oam.LabelAppName: app.Name, 65 oam.LabelAppNamespace: app.Namespace, 66 }, 67 }, 68 } 69 r.NoError(cli.Create(context.Background(), legacyRT)) 70 _rootRT, _currentRT, _historyRTs, _crRT, err := ListApplicationResourceTrackers(context.Background(), cli, app) 71 r.NoError(err) 72 r.Equal(rootRT, _rootRT) 73 r.Equal(versionedRTs[len(versionedRTs)-1], _currentRT) 74 r.Equal(versionedRTs[0:len(versionedRTs)-1], _historyRTs) 75 r.Equal(crRT, _crRT) 76 badRT := &v1beta1.ResourceTracker{ 77 ObjectMeta: v1.ObjectMeta{ 78 Name: "bad-rt", 79 Labels: map[string]string{ 80 oam.LabelAppName: app.Name, 81 oam.LabelAppNamespace: app.Namespace, 82 oam.LabelAppUID: "bad-uid", 83 }, 84 }, 85 } 86 r.NoError(cli.Create(context.Background(), badRT)) 87 _, _, _, _, err = ListApplicationResourceTrackers(context.Background(), cli, app) 88 r.Error(err) 89 r.Contains(err.Error(), "controlled by another application") 90 } 91 92 func TestRecordAndDeleteManifestsInResourceTracker(t *testing.T) { 93 r := require.New(t) 94 cli := fake.NewClientBuilder().WithScheme(common.Scheme).Build() 95 rt := &v1beta1.ResourceTracker{ObjectMeta: v1.ObjectMeta{Name: "rt"}} 96 r.NoError(cli.Create(context.Background(), rt)) 97 n := 10 98 var objs []*unstructured.Unstructured 99 for i := 0; i < n; i++ { 100 obj := &unstructured.Unstructured{} 101 obj.SetName(fmt.Sprintf("workload-%d", i)) 102 objs = append(objs, obj) 103 r.NoError(RecordManifestsInResourceTracker(context.Background(), cli, rt, []*unstructured.Unstructured{obj}, rand.Int()%2 == 0, false, "")) 104 } 105 rand.Shuffle(len(objs), func(i, j int) { objs[i], objs[j] = objs[j], objs[i] }) 106 for i := 0; i < n; i++ { 107 r.NoError(DeletedManifestInResourceTracker(context.Background(), cli, rt, objs[i], true)) 108 r.Equal(len(rt.Spec.ManagedResources), n-i-1) 109 } 110 } 111 112 func TestPublishedVersion(t *testing.T) { 113 r := require.New(t) 114 ctx := context.Background() 115 cli := fake.NewClientBuilder().WithScheme(common.Scheme).Build() 116 app := &v1beta1.Application{ 117 ObjectMeta: v1.ObjectMeta{Name: "app", Namespace: "namespace", UID: types.UID("uid"), Generation: int64(1)}, 118 } 119 meta.AddAnnotations(app, map[string]string{oam.AnnotationPublishVersion: "publish-version-v1"}) 120 rt1, err := CreateCurrentResourceTracker(ctx, cli, app) 121 r.NoError(err) 122 app.SetGeneration(int64(2)) 123 _, err = CreateCurrentResourceTracker(ctx, cli, app) 124 r.True(errors.IsAlreadyExists(err)) 125 app.SetGeneration(int64(3)) 126 app.Annotations[oam.AnnotationPublishVersion] = "publish-version-v2" 127 _, err = CreateCurrentResourceTracker(ctx, cli, app) 128 r.NoError(err) 129 app.SetGeneration(int64(4)) 130 app.Annotations[oam.AnnotationPublishVersion] = "publish-version-v3" 131 _, err = CreateCurrentResourceTracker(ctx, cli, app) 132 r.NoError(err) 133 app.SetGeneration(int64(5)) 134 _, currentRT, historyRTs, _, err := ListApplicationResourceTrackers(ctx, cli, app) 135 r.NoError(err) 136 r.Equal(int64(4), currentRT.Spec.ApplicationGeneration) 137 r.Equal(2, len(historyRTs)) 138 // use old publish version, check conflict 139 app.SetGeneration(int64(6)) 140 app.Annotations[oam.AnnotationPublishVersion] = "publish-version-v2" 141 _, _, _, _, err = ListApplicationResourceTrackers(ctx, cli, app) 142 r.Error(err) 143 r.Contains(err.Error(), "in-use and outdated") 144 // use old deleted publish version, check no conflict 145 rt1.SetFinalizers([]string{}) 146 r.NoError(cli.Update(ctx, rt1)) 147 r.NoError(cli.Delete(ctx, rt1)) 148 app.SetGeneration(int64(7)) 149 app.Annotations[oam.AnnotationPublishVersion] = "publish-version-v1" 150 _, currentRT, historyRTs, _, err = ListApplicationResourceTrackers(ctx, cli, app) 151 r.NoError(err) 152 r.Nil(currentRT) 153 r.Equal(2, len(historyRTs)) 154 } 155 156 func TestListApplicationResourceTrackers(t *testing.T) { 157 r := require.New(t) 158 ctx := context.Background() 159 rt := &unstructured.Unstructured{} 160 rt.SetGroupVersionKind(applicationResourceTrackerGroupVersionKind) 161 rt.SetName("rt") 162 rt.SetNamespace("example") 163 rt.SetLabels(map[string]string{oam.LabelAppName: "app"}) 164 cli := &clientWithoutRTPermission{Client: fake.NewClientBuilder().WithScheme(common.Scheme).WithObjects(rt).Build()} 165 app := &v1beta1.Application{} 166 app.SetName("app") 167 app.SetNamespace("example") 168 _, err := listApplicationResourceTrackers(ctx, cli, app) 169 r.NotNil(err) 170 r.Contains(err.Error(), "no permission for ResourceTracker and vela-prism is not serving ApplicationResourceTracker") 171 cli.recognizeApplicationResourceTracker = true 172 rts, err := listApplicationResourceTrackers(ctx, cli, app) 173 r.NoError(err) 174 r.Equal(len(rts), 1) 175 r.Equal(rts[0].Name, "rt-example") 176 } 177 178 type clientWithoutRTPermission struct { 179 client.Client 180 recognizeApplicationResourceTracker bool 181 } 182 183 func (c *clientWithoutRTPermission) List(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error { 184 if _, isRTList := list.(*v1beta1.ResourceTrackerList); isRTList { 185 return errors.NewForbidden(schema.GroupResource{}, "", nil) 186 } 187 if unsList, isUnsList := list.(*unstructured.UnstructuredList); isUnsList && unsList.GetKind() == applicationResourceTrackerGroupVersionKind.Kind && !c.recognizeApplicationResourceTracker { 188 return &apimeta.NoKindMatchError{} 189 } 190 return c.Client.List(ctx, list, opts...) 191 }