Skip to content

Commit

Permalink
Disable and hide deletion protection (#1052)
Browse files Browse the repository at this point in the history
Signed-off-by: Jose Vazquez <[email protected]>
  • Loading branch information
josvazg committed Aug 2, 2023
1 parent 5f99ca2 commit 9da259e
Show file tree
Hide file tree
Showing 8 changed files with 181 additions and 55 deletions.
66 changes: 27 additions & 39 deletions cmd/manager/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,11 @@ import (
const (
objectDeletionProtectionFlag = "object-deletion-protection"
subobjectDeletionProtectionFlag = "subobject-deletion-protection"
objectDeletionProtectionDefault = true
subobjectDeletionProtectionDefault = true
objectDeletionProtectionDefault = false
subobjectDeletionProtectionDefault = false

objectDeletionProtectionEnvVar = "UNSUPPORTED_OBJECT_DELETION_PROTECTION"
subobjectDeletionProtectionEnvVar = "UNSUPPORTED_SUBOBJECT_DELETION_PROTECTION"
)

var (
Expand Down Expand Up @@ -237,10 +240,6 @@ func parseConfiguration() Config {
"Enabling this will ensure there is only one active controller manager.")
flag.StringVar(&config.LogLevel, "log-level", "info", "Log level. Available values: debug | info | warn | error | dpanic | panic | fatal")
flag.StringVar(&config.LogEncoder, "log-encoder", "json", "Log encoder. Available values: json | console")
flag.BoolVar(&config.ObjectDeletionProtection, objectDeletionProtectionFlag, true, "Defines the operator will not delete Atlas resource "+
"when a Custom Resource is deleted")
flag.BoolVar(&config.SubObjectDeletionProtection, subobjectDeletionProtectionFlag, true, "Defines that the operator will not overwrite "+
"(and consequently delete) subresources that were not previously created by the operator")
appVersion := flag.Bool("v", false, "prints application version")
flag.Parse()

Expand All @@ -264,7 +263,7 @@ func parseConfiguration() Config {
config.Namespace = watchedNamespace
}

configureDeletionProtectionFlags(&config)
configureDeletionProtection(&config)

return config
}
Expand Down Expand Up @@ -319,45 +318,34 @@ func initCustomZapLogger(level, encoding string) (*zap.Logger, error) {
return cfg.Build()
}

func configureDeletionProtectionFlags(config *Config) {
func configureDeletionProtection(config *Config) {
if config == nil {
return
}
config.ObjectDeletionProtection = objectDeletionProtectionDefault
config.SubObjectDeletionProtection = subobjectDeletionProtectionDefault

objectDeletionSet := false
subObjectDeletionSet := false

flag.Visit(func(f *flag.Flag) {
if f.Name == objectDeletionProtectionFlag {
objectDeletionSet = true
}

if f.Name == subobjectDeletionProtectionFlag {
subObjectDeletionSet = true
}
})
// TODO: replace with the CLI flags at feature completion
enableDeletionProtectionFromEnvVars(config, version.Version)
}

if !objectDeletionSet {
objDeletion := strings.ToLower(os.Getenv("OBJECT_DELETION_PROTECTION"))
switch objDeletion {
case "true":
config.ObjectDeletionProtection = true
case "false":
config.ObjectDeletionProtection = false
default:
config.ObjectDeletionProtection = objectDeletionProtectionDefault
func enableDeletionProtectionFromEnvVars(config *Config, v string) {
if version.IsRelease(v) {
if isOn(os.Getenv(objectDeletionProtectionEnvVar)) ||
isOn(os.Getenv(subobjectDeletionProtectionEnvVar)) {
log.Printf("Deletion Protection feature is not available yet in production releases")
}
return
}

if !subObjectDeletionSet {
objDeletion := strings.ToLower(os.Getenv("SUBOBJECT_DELETION_PROTECTION"))
switch objDeletion {
case "true":
config.SubObjectDeletionProtection = true
case "false":
config.SubObjectDeletionProtection = false
default:
config.SubObjectDeletionProtection = subobjectDeletionProtectionDefault
}
if isOn(os.Getenv(objectDeletionProtectionEnvVar)) {
config.ObjectDeletionProtection = true
}
if isOn(os.Getenv(subobjectDeletionProtectionEnvVar)) {
config.SubObjectDeletionProtection = true
}
}

func isOn(value string) bool {
return strings.ToLower(value) == "on"
}
89 changes: 89 additions & 0 deletions cmd/manager/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package main

import (
"os"
"testing"

"github.com/stretchr/testify/assert"

"github.com/mongodb/mongodb-atlas-kubernetes/pkg/version"
)

const (
nonReleaseVersion = "1.8.0-30-g81233c6-dirty"
releaseVersion = "1.9.0-certified"
)

func TestDeletionProtectionDisabledByDefault(t *testing.T) {
os.Unsetenv(objectDeletionProtectionEnvVar)
os.Unsetenv(subobjectDeletionProtectionEnvVar)

cfg := Config{
ObjectDeletionProtection: objectDeletionProtectionDefault,
SubObjectDeletionProtection: subobjectDeletionProtectionDefault,
}
enableDeletionProtectionFromEnvVars(&cfg, version.DefaultVersion)

assert.Equal(t, false, cfg.ObjectDeletionProtection)
assert.Equal(t, false, cfg.SubObjectDeletionProtection)
}

func TestDeletionProtectionIgnoredOnReleases(t *testing.T) {
version.Version = releaseVersion
os.Setenv(objectDeletionProtectionEnvVar, "On")
os.Setenv(subobjectDeletionProtectionEnvVar, "On")

cfg := Config{
ObjectDeletionProtection: objectDeletionProtectionDefault,
SubObjectDeletionProtection: subobjectDeletionProtectionDefault,
}
enableDeletionProtectionFromEnvVars(&cfg, releaseVersion)

assert.Equal(t, false, cfg.ObjectDeletionProtection)
assert.Equal(t, false, cfg.SubObjectDeletionProtection)
}

func TestDeletionProtectionEnabledAsEnvVars(t *testing.T) {
testCases := []struct {
title string
objDelProtect bool
subObjDelProtect bool
}{
{
"both env vars set on non release version enables both protections",
true,
true,
},
{
"obj env var set on non release version enables obj protection only",
true,
false,
},
{
"subobj env var set on non release version enables subobj protection only",
false,
true,
},
}
for _, tc := range testCases {
t.Run(tc.title, func(t *testing.T) {
os.Unsetenv(objectDeletionProtectionEnvVar)
os.Unsetenv(subobjectDeletionProtectionEnvVar)
if tc.objDelProtect {
os.Setenv(objectDeletionProtectionEnvVar, "On")
}
if tc.subObjDelProtect {
os.Setenv(subobjectDeletionProtectionEnvVar, "On")
}

cfg := Config{
ObjectDeletionProtection: objectDeletionProtectionDefault,
SubObjectDeletionProtection: subobjectDeletionProtectionDefault,
}
enableDeletionProtectionFromEnvVars(&cfg, nonReleaseVersion)

assert.Equal(t, tc.objDelProtect, cfg.ObjectDeletionProtection)
assert.Equal(t, tc.subObjDelProtect, cfg.SubObjectDeletionProtection)
})
}
}
3 changes: 2 additions & 1 deletion pkg/api/v1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 13 additions & 1 deletion pkg/version/version.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
package version

import (
"regexp"
"strings"
)

const DefaultVersion = "unknown"

// Version set by the linker during link time.
var Version = "unknown"
var Version = DefaultVersion

func IsRelease(v string) bool {
return v != DefaultVersion &&
regexp.MustCompile(`^[0-9]+\.[0-9]+\.[0-9]+[-certified]*$`).Match([]byte(strings.TrimSpace(v)))
}
48 changes: 48 additions & 0 deletions pkg/version/version_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package version_test

import (
"testing"

"github.com/stretchr/testify/assert"

"github.com/mongodb/mongodb-atlas-kubernetes/pkg/version"
)

func TestReleaseVersion(t *testing.T) {
testCases := []struct {
title string
version string
expected bool
}{
{
"default is not release",
version.DefaultVersion,
false,
},
{
"empty is not release",
"",
false,
},
{
"dirty is not release",
"1.8.0-30-g81233c6-dirty",
false,
},
{
"semver IS release",
"1.8.0",
true,
},
{
"semver certified IS release",
"1.8.0-certified",
true,
},
}
for _, tc := range testCases {
t.Run(tc.title, func(t *testing.T) {
assert.Equal(t, tc.expected, version.IsRelease(tc.version))
})
}
}
8 changes: 0 additions & 8 deletions test/e2e/cli/helm/helm.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,6 @@ func InstallOperatorWideSubmodule(input model.UserInputs) {
"atlas-operator-"+input.Project.GetProjectName(),
config.AtlasOperatorHelmChartPath,
"--set-string", fmt.Sprintf("atlasURI=%s", config.AtlasHost),
"--set", "objectDeletionProtection=false",
"--set", "subobjectDeletionProtection=false",
"--set-string", fmt.Sprintf("image.repository=%s", repo),
"--set-string", fmt.Sprintf("image.tag=%s", tag),
"--namespace", input.Namespace,
Expand All @@ -124,8 +122,6 @@ func InstallOperatorNamespacedFromLatestRelease(input model.UserInputs) {
"mongodb/mongodb-atlas-operator",
"--set", fmt.Sprintf("watchNamespaces={%s}", input.Namespace),
"--set-string", fmt.Sprintf("atlasURI=%s", config.AtlasHost),
"--set", "objectDeletionProtection=false",
"--set", "subobjectDeletionProtection=false",
"--namespace="+input.Namespace,
"--create-namespace",
)
Expand All @@ -145,8 +141,6 @@ func InstallOperatorNamespacedSubmodule(input model.UserInputs) {
"--set-string", fmt.Sprintf("image.tag=%s", tag),
"--set", fmt.Sprintf("watchNamespaces={%s}", input.Namespace),
"--set", "mongodb-atlas-operator-crds.enabled=false",
"--set", "objectDeletionProtection=false",
"--set", "subobjectDeletionProtection=false",
"--namespace="+input.Namespace,
"--create-namespace",
)
Expand Down Expand Up @@ -194,8 +188,6 @@ func UpgradeOperatorChart(input model.UserInputs) {
"atlas-operator-"+input.Project.GetProjectName(),
config.AtlasOperatorHelmChartPath,
"--set-string", fmt.Sprintf("atlasURI=%s", config.AtlasHost),
"--set", "objectDeletionProtection=false",
"--set", "subobjectDeletionProtection=false",
"--set-string", fmt.Sprintf("image.repository=%s", repo),
"--set-string", fmt.Sprintf("image.tag=%s", tag),
"-n", input.Namespace,
Expand Down
6 changes: 1 addition & 5 deletions test/e2e/helm_chart_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package e2e_test

import (
"context"
"encoding/json"
"fmt"
"os"
Expand Down Expand Up @@ -299,10 +298,7 @@ func deleteDeploymentAndOperator(data *model.TestDataProvider) {
helm.Uninstall(data.Resources.Deployments[0].Spec.GetDeploymentName(), data.Resources.Namespace)
Eventually(
func(g Gomega) {
if atlasClient.IsProjectExists(g, data.Resources.ProjectID) {
_, err := atlasClient.Client.Projects.Delete(context.TODO(), data.Resources.ProjectID)
g.Expect(err).To(BeNil())
}
atlasClient.IsProjectExists(g, data.Resources.ProjectID)
},
"7m", "20s",
).Should(Succeed(), "Project and deployment should be deleted from Atlas")
Expand Down

0 comments on commit 9da259e

Please sign in to comment.