@@ -1018,6 +1018,244 @@ var _ = Describe("HandlersUtils", func() {
10181018 }, timeout , pollingInterval ).Should (BeTrue ())
10191019 })
10201020
1021+ It (`undeployStaleResources scopes deletion to resources annotated for this ClusterSummary, even when isMgmtCluster is false` , func () {
1022+ // This reproduces the scenario of a self-managed SveltosCluster: this ClusterSummary's
1023+ // "remote" cluster is physically the same cluster other ClusterSummaries deploy Local
1024+ // resources into. The remote-cluster cleanup pass (isMgmtCluster=false) must still only
1025+ // ever touch resources this exact ClusterSummary created, identified via the
1026+ // clustersummary annotation, and must leave other ClusterSummaries' resources alone.
1027+ ownClusterRoleName := randomString ()
1028+ ownClusterRole := & rbacv1.ClusterRole {
1029+ ObjectMeta : metav1.ObjectMeta {
1030+ Name : ownClusterRoleName ,
1031+ Labels : map [string ]string {
1032+ deployer .ReasonLabel : string (libsveltosv1beta1 .FeatureResources ),
1033+ },
1034+ Annotations : map [string ]string {
1035+ deployer .ReferenceKindAnnotation : string (libsveltosv1beta1 .ConfigMapReferencedResourceKind ),
1036+ deployer .ReferenceNamespaceAnnotation : randomString (),
1037+ deployer .ReferenceNameAnnotation : randomString (),
1038+ controllers .ClusterSummaryAnnotation : controllers .GetClusterSummaryAnnotationValue (clusterSummary ),
1039+ },
1040+ },
1041+ }
1042+
1043+ otherClusterRoleName := randomString ()
1044+ otherClusterRole := & rbacv1.ClusterRole {
1045+ ObjectMeta : metav1.ObjectMeta {
1046+ Name : otherClusterRoleName ,
1047+ Labels : map [string ]string {
1048+ deployer .ReasonLabel : string (libsveltosv1beta1 .FeatureResources ),
1049+ },
1050+ Annotations : map [string ]string {
1051+ deployer .ReferenceKindAnnotation : string (libsveltosv1beta1 .ConfigMapReferencedResourceKind ),
1052+ deployer .ReferenceNamespaceAnnotation : randomString (),
1053+ deployer .ReferenceNameAnnotation : randomString (),
1054+ // Deployed (Local) by a different ClusterSummary for a different managed cluster.
1055+ controllers .ClusterSummaryAnnotation : randomString (),
1056+ },
1057+ },
1058+ }
1059+
1060+ // Simulates a resource deployed by an older addon-controller version, before the
1061+ // clustersummary annotation was set on every deploy. It must still be detected as
1062+ // stale and removed by this same ClusterSummary; otherwise upgrading would leave such
1063+ // resources undeletable forever.
1064+ legacyClusterRoleName := randomString ()
1065+ legacyClusterRole := & rbacv1.ClusterRole {
1066+ ObjectMeta : metav1.ObjectMeta {
1067+ Name : legacyClusterRoleName ,
1068+ Labels : map [string ]string {
1069+ deployer .ReasonLabel : string (libsveltosv1beta1 .FeatureResources ),
1070+ },
1071+ Annotations : map [string ]string {
1072+ deployer .ReferenceKindAnnotation : string (libsveltosv1beta1 .ConfigMapReferencedResourceKind ),
1073+ deployer .ReferenceNamespaceAnnotation : randomString (),
1074+ deployer .ReferenceNameAnnotation : randomString (),
1075+ },
1076+ },
1077+ }
1078+
1079+ Expect (testEnv .Create (context .TODO (), ownClusterRole )).To (Succeed ())
1080+ Expect (waitForObject (ctx , testEnv .Client , ownClusterRole )).To (Succeed ())
1081+ Expect (testEnv .Create (context .TODO (), otherClusterRole )).To (Succeed ())
1082+ Expect (waitForObject (ctx , testEnv .Client , otherClusterRole )).To (Succeed ())
1083+ Expect (testEnv .Create (context .TODO (), legacyClusterRole )).To (Succeed ())
1084+ Expect (waitForObject (ctx , testEnv .Client , legacyClusterRole )).To (Succeed ())
1085+
1086+ currentClusterProfile := & configv1beta1.ClusterProfile {}
1087+ Expect (testEnv .Get (context .TODO (),
1088+ types.NamespacedName {Name : clusterProfile .Name },
1089+ currentClusterProfile )).To (Succeed ())
1090+
1091+ addOwnerReference (context .TODO (), testEnv .Client , ownClusterRole , currentClusterProfile )
1092+ addOwnerReference (context .TODO (), testEnv .Client , otherClusterRole , currentClusterProfile )
1093+ addOwnerReference (context .TODO (), testEnv .Client , legacyClusterRole , currentClusterProfile )
1094+
1095+ currentClusterSummary := & configv1beta1.ClusterSummary {}
1096+ Expect (testEnv .Get (context .TODO (),
1097+ types.NamespacedName {Namespace : clusterSummary .Namespace , Name : clusterSummary .Name },
1098+ currentClusterSummary )).To (Succeed ())
1099+ currentClusterSummary .Status .FeatureSummaries = []configv1beta1.FeatureSummary {
1100+ {
1101+ FeatureID : libsveltosv1beta1 .FeatureResources ,
1102+ Status : libsveltosv1beta1 .FeatureStatusProvisioned ,
1103+ },
1104+ }
1105+ currentClusterSummary .Status .DeployedGVKs = []libsveltosv1beta1.FeatureDeploymentInfo {
1106+ {
1107+ FeatureID : libsveltosv1beta1 .FeatureResources ,
1108+ DeployedGroupVersionKind : []string {
1109+ testClusterRoleKindV1 ,
1110+ },
1111+ },
1112+ }
1113+ Expect (testEnv .Status ().Update (context .TODO (), currentClusterSummary )).To (Succeed ())
1114+
1115+ deployedGKVs := controllers .GetDeployedGroupVersionKinds (currentClusterSummary , libsveltosv1beta1 .FeatureResources )
1116+ Expect (deployedGKVs ).ToNot (BeEmpty ())
1117+
1118+ // None of the ClusterRoles is in currentPolicies (nil), so all are candidates for
1119+ // deletion were it not for the clustersummary-annotation scoping.
1120+ _ , err := controllers .UndeployStaleResources (context .TODO (), false , testEnv .Config , testEnv .Client ,
1121+ libsveltosv1beta1 .FeatureResources , currentClusterSummary , deployedGKVs , nil ,
1122+ textlogger .NewLogger (textlogger .NewConfig ()))
1123+ Expect (err ).To (BeNil ())
1124+
1125+ // Own resource is no longer referenced: it must be removed.
1126+ Eventually (func () bool {
1127+ currentClusterRole := & rbacv1.ClusterRole {}
1128+ err = testEnv .Get (context .TODO (), types.NamespacedName {Name : ownClusterRoleName }, currentClusterRole )
1129+ return err != nil && apierrors .IsNotFound (err )
1130+ }, timeout , pollingInterval ).Should (BeTrue ())
1131+
1132+ // Legacy (pre-annotation) resource is no longer referenced either: it must still be
1133+ // detected as stale and removed.
1134+ Eventually (func () bool {
1135+ currentClusterRole := & rbacv1.ClusterRole {}
1136+ err = testEnv .Get (context .TODO (), types.NamespacedName {Name : legacyClusterRoleName }, currentClusterRole )
1137+ return err != nil && apierrors .IsNotFound (err )
1138+ }, timeout , pollingInterval ).Should (BeTrue ())
1139+
1140+ // Other ClusterSummary's resource must never be touched.
1141+ Consistently (func () error {
1142+ currentClusterRole := & rbacv1.ClusterRole {}
1143+ return testEnv .Get (context .TODO (), types.NamespacedName {Name : otherClusterRoleName }, currentClusterRole )
1144+ }, timeout , pollingInterval ).Should (BeNil ())
1145+ })
1146+
1147+ It (`undeployStaleResources does not remove resources deployed by the other deployment type of the same ClusterSummary` , func () {
1148+ // Further self-managed SveltosCluster scenario: this time both resources were deployed
1149+ // by the SAME ClusterSummary, one via deploymentType Local and one via deploymentType
1150+ // Remote. Because it is the same ClusterSummary, the clustersummary annotation alone
1151+ // cannot tell the two apart once the remote cluster is physically the management
1152+ // cluster. The deploymenttype annotation must protect the Local resource from the
1153+ // Remote cleanup pass, and the Remote resource from the Local cleanup pass.
1154+ localClusterRoleName := randomString ()
1155+ localClusterRole := & rbacv1.ClusterRole {
1156+ ObjectMeta : metav1.ObjectMeta {
1157+ Name : localClusterRoleName ,
1158+ Labels : map [string ]string {
1159+ deployer .ReasonLabel : string (libsveltosv1beta1 .FeatureResources ),
1160+ },
1161+ Annotations : map [string ]string {
1162+ deployer .ReferenceKindAnnotation : string (libsveltosv1beta1 .ConfigMapReferencedResourceKind ),
1163+ deployer .ReferenceNamespaceAnnotation : randomString (),
1164+ deployer .ReferenceNameAnnotation : randomString (),
1165+ controllers .ClusterSummaryAnnotation : controllers .GetClusterSummaryAnnotationValue (clusterSummary ),
1166+ controllers .DeploymentTypeAnnotation : string (configv1beta1 .DeploymentTypeLocal ),
1167+ },
1168+ },
1169+ }
1170+
1171+ remoteClusterRoleName := randomString ()
1172+ remoteClusterRole := & rbacv1.ClusterRole {
1173+ ObjectMeta : metav1.ObjectMeta {
1174+ Name : remoteClusterRoleName ,
1175+ Labels : map [string ]string {
1176+ deployer .ReasonLabel : string (libsveltosv1beta1 .FeatureResources ),
1177+ },
1178+ Annotations : map [string ]string {
1179+ deployer .ReferenceKindAnnotation : string (libsveltosv1beta1 .ConfigMapReferencedResourceKind ),
1180+ deployer .ReferenceNamespaceAnnotation : randomString (),
1181+ deployer .ReferenceNameAnnotation : randomString (),
1182+ controllers .ClusterSummaryAnnotation : controllers .GetClusterSummaryAnnotationValue (clusterSummary ),
1183+ controllers .DeploymentTypeAnnotation : string (configv1beta1 .DeploymentTypeRemote ),
1184+ },
1185+ },
1186+ }
1187+
1188+ Expect (testEnv .Create (context .TODO (), localClusterRole )).To (Succeed ())
1189+ Expect (waitForObject (ctx , testEnv .Client , localClusterRole )).To (Succeed ())
1190+ Expect (testEnv .Create (context .TODO (), remoteClusterRole )).To (Succeed ())
1191+ Expect (waitForObject (ctx , testEnv .Client , remoteClusterRole )).To (Succeed ())
1192+
1193+ currentClusterProfile := & configv1beta1.ClusterProfile {}
1194+ Expect (testEnv .Get (context .TODO (),
1195+ types.NamespacedName {Name : clusterProfile .Name },
1196+ currentClusterProfile )).To (Succeed ())
1197+
1198+ addOwnerReference (context .TODO (), testEnv .Client , localClusterRole , currentClusterProfile )
1199+ addOwnerReference (context .TODO (), testEnv .Client , remoteClusterRole , currentClusterProfile )
1200+
1201+ currentClusterSummary := & configv1beta1.ClusterSummary {}
1202+ Expect (testEnv .Get (context .TODO (),
1203+ types.NamespacedName {Namespace : clusterSummary .Namespace , Name : clusterSummary .Name },
1204+ currentClusterSummary )).To (Succeed ())
1205+ currentClusterSummary .Status .FeatureSummaries = []configv1beta1.FeatureSummary {
1206+ {
1207+ FeatureID : libsveltosv1beta1 .FeatureResources ,
1208+ Status : libsveltosv1beta1 .FeatureStatusProvisioned ,
1209+ },
1210+ }
1211+ currentClusterSummary .Status .DeployedGVKs = []libsveltosv1beta1.FeatureDeploymentInfo {
1212+ {
1213+ FeatureID : libsveltosv1beta1 .FeatureResources ,
1214+ DeployedGroupVersionKind : []string {
1215+ testClusterRoleKindV1 ,
1216+ },
1217+ },
1218+ }
1219+ Expect (testEnv .Status ().Update (context .TODO (), currentClusterSummary )).To (Succeed ())
1220+
1221+ deployedGKVs := controllers .GetDeployedGroupVersionKinds (currentClusterSummary , libsveltosv1beta1 .FeatureResources )
1222+ Expect (deployedGKVs ).ToNot (BeEmpty ())
1223+
1224+ // Neither ClusterRole is in currentPolicies (nil), so both are candidates for deletion
1225+ // were it not for the deploymenttype-annotation scoping.
1226+
1227+ // isMgmtCluster=false simulates the Remote cleanup pass: it must remove the
1228+ // Remote-deployed resource, but leave the Local-deployed one alone.
1229+ _ , err := controllers .UndeployStaleResources (context .TODO (), false , testEnv .Config , testEnv .Client ,
1230+ libsveltosv1beta1 .FeatureResources , currentClusterSummary , deployedGKVs , nil ,
1231+ textlogger .NewLogger (textlogger .NewConfig ()))
1232+ Expect (err ).To (BeNil ())
1233+
1234+ Eventually (func () bool {
1235+ currentClusterRole := & rbacv1.ClusterRole {}
1236+ err = testEnv .Get (context .TODO (), types.NamespacedName {Name : remoteClusterRoleName }, currentClusterRole )
1237+ return err != nil && apierrors .IsNotFound (err )
1238+ }, timeout , pollingInterval ).Should (BeTrue ())
1239+
1240+ Consistently (func () error {
1241+ currentClusterRole := & rbacv1.ClusterRole {}
1242+ return testEnv .Get (context .TODO (), types.NamespacedName {Name : localClusterRoleName }, currentClusterRole )
1243+ }, timeout , pollingInterval ).Should (BeNil ())
1244+
1245+ // isMgmtCluster=true simulates the Local cleanup pass: it must now remove the
1246+ // Local-deployed resource.
1247+ _ , err = controllers .UndeployStaleResources (context .TODO (), true , testEnv .Config , testEnv .Client ,
1248+ libsveltosv1beta1 .FeatureResources , currentClusterSummary , deployedGKVs , nil ,
1249+ textlogger .NewLogger (textlogger .NewConfig ()))
1250+ Expect (err ).To (BeNil ())
1251+
1252+ Eventually (func () bool {
1253+ currentClusterRole := & rbacv1.ClusterRole {}
1254+ err = testEnv .Get (context .TODO (), types.NamespacedName {Name : localClusterRoleName }, currentClusterRole )
1255+ return err != nil && apierrors .IsNotFound (err )
1256+ }, timeout , pollingInterval ).Should (BeTrue ())
1257+ })
1258+
10211259 It ("addExtraLabels adds extra labels on unstructured" , func () {
10221260 u := & unstructured.Unstructured {}
10231261 extraLabels := map [string ]string {
0 commit comments