@@ -19,6 +19,7 @@ package deployer
1919import (
2020 "context"
2121 "fmt"
22+ "strconv"
2223 "strings"
2324
2425 corev1 "k8s.io/api/core/v1"
@@ -32,21 +33,44 @@ import (
3233)
3334
3435const (
35- // ReferenceLabelKind is added to each policy deployed by a ClusterSummary
36+ // ReferenceKindLabel is added to each policy deployed by a ClusterSummary
3637 // instance to a managed Cluster. Indicates the Kind (ConfigMap or Secret)
3738 // containing the policy.
39+ // Deprecated: replaced by annotation
3840 ReferenceKindLabel = "projectsveltos.io/reference-kind"
3941
4042 // ReferenceNameLabel is added to each policy deployed by a ClusterSummary
4143 // instance to a managed Cluster. Indicates the name of the ConfigMap/Secret
4244 // containing the policy.
45+ // Deprecated: replaced by annotation
4346 ReferenceNameLabel = "projectsveltos.io/reference-name"
4447
4548 // ReferenceNamespaceLabel is added to each policy deployed by a ClusterSummary
4649 // instance to a managed Cluster. Indicates the namespace of the ConfigMap/Secret
4750 // containing the policy.
51+ // Deprecated: replaced by annotation
4852 ReferenceNamespaceLabel = "projectsveltos.io/reference-namespace"
4953
54+ // ReferenceKindAnnotation is added to each policy deployed by a ClusterSummary
55+ // instance to a managed Cluster. Indicates the Kind (ConfigMap or Secret)
56+ // containing the policy.
57+ ReferenceKindAnnotation = "projectsveltos.io/reference-kind"
58+
59+ // ReferenceNameAnnotation is added to each policy deployed by a ClusterSummary
60+ // instance to a managed Cluster. Indicates the name of the ConfigMap/Secret
61+ // containing the policy.
62+ ReferenceNameAnnotation = "projectsveltos.io/reference-name"
63+
64+ // ReferenceNamespaceAnnotation is added to each policy deployed by a ClusterSummary
65+ // instance to a managed Cluster. Indicates the namespace of the ConfigMap/Secret
66+ // containing the policy.
67+ ReferenceNamespaceAnnotation = "projectsveltos.io/reference-namespace"
68+
69+ // ReferenceTierAnnotation is added to each policy deployed by a ClusterSummary
70+ // instance to a managed Cluster. Indicates the namespace of the ConfigMap/Secret
71+ // containing the policy.
72+ ReferenceTierAnnotation = "projectsveltos.io/reference-tier"
73+
5074 // PolicyHash is the annotation set on a policy when deployed in a managed
5175 // cluster.
5276 PolicyHash = "projectsveltos.io/hash"
@@ -129,7 +153,7 @@ func (r *ResourceInfo) GetResourceVersion() string {
129153// If object exists, return value of PolicyHash annotation.
130154func ValidateObjectForUpdate (ctx context.Context , dr dynamic.ResourceInterface ,
131155 object * unstructured.Unstructured , referenceKind , referenceNamespace , referenceName string ,
132- profile client.Object ) (* ResourceInfo , error ) {
156+ referenceTier int32 , profile client.Object ) (* ResourceInfo , error ) {
133157
134158 if object == nil {
135159 return nil , nil
@@ -154,40 +178,27 @@ func ValidateObjectForUpdate(ctx context.Context, dr dynamic.ResourceInterface,
154178 }
155179 }
156180
157- if labels := currentObject .GetLabels (); labels != nil {
158- kind , kindOk := labels [ReferenceKindLabel ]
159- namespace , namespaceOk := labels [ReferenceNamespaceLabel ]
160- name , nameOk := labels [ReferenceNameLabel ]
181+ kind , namespace , name , tier := getReferenceInfo (currentObject )
161182
162- if kindOk {
183+ // if proposed tier is lower than current tier, do not check for conflict on referenced resource.
184+ if referenceTier >= tier {
185+ if kind != "" {
163186 if kind != referenceKind {
164187 return resourceInfo , & ConflictError {
165188 message : fmt .Sprintf ("A conflict was detected while deploying resource %s:%s/%s. %s" +
166189 "This resource is currently deployed because of %s %s/%s.\n " ,
167190 object .GroupVersionKind ().Kind , object .GetNamespace (), object .GetName (),
168191 getOwnerMessage (currentObject ), kind , namespace , name )}
169192 }
170- }
171- if namespaceOk {
193+
172194 if namespace != referenceNamespace {
173195 return resourceInfo , & ConflictError {
174196 message : fmt .Sprintf ("A conflict was detected while deploying resource %s:%s/%s. %s" +
175197 "This resource is currently deployed because of %s %s/%s.\n " ,
176198 object .GroupVersionKind ().Kind , object .GetNamespace (), object .GetName (),
177199 getOwnerMessage (currentObject ), kind , namespace , name )}
178200 }
179- }
180- if nameOk {
181- if name != referenceName {
182- return resourceInfo , & ConflictError {
183- message : fmt .Sprintf ("A conflict was detected while deploying resource %s:%s/%s. %s" +
184- "This resource is currently deployed because of %s %s/%s.\n " ,
185- object .GroupVersionKind ().Kind , object .GetNamespace (), object .GetName (),
186- getOwnerMessage (currentObject ), kind , namespace , name )}
187- }
188- }
189201
190- if nameOk {
191202 if name != referenceName {
192203 return resourceInfo , & ConflictError {
193204 message : fmt .Sprintf ("A conflict was detected while deploying resource %s:%s/%s. %s" +
@@ -196,11 +207,11 @@ func ValidateObjectForUpdate(ctx context.Context, dr dynamic.ResourceInterface,
196207 getOwnerMessage (currentObject ), kind , namespace , name )}
197208 }
198209 }
210+ }
199211
200- err := validateSveltosOwner (object , currentObject , profile , kind , namespace , name )
201- if err != nil {
202- return resourceInfo , & ConflictError {message : err .Error ()}
203- }
212+ err = validateSveltosOwner (object , currentObject , profile , kind , namespace , name )
213+ if err != nil {
214+ return resourceInfo , & ConflictError {message : err .Error ()}
204215 }
205216
206217 // Only in case object exists and there are no conflicts, return hash
@@ -211,6 +222,53 @@ func ValidateObjectForUpdate(ctx context.Context, dr dynamic.ResourceInterface,
211222 return resourceInfo , nil
212223}
213224
225+ // getReferenceInfo extracts the kind, namespace, and name from object annotations
226+ // and falls back to labels if the annotations are missing or the annotation map is nil.
227+ func getReferenceInfo (object * unstructured.Unstructured ) (kind , namespace , name string , tier int32 ) {
228+ // 1. Attempt to get info from Annotations
229+ annotations := object .GetAnnotations ()
230+ if annotations != nil {
231+ kind = annotations [ReferenceKindAnnotation ]
232+ namespace = annotations [ReferenceNamespaceAnnotation ]
233+ name = annotations [ReferenceNameAnnotation ]
234+
235+ const defaultTier = 100
236+ tier := int32 (defaultTier )
237+ tierStr , tierOk := annotations [ReferenceTierAnnotation ]
238+ if tierOk {
239+ tier64 , err := strconv .ParseInt (tierStr , 10 , 32 )
240+ if err == nil {
241+ tier = int32 (tier64 )
242+ }
243+ // If ParseInt fails, 'tier' remains the DefaultTier (100)
244+ }
245+
246+ // If we found the kind, we assume the annotation set is complete enough.
247+ if kind != "" {
248+ return kind , namespace , name , tier
249+ }
250+ }
251+
252+ // 2. Fallback to Labels if annotations were nil OR kind was not found in annotations
253+ labels := object .GetLabels ()
254+ if labels != nil {
255+ var kindOk bool
256+
257+ // NOTE: You had a bug in your original prompt's fallback logic where it
258+ // still referenced 'annotations'. This is corrected here to use 'labels'.
259+ kind , kindOk = labels [ReferenceKindLabel ]
260+ namespace = labels [ReferenceNamespaceLabel ]
261+ name = labels [ReferenceNameLabel ]
262+
263+ if kindOk {
264+ return kind , namespace , name , tier
265+ }
266+ }
267+
268+ // 3. Info not found
269+ return "" , "" , "" , tier
270+ }
271+
214272func validateSveltosOwner (object , currentObject * unstructured.Unstructured , profile client.Object ,
215273 kind , namespace , name string ) error {
216274
0 commit comments