Skip to content

Commit d3efa56

Browse files
authored
CLOUDP-175080: Deletion protection for project resources (final) (#1100)
1 parent ac56de3 commit d3efa56

14 files changed

+3076
-97
lines changed

pkg/controller/atlasproject/atlasproject_controller.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -309,32 +309,32 @@ func (r *AtlasProjectReconciler) ensureProjectResources(ctx context.Context, wor
309309
}
310310
results = append(results, result)
311311

312-
if result = ensureMaintenanceWindow(workflowCtx, project.ID(), project); result.IsOk() {
312+
if result = ensureMaintenanceWindow(ctx, workflowCtx, project, r.SubObjectDeletionProtection); result.IsOk() {
313313
r.EventRecorder.Event(project, "Normal", string(status.MaintenanceWindowReadyType), "")
314314
}
315315
results = append(results, result)
316316

317-
if result = r.ensureEncryptionAtRest(workflowCtx, project.ID(), project); result.IsOk() {
317+
if result = r.ensureEncryptionAtRest(ctx, workflowCtx, project, r.SubObjectDeletionProtection); result.IsOk() {
318318
r.EventRecorder.Event(project, "Normal", string(status.EncryptionAtRestReadyType), "")
319319
}
320320
results = append(results, result)
321321

322-
if result = ensureAuditing(workflowCtx, project.ID(), project); result.IsOk() {
322+
if result = ensureAuditing(ctx, workflowCtx, project, r.SubObjectDeletionProtection); result.IsOk() {
323323
r.EventRecorder.Event(project, "Normal", string(status.AuditingReadyType), "")
324324
}
325325
results = append(results, result)
326326

327-
if result = ensureProjectSettings(workflowCtx, project.ID(), project); result.IsOk() {
327+
if result = ensureProjectSettings(ctx, workflowCtx, project, r.SubObjectDeletionProtection); result.IsOk() {
328328
r.EventRecorder.Event(project, "Normal", string(status.ProjectSettingsReadyType), "")
329329
}
330330
results = append(results, result)
331331

332-
if result = ensureCustomRoles(workflowCtx, project.ID(), project); result.IsOk() {
332+
if result = ensureCustomRoles(ctx, workflowCtx, project, r.SubObjectDeletionProtection); result.IsOk() {
333333
r.EventRecorder.Event(project, "Normal", string(status.ProjectCustomRolesReadyType), "")
334334
}
335335
results = append(results, result)
336336

337-
if result = r.ensureAssignedTeams(workflowCtx, project.ID(), project); result.IsOk() {
337+
if result = r.ensureAssignedTeams(ctx, workflowCtx, project, r.SubObjectDeletionProtection); result.IsOk() {
338338
r.EventRecorder.Event(project, "Normal", string(status.ProjectTeamsReadyType), "")
339339
}
340340
results = append(results, result)

pkg/controller/atlasproject/auditing.go

Lines changed: 68 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@ package atlasproject
22

33
import (
44
"context"
5+
"encoding/json"
6+
"fmt"
57
"reflect"
68

9+
"github.com/mongodb/mongodb-atlas-kubernetes/pkg/controller/customresource"
10+
711
"go.mongodb.org/atlas/mongodbatlas"
812

913
v1 "github.com/mongodb/mongodb-atlas-kubernetes/pkg/api/v1"
@@ -12,19 +16,37 @@ import (
1216
"github.com/mongodb/mongodb-atlas-kubernetes/pkg/util/toptr"
1317
)
1418

15-
func ensureAuditing(ctx *workflow.Context, projectID string, project *v1.AtlasProject) workflow.Result {
16-
result := createOrDeleteAuditing(ctx, projectID, project)
19+
func ensureAuditing(ctx context.Context, workflowCtx *workflow.Context, project *v1.AtlasProject, protected bool) workflow.Result {
20+
canReconcile, err := canAuditingReconcile(ctx, workflowCtx.Client, protected, project)
21+
if err != nil {
22+
result := workflow.Terminate(workflow.Internal, fmt.Sprintf("unable to resolve ownership for deletion protection: %s", err))
23+
workflowCtx.SetConditionFromResult(status.AuditingReadyType, result)
24+
25+
return result
26+
}
27+
28+
if !canReconcile {
29+
result := workflow.Terminate(
30+
workflow.AtlasDeletionProtection,
31+
"unable to reconcile Auditing due to deletion protection being enabled. see https://dochub.mongodb.org/core/ako-deletion-protection for further information",
32+
)
33+
workflowCtx.SetConditionFromResult(status.AuditingReadyType, result)
34+
35+
return result
36+
}
37+
38+
result := createOrDeleteAuditing(workflowCtx, project.ID(), project)
1739
if !result.IsOk() {
18-
ctx.SetConditionFromResult(status.AuditingReadyType, result)
40+
workflowCtx.SetConditionFromResult(status.AuditingReadyType, result)
1941
return result
2042
}
2143

2244
if isAuditingEmpty(project.Spec.Auditing) {
23-
ctx.UnsetCondition(status.AuditingReadyType)
45+
workflowCtx.UnsetCondition(status.AuditingReadyType)
2446
return workflow.OK()
2547
}
2648

27-
ctx.SetConditionTrue(status.AuditingReadyType)
49+
workflowCtx.SetConditionTrue(status.AuditingReadyType)
2850
return workflow.OK()
2951
}
3052

@@ -59,12 +81,24 @@ func auditingInSync(atlas *mongodbatlas.Auditing, spec *v1.Auditing) bool {
5981
return true
6082
}
6183

62-
if isAuditingEmpty(atlas) || isAuditingEmpty(spec) {
63-
return false
84+
specAsAtlas := &mongodbatlas.Auditing{
85+
AuditAuthorizationSuccess: toptr.MakePtr(false),
86+
Enabled: toptr.MakePtr(false),
87+
}
88+
89+
if !isAuditingEmpty(spec) {
90+
specAsAtlas = spec.ToAtlas()
91+
}
92+
93+
if isAuditingEmpty(atlas) {
94+
atlas = &mongodbatlas.Auditing{
95+
AuditAuthorizationSuccess: toptr.MakePtr(false),
96+
Enabled: toptr.MakePtr(false),
97+
}
6498
}
6599

66-
specAsAtlas := spec.ToAtlas()
67100
removeConfigurationType(atlas)
101+
68102
return reflect.DeepEqual(atlas, specAsAtlas)
69103
}
70104

@@ -85,3 +119,29 @@ func patchAuditing(ctx *workflow.Context, projectID string, auditing *mongodbatl
85119
_, _, err := ctx.Client.Auditing.Configure(context.Background(), projectID, auditing)
86120
return err
87121
}
122+
123+
func canAuditingReconcile(ctx context.Context, atlasClient mongodbatlas.Client, protected bool, akoProject *v1.AtlasProject) (bool, error) {
124+
if !protected {
125+
return true, nil
126+
}
127+
128+
latestConfig := &v1.AtlasProjectSpec{}
129+
latestConfigString, ok := akoProject.Annotations[customresource.AnnotationLastAppliedConfiguration]
130+
if ok {
131+
if err := json.Unmarshal([]byte(latestConfigString), latestConfig); err != nil {
132+
return false, err
133+
}
134+
}
135+
136+
auditing, _, err := atlasClient.Auditing.Get(ctx, akoProject.ID())
137+
if err != nil {
138+
return false, err
139+
}
140+
141+
if isAuditingEmpty(auditing) {
142+
return true, nil
143+
}
144+
145+
return auditingInSync(auditing, latestConfig.Auditing) ||
146+
auditingInSync(auditing, akoProject.Spec.Auditing), nil
147+
}

0 commit comments

Comments
 (0)