diff --git a/pkg/resolver/cluster.go b/pkg/resolver/cluster.go index d0bdca40..7f5f938c 100644 --- a/pkg/resolver/cluster.go +++ b/pkg/resolver/cluster.go @@ -47,7 +47,29 @@ func (r *Resolver) PopulateClusterDefaults(cluster *multigresv1alpha1.MultigresC cluster.Spec.TemplateDefaults.ShardTemplate = FallbackShardTemplate } - // 3. Default Inline Configs (Deep Defaulting) + // 3. Smart Defaulting: System Catalog + // If no databases are defined, inject the mandatory system database "postgres". + if len(cluster.Spec.Databases) == 0 { + cluster.Spec.Databases = append(cluster.Spec.Databases, multigresv1alpha1.DatabaseConfig{ + Name: DefaultSystemDatabaseName, + Default: true, + }) + } + + // If any database has no tablegroups, inject the mandatory default tablegroup "default". + for i := range cluster.Spec.Databases { + if len(cluster.Spec.Databases[i].TableGroups) == 0 { + cluster.Spec.Databases[i].TableGroups = append( + cluster.Spec.Databases[i].TableGroups, + multigresv1alpha1.TableGroupConfig{ + Name: DefaultSystemTableGroupName, + Default: true, + }, + ) + } + } + + // 4. Default Inline Configs (Deep Defaulting) // We ONLY default these if the user explicitly provided the block (Inline). // We DO NOT fetch templates here, adhering to the "Non-Goal" of the design doc. diff --git a/pkg/resolver/cluster_test.go b/pkg/resolver/cluster_test.go index cf18a749..3af174dd 100644 --- a/pkg/resolver/cluster_test.go +++ b/pkg/resolver/cluster_test.go @@ -46,6 +46,19 @@ func TestResolver_PopulateClusterDefaults(t *testing.T) { CellTemplate: FallbackCellTemplate, ShardTemplate: FallbackShardTemplate, }, + // Expect Smart Defaulting: System Catalog + Databases: []multigresv1alpha1.DatabaseConfig{ + { + Name: DefaultSystemDatabaseName, + Default: true, + TableGroups: []multigresv1alpha1.TableGroupConfig{ + { + Name: DefaultSystemTableGroupName, + Default: true, + }, + }, + }, + }, }, }, }, @@ -65,6 +78,13 @@ func TestResolver_PopulateClusterDefaults(t *testing.T) { CellTemplate: "custom-cell", ShardTemplate: "custom-shard", }, + Databases: []multigresv1alpha1.DatabaseConfig{ + { + Name: "existing-db", + Default: true, + TableGroups: []multigresv1alpha1.TableGroupConfig{{Name: "tg1"}}, + }, + }, }, }, want: &multigresv1alpha1.MultigresCluster{ @@ -82,6 +102,13 @@ func TestResolver_PopulateClusterDefaults(t *testing.T) { CellTemplate: "custom-cell", ShardTemplate: "custom-shard", }, + Databases: []multigresv1alpha1.DatabaseConfig{ + { + Name: "existing-db", + Default: true, + TableGroups: []multigresv1alpha1.TableGroupConfig{{Name: "tg1"}}, + }, + }, }, }, }, @@ -111,6 +138,19 @@ func TestResolver_PopulateClusterDefaults(t *testing.T) { CellTemplate: FallbackCellTemplate, ShardTemplate: FallbackShardTemplate, }, + // Expect Smart Defaulting for DBs + Databases: []multigresv1alpha1.DatabaseConfig{ + { + Name: DefaultSystemDatabaseName, + Default: true, + TableGroups: []multigresv1alpha1.TableGroupConfig{ + { + Name: DefaultSystemTableGroupName, + Default: true, + }, + }, + }, + }, GlobalTopoServer: multigresv1alpha1.GlobalTopoServerSpec{ Etcd: &multigresv1alpha1.EtcdSpec{ Image: DefaultEtcdImage, @@ -156,6 +196,19 @@ func TestResolver_PopulateClusterDefaults(t *testing.T) { CellTemplate: FallbackCellTemplate, ShardTemplate: FallbackShardTemplate, }, + // Expect Smart Defaulting for DBs + Databases: []multigresv1alpha1.DatabaseConfig{ + { + Name: DefaultSystemDatabaseName, + Default: true, + TableGroups: []multigresv1alpha1.TableGroupConfig{ + { + Name: DefaultSystemTableGroupName, + Default: true, + }, + }, + }, + }, Cells: []multigresv1alpha1.CellConfig{ { Name: "cell-1", @@ -195,6 +248,19 @@ func TestResolver_PopulateClusterDefaults(t *testing.T) { CellTemplate: FallbackCellTemplate, ShardTemplate: FallbackShardTemplate, }, + // Expect Smart Defaulting for DBs + Databases: []multigresv1alpha1.DatabaseConfig{ + { + Name: DefaultSystemDatabaseName, + Default: true, + TableGroups: []multigresv1alpha1.TableGroupConfig{ + { + Name: DefaultSystemTableGroupName, + Default: true, + }, + }, + }, + }, GlobalTopoServer: multigresv1alpha1.GlobalTopoServerSpec{ Etcd: &multigresv1alpha1.EtcdSpec{ Image: "custom-etcd", @@ -206,6 +272,44 @@ func TestResolver_PopulateClusterDefaults(t *testing.T) { }, }, }, + "Smart Defaulting: Inject TableGroup when DB exists": { + input: &multigresv1alpha1.MultigresCluster{ + Spec: multigresv1alpha1.MultigresClusterSpec{ + Databases: []multigresv1alpha1.DatabaseConfig{ + {Name: "postgres", Default: true}, // No TableGroups + }, + }, + }, + want: &multigresv1alpha1.MultigresCluster{ + Spec: multigresv1alpha1.MultigresClusterSpec{ + Images: multigresv1alpha1.ClusterImages{ + Postgres: DefaultPostgresImage, + MultiAdmin: DefaultMultiAdminImage, + MultiOrch: DefaultMultiOrchImage, + MultiPooler: DefaultMultiPoolerImage, + MultiGateway: DefaultMultiGatewayImage, + ImagePullPolicy: DefaultImagePullPolicy, + }, + TemplateDefaults: multigresv1alpha1.TemplateDefaults{ + CoreTemplate: FallbackCoreTemplate, + CellTemplate: FallbackCellTemplate, + ShardTemplate: FallbackShardTemplate, + }, + Databases: []multigresv1alpha1.DatabaseConfig{ + { + Name: "postgres", + Default: true, + TableGroups: []multigresv1alpha1.TableGroupConfig{ + { + Name: DefaultSystemTableGroupName, + Default: true, + }, + }, + }, + }, + }, + }, + }, } for name, tc := range tests { diff --git a/pkg/resolver/defaults.go b/pkg/resolver/defaults.go index e7811cae..cebc3f91 100644 --- a/pkg/resolver/defaults.go +++ b/pkg/resolver/defaults.go @@ -21,6 +21,12 @@ const ( // FallbackShardTemplate is the name of the template to look for if no specific ShardTemplate is referenced. FallbackShardTemplate = "default" + // DefaultSystemDatabaseName is the name of the mandatory system database. + DefaultSystemDatabaseName = "postgres" + + // DefaultSystemTableGroupName is the name of the mandatory default table group. + DefaultSystemTableGroupName = "default" + // DefaultPostgresImage is the default container image used for PostgreSQL instances. DefaultPostgresImage = "postgres:15-alpine" diff --git a/pkg/resolver/go.mod b/pkg/resolver/go.mod index e00a7c35..bb858e80 100644 --- a/pkg/resolver/go.mod +++ b/pkg/resolver/go.mod @@ -4,7 +4,7 @@ go 1.25.0 require ( github.com/google/go-cmp v0.7.0 - github.com/numtide/multigres-operator/api v0.0.0-20251224124005-355869230728 + github.com/numtide/multigres-operator/api v0.0.0-20260103121224-057738b43b3b k8s.io/api v0.34.3 k8s.io/apimachinery v0.34.3 k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 diff --git a/pkg/resolver/go.sum b/pkg/resolver/go.sum index c1b3cd8e..295eea9f 100644 --- a/pkg/resolver/go.sum +++ b/pkg/resolver/go.sum @@ -82,6 +82,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/numtide/multigres-operator/api v0.0.0-20251224124005-355869230728 h1:a+iM4L0nC7kUgcAD1P+wuNJg4x0rjcy6rVZ3+vF52Cc= github.com/numtide/multigres-operator/api v0.0.0-20251224124005-355869230728/go.mod h1:A1bBmTxHr+362dGZ5G6u2S4xsP6enbgdUS/UJUOmKbc= +github.com/numtide/multigres-operator/api v0.0.0-20260103121224-057738b43b3b h1:twErev/AdyVQvlYa5vbo5wqq+NlexZJmIm0pwEQd5qI= +github.com/numtide/multigres-operator/api v0.0.0-20260103121224-057738b43b3b/go.mod h1:A1bBmTxHr+362dGZ5G6u2S4xsP6enbgdUS/UJUOmKbc= github.com/onsi/ginkgo/v2 v2.25.3 h1:Ty8+Yi/ayDAGtk4XxmmfUy4GabvM+MegeB4cDLRi6nw= github.com/onsi/ginkgo/v2 v2.25.3/go.mod h1:43uiyQC4Ed2tkOzLsEYm7hnrb7UJTWHYNsuy3bG/snE= github.com/onsi/gomega v1.38.3 h1:eTX+W6dobAYfFeGC2PV6RwXRu/MyT+cQguijutvkpSM=