fix(atlas-operator): add toggle for cluster-wide secret access to the operator binary
Author: AidilRiskiCreated Sep 4, 2026Updated Sep 4, 2026
Summary
Related to https://github.com/ariga/atlas/issues/3789.
After disabling the cluster-wide secret access, the binary fails to start as it tries to watch the secrets. Is it possible to also add a toggle here? Maybe using an environment variable.
It should probably look like this: https://github.com/ariga/atlas-operator/compare/master...AidilRiski:atlas-operator:fix-conditional-secret-watch
Happy to open a PR if you'd accept contributions for this.
Diff
diff --git a/cmd/main.go b/cmd/main.go
index ccc29b8..7db7867 100644
--- a/cmd/main.go
+++ b/cmd/main.go
@@ -66,6 +66,12 @@ const (
prewarmDevDB = "PREWARM_DEVDB"
// allowCustomConfig when enabled it allows the use of custom config
allowsCustomConfig = "ALLOW_CUSTOM_CONFIG"
+ // watchSecrets when enabled it registers Secret informers so the controllers
+ // re-reconcile when referenced Secrets change. Requires RBAC permission to
+ // list/watch Secrets. Disable in environments that provide credentials through
+ // other means (e.g. file-based injection via OpenBao/Vault) and do not grant
+ // the operator Secret access.
+ envWatchSecrets = "WATCH_SECRETS"
)
func init() {
@@ -191,12 +197,16 @@ func main() {
}
prewarmDevDB := getPrewarmDevDBEnv()
allowCustomConfig := getAllowCustomConfigEnv()
+ watchSecrets := getWatchSecretsEnv()
// Setup controller for AtlasSchema
schemaController := controller.NewAtlasSchemaReconciler(mgr, prewarmDevDB)
schemaController.SetAtlasClient(controller.NewAtlasExec)
if allowCustomConfig {
schemaController.AllowCustomConfig()
}
+ if watchSecrets {
+ schemaController.WatchSecrets()
+ }
if err := schemaController.SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "AtlasSchema")
os.Exit(1)
@@ -207,6 +217,9 @@ func main() {
if allowCustomConfig {
migrationController.AllowCustomConfig()
}
+ if watchSecrets {
+ migrationController.WatchSecrets()
+ }
if err = migrationController.SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "AtlasMigration")
os.Exit(1)
@@ -356,3 +369,18 @@ func getAllowCustomConfigEnv() bool {
}
return allowsCustomConfig
}
+
+// getWatchSecretsEnv returns the value of the env var WATCH_SECRETS.
+// if the env var is not set, it returns true (backwards compatible).
+func getWatchSecretsEnv() bool {
+ env := os.Getenv(envWatchSecrets)
+ if env == "" {
+ return true
+ }
+ watchSecrets, err := strconv.ParseBool(env)
+ if err != nil {
+ setupLog.Error(err, "invalid value for env var WATCH_SECRETS, expected true or false")
+ os.Exit(1)
+ }
+ return watchSecrets
+}
diff --git a/internal/controller/atlasmigration_controller.go b/internal/controller/atlasmigration_controller.go
index dcc1c82..c8d3fbe 100644
--- a/internal/controller/atlasmigration_controller.go
+++ b/internal/controller/atlasmigration_controller.go
@@ -68,6 +68,7 @@ type (
devDB *devDBReconciler
// AllowCustomConfig allows the controller to use custom atlas.hcl config.
allowCustomConfig bool
+ watchSecrets bool
}
// migrationData is the data used to render the HCL template
// that will be used for Atlas CLI
@@ -178,15 +179,25 @@ func (r *AtlasMigrationReconciler) storeDirState(ctx context.Context, obj client
// SetupWithManager sets up the controller with the Manager.
func (r *AtlasMigrationReconciler) SetupWithManager(mgr ctrl.Manager) error {
- return ctrl.NewControllerManagedBy(mgr).
+ b := ctrl.NewControllerManagedBy(mgr).
WithOptions(controller.Options{
MaxConcurrentReconciles: runtime.NumCPU(),
}).
For(&dbv1alpha1.AtlasMigration{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})).
Owns(&dbv1alpha1.AtlasMigration{}).
- Watches(&corev1.Secret{}, r.secretWatcher).
- Watches(&corev1.ConfigMap{}, r.configMapWatcher).
- Complete(r)
+ Watches(&corev1.ConfigMap{}, r.configMapWatcher)
+ if r.watchSecrets {
+ b = b.Watches(&corev1.Secret{}, r.secretWatcher)
+ }
+ return b.Complete(r)
+}
+
+// WatchSecrets enables the Secret informer so the controller re-reconciles
+// when a referenced Secret changes. When disabled, the controller skips the
+// cluster-wide Secret LIST/WATCH, avoiding RBAC errors in environments that
+// provide credentials through other means (e.g. file-based injection).
+func (r *AtlasMigrationReconciler) WatchSecrets() {
+ r.watchSecrets = true
}
// SetAtlasClient sets the Atlas client for the reconciler.
diff --git a/internal/controller/atlasschema_controller.go b/internal/controller/atlasschema_controller.go
index f5301c0..05fbf9a 100644
--- a/internal/controller/atlasschema_controller.go
+++ b/internal/controller/atlasschema_controller.go
@@ -67,6 +67,7 @@ type (
devDB *devDBReconciler
// AllowCustomConfig allows the controller to use custom atlas.hcl config.
allowCustomConfig bool
+ watchSecrets bool
}
// managedData contains information about the managed database and its desired state.
managedData struct {
@@ -453,15 +454,25 @@ func (r *AtlasSchemaReconciler) schemaApply(
// SetupWithManager sets up the controller with the Manager.
func (r *AtlasSchemaReconciler) SetupWithManager(mgr ctrl.Manager) error {
- return ctrl.NewControllerManagedBy(mgr).
+ b := ctrl.NewControllerManagedBy(mgr).
WithOptions(controller.Options{
MaxConcurrentReconciles: runtime.NumCPU(),
}).
For(&dbv1alpha1.AtlasSchema{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})).
Owns(&dbv1alpha1.AtlasSchema{}).
- Watches(&corev1.ConfigMap{}, r.configMapWatcher).
- Watches(&corev1.Secret{}, r.secretWatcher).
- Complete(r)
+ Watches(&corev1.ConfigMap{}, r.configMapWatcher)
+ if r.watchSecrets {
+ b = b.Watches(&corev1.Secret{}, r.secretWatcher)
+ }
+ return b.Complete(r)
+}
+
+// WatchSecrets enables the Secret informer so the controller re-reconciles
+// when a referenced Secret changes. When disabled, the controller skips the
+// cluster-wide Secret LIST/WATCH, avoiding RBAC errors in environments that
+// provide credentials through other means (e.g. file-based injection).
+func (r *AtlasSchemaReconciler) WatchSecrets() {
+ r.watchSecrets = true
}
func (r *AtlasSchemaReconciler) watchRefs(res *dbv1alpha1.AtlasSchema) {Source: ariga/atlas