Skip to content

Commit 351ff88

Browse files
CLOUDP-224541: Added terminationProtection flag (#1356)
Added terminationProtection flag & refactored deletion logic --------- Co-authored-by: Sergiusz Urbaniak <sergiusz.urbaniak@gmail.com>
1 parent fd82446 commit 351ff88

15 files changed

+154
-57
lines changed

config/crd/bases/atlas.mongodb.com_atlasdeployments.yaml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,13 @@ spec:
390390
type: object
391391
maxItems: 50
392392
type: array
393+
terminationProtectionEnabled:
394+
default: false
395+
description: Flag that indicates whether termination protection
396+
is enabled on the cluster. If set to true, MongoDB Cloud won't
397+
delete the cluster. If set to false, MongoDB Cloud will delete
398+
the cluster.
399+
type: boolean
393400
versionReleaseSystem:
394401
type: string
395402
type: object
@@ -594,7 +601,10 @@ spec:
594601
type: array
595602
terminationProtectionEnabled:
596603
default: false
597-
description: TerminationProtectionEnabled flag
604+
description: Flag that indicates whether termination protection
605+
is enabled on the cluster. If set to true, MongoDB Cloud won't
606+
delete the cluster. If set to false, MongoDB Cloud will delete
607+
the cluster.
598608
type: boolean
599609
required:
600610
- name

config/crd/bases/atlas.mongodb.com_atlasprojects.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ spec:
157157
types.
158158
type: boolean
159159
flowName:
160-
description: Flowdock flow namse in lower-case letters.
160+
description: Flowdock flow name in lower-case letters.
161161
type: string
162162
flowdockApiTokenRef:
163163
description: The Flowdock personal API token. Populated
@@ -185,7 +185,7 @@ spec:
185185
are sent. Populated for the SMS notifications type.
186186
type: string
187187
opsGenieApiKeyRef:
188-
description: Opsgenie API Key. Populated for the OPS_GENIE
188+
description: OpsGenie API Key. Populated for the OPS_GENIE
189189
notifications type. If the key later becomes invalid,
190190
Atlas sends an email to the project owner and eventually
191191
removes the token.

pkg/api/v1/atlasdeployment_types.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,9 @@ type AdvancedDeploymentSpec struct {
127127
CustomZoneMapping []CustomZoneMapping `json:"customZoneMapping,omitempty"`
128128
// +optional
129129
ManagedNamespaces []ManagedNamespace `json:"managedNamespaces,omitempty"`
130+
// Flag that indicates whether termination protection is enabled on the cluster. If set to true, MongoDB Cloud won't delete the cluster. If set to false, MongoDB Cloud will delete the cluster.
131+
// +kubebuilder:default:=false
132+
TerminationProtectionEnabled bool `json:"terminationProtectionEnabled,omitempty"`
130133
}
131134

132135
// ToAtlas converts the AdvancedDeploymentSpec to native Atlas client ToAtlas format.
@@ -169,7 +172,7 @@ type ServerlessSpec struct {
169172
// Serverless Backup Options
170173
BackupOptions ServerlessBackupOptions `json:"backupOptions,omitempty"`
171174

172-
// TerminationProtectionEnabled flag
175+
// Flag that indicates whether termination protection is enabled on the cluster. If set to true, MongoDB Cloud won't delete the cluster. If set to false, MongoDB Cloud will delete the cluster.
173176
// +kubebuilder:default:=false
174177
TerminationProtectionEnabled bool `json:"terminationProtectionEnabled,omitempty"`
175178
}

pkg/api/v1/atlasdeployment_types_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ func init() {
3333
excludedClusterFieldsTheirs["replicationFactor"] = true
3434

3535
// Termination protection
36-
excludedClusterFieldsTheirs["terminationProtectionEnabled"] = true
36+
// excludedClusterFieldsTheirs["terminationProtectionEnabled"] = true
3737

3838
// Root cert type
3939
excludedClusterFieldsTheirs["rootCertType"] = true

pkg/controller/atlasdatabaseuser/atlasdatabaseuser_controller.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ func (r *AtlasDatabaseUserReconciler) handleDeletion(
244244
}
245245
}
246246

247-
if customresource.IsResourceProtected(dbUser, r.ObjectDeletionProtection) {
247+
if customresource.IsResourcePolicyKeepOrDefault(dbUser, r.ObjectDeletionProtection) {
248248
log.Info("Not removing Atlas database user from Atlas as per configuration")
249249

250250
err := customresource.ManageFinalizer(ctx, r.Client, dbUser, customresource.UnsetFinalizer)

pkg/controller/atlasdatafederation/datafederation_controller.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ func (r *AtlasDataFederationReconciler) Reconcile(context context.Context, req c
156156

157157
if !dataFederation.GetDeletionTimestamp().IsZero() {
158158
if customresource.HaveFinalizer(dataFederation, customresource.FinalizerLabel) {
159-
if customresource.IsResourceProtected(dataFederation, r.ObjectDeletionProtection) {
159+
if customresource.IsResourcePolicyKeepOrDefault(dataFederation, r.ObjectDeletionProtection) {
160160
log.Info("Not removing AtlasDataFederation from Atlas as per configuration")
161161
} else {
162162
if err = r.deleteDataFederationFromAtlas(context, atlasClient, dataFederation, project, log); err != nil {

pkg/controller/atlasdeployment/atlasdeployment_controller.go

Lines changed: 41 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -272,40 +272,51 @@ func (r *AtlasDeploymentReconciler) handleDeletion(
272272
return true, workflow.Terminate(workflow.Internal, err.Error())
273273
}
274274
}
275+
276+
return false, workflow.OK()
275277
}
276278

277-
if !deployment.GetDeletionTimestamp().IsZero() {
278-
if customresource.HaveFinalizer(deployment, customresource.FinalizerLabel) {
279-
if err := r.cleanupBindings(workflowCtx.Context, deployment); err != nil {
280-
result := workflow.Terminate(workflow.Internal, err.Error())
281-
log.Errorw("failed to cleanup deployment bindings (backups)", "error", err)
282-
return true, result
283-
}
284-
isProtected := customresource.IsResourceProtected(deployment, r.ObjectDeletionProtection)
285-
if isProtected {
286-
log.Info("Not removing Atlas deployment from Atlas as per configuration")
287-
} else {
288-
if customresource.ResourceShouldBeLeftInAtlas(deployment) {
289-
log.Infof("Not removing Atlas Deployment from Atlas as the '%s' annotation is set", customresource.ResourcePolicyAnnotation)
290-
} else {
291-
if err := r.deleteDeploymentFromAtlas(workflowCtx, log, project, deployment); err != nil {
292-
log.Errorf("failed to remove deployment from Atlas: %s", err)
293-
result := workflow.Terminate(workflow.Internal, err.Error())
294-
workflowCtx.SetConditionFromResult(status.DeploymentReadyType, result)
295-
return true, result
296-
}
297-
}
298-
}
299-
err := customresource.ManageFinalizer(workflowCtx.Context, r.Client, deployment, customresource.UnsetFinalizer)
300-
if err != nil {
301-
result := workflow.Terminate(workflow.Internal, err.Error())
302-
log.Errorw("failed to remove finalizer", "error", err)
303-
return true, result
304-
}
305-
}
279+
if !customresource.HaveFinalizer(deployment, customresource.FinalizerLabel) {
306280
return true, prevResult
307281
}
308-
return false, workflow.OK()
282+
283+
if err := r.cleanupBindings(workflowCtx.Context, deployment); err != nil {
284+
result := workflow.Terminate(workflow.Internal, err.Error())
285+
log.Errorw("failed to cleanup deployment bindings (backups)", "error", err)
286+
return true, result
287+
}
288+
289+
switch {
290+
case customresource.IsResourcePolicyKeepOrDefault(deployment, r.ObjectDeletionProtection):
291+
log.Info("Not removing Atlas deployment from Atlas as per configuration")
292+
case customresource.IsResourcePolicyKeep(deployment):
293+
log.Infof("Not removing Atlas deployment from Atlas as the '%s' annotation is set", customresource.ResourcePolicyAnnotation)
294+
case isTerminationProtectionEnabled(deployment):
295+
msg := fmt.Sprintf("Termination protection for %s deployment enabled. Deployment in Atlas won't be removed", deployment.GetName())
296+
log.Info(msg)
297+
r.EventRecorder.Event(deployment, "Warning", "AtlasDeploymentTermination", msg)
298+
default:
299+
if err := r.deleteDeploymentFromAtlas(workflowCtx, log, project, deployment); err != nil {
300+
log.Errorf("failed to remove deployment from Atlas: %s", err)
301+
result := workflow.Terminate(workflow.Internal, err.Error())
302+
workflowCtx.SetConditionFromResult(status.DeploymentReadyType, result)
303+
return true, result
304+
}
305+
}
306+
307+
if err := customresource.ManageFinalizer(workflowCtx.Context, r.Client, deployment, customresource.UnsetFinalizer); err != nil {
308+
result := workflow.Terminate(workflow.Internal, err.Error())
309+
log.Errorw("failed to remove finalizer", "error", err)
310+
return true, result
311+
}
312+
313+
return true, prevResult
314+
}
315+
316+
func isTerminationProtectionEnabled(deployment *mdbv1.AtlasDeployment) bool {
317+
return (deployment.Spec.DeploymentSpec != nil &&
318+
deployment.Spec.DeploymentSpec.TerminationProtectionEnabled) || (deployment.Spec.ServerlessSpec != nil &&
319+
deployment.Spec.ServerlessSpec.TerminationProtectionEnabled)
309320
}
310321

311322
func (r *AtlasDeploymentReconciler) cleanupBindings(context context.Context, deployment *mdbv1.AtlasDeployment) error {

pkg/controller/atlasproject/atlasproject_controller.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ func (r *AtlasProjectReconciler) ensureDeletionFinalizer(workflowCtx *workflow.C
241241

242242
if !project.GetDeletionTimestamp().IsZero() {
243243
if customresource.HaveFinalizer(project, customresource.FinalizerLabel) {
244-
if customresource.IsResourceProtected(project, r.ObjectDeletionProtection) {
244+
if customresource.IsResourcePolicyKeepOrDefault(project, r.ObjectDeletionProtection) {
245245
log.Info("Not removing Project from Atlas as per configuration")
246246
result = workflow.OK()
247247
} else {

pkg/controller/atlasproject/team_reconciler.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ func (r *AtlasProjectReconciler) teamReconcile(
123123
if !team.GetDeletionTimestamp().IsZero() {
124124
if customresource.HaveFinalizer(team, customresource.FinalizerLabel) {
125125
log.Warnf("team %s is assigned to a project. Remove it from all projects before delete", team.Name)
126-
} else if customresource.IsResourceProtected(team, r.ObjectDeletionProtection) {
126+
} else if customresource.IsResourcePolicyKeepOrDefault(team, r.ObjectDeletionProtection) {
127127
log.Info("Not removing Team from Atlas as per configuration")
128128
return workflow.OK().ReconcileResult(), nil
129129
} else {

pkg/controller/customresource/customresource.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,16 @@ func MarkReconciliationStarted(client client.Client, resource mdbv1.AtlasCustomR
8383
return ctx
8484
}
8585

86-
// ResourceShouldBeLeftInAtlas returns 'true' if the resource should not be removed from Atlas on K8s resource removal.
87-
func ResourceShouldBeLeftInAtlas(resource mdbv1.AtlasCustomResource) bool {
86+
func IsResourcePolicyKeepOrDefault(resource mdbv1.AtlasCustomResource, protectionFlag bool) bool {
87+
if policy, ok := resource.GetAnnotations()[ResourcePolicyAnnotation]; ok {
88+
return policy == ResourcePolicyKeep
89+
}
90+
91+
return protectionFlag
92+
}
93+
94+
// IsResourcePolicyKeep returns 'true' if the resource should not be removed from Atlas on K8s resource removal.
95+
func IsResourcePolicyKeep(resource mdbv1.AtlasCustomResource) bool {
8896
if v, ok := resource.GetAnnotations()[ResourcePolicyAnnotation]; ok {
8997
return v == ResourcePolicyKeep
9098
}

0 commit comments

Comments
 (0)