@@ -8,40 +8,74 @@ import (
88 "k8s.io/apimachinery/pkg/types"
99
1010 multigresv1alpha1 "github.com/numtide/multigres-operator/api/v1alpha1"
11+ corev1 "k8s.io/api/core/v1"
1112)
1213
13- // ResolveCellTemplate fetches and resolves a CellTemplate by name, handling defaults.
14+ // ResolveCell determines the final configuration for a specific Cell.
15+ // It orchestrates: Template Lookup -> Fetch -> Merge -> Defaulting.
16+ func (r * Resolver ) ResolveCell (
17+ ctx context.Context ,
18+ cellSpec * multigresv1alpha1.CellConfig ,
19+ ) (* multigresv1alpha1.StatelessSpec , * multigresv1alpha1.LocalTopoServerSpec , error ) {
20+ // 1. Fetch Template (Logic handles defaults)
21+ templateName := cellSpec .CellTemplate
22+ tpl , err := r .ResolveCellTemplate (ctx , templateName )
23+ if err != nil {
24+ return nil , nil , err
25+ }
26+
27+ // 2. Merge Logic
28+ gateway , localTopo := mergeCellConfig (tpl , cellSpec .Overrides , cellSpec .Spec )
29+
30+ // 3. Apply Deep Defaults (Level 4)
31+ // We use empty resources for Gateway default, as the specific values are often deployment-dependent,
32+ // but we must ensure Replicas is at least 1.
33+ defaultStatelessSpec (gateway , corev1.ResourceRequirements {}, 1 )
34+
35+ // Note: We do NOT default LocalTopo here because it is optional.
36+ // If it is nil, it remains nil (meaning the cell uses Global Topo).
37+ // If it is non-nil (e.g. from template), we apply Etcd defaults.
38+ if localTopo != nil && localTopo .Etcd != nil {
39+ defaultEtcdSpec (localTopo .Etcd )
40+ }
41+
42+ return gateway , localTopo , nil
43+ }
44+
45+ // ResolveCellTemplate fetches and resolves a CellTemplate by name.
46+ // If name is empty, it resolves using the Cluster Defaults, then the Namespace Default.
1447func (r * Resolver ) ResolveCellTemplate (
1548 ctx context.Context ,
16- templateName string ,
49+ name string ,
1750) (* multigresv1alpha1.CellTemplate , error ) {
18- name := templateName
51+ resolvedName := name
1952 isImplicitFallback := false
2053
21- if name == "" {
22- name = r .TemplateDefaults .CellTemplate
54+ if resolvedName == "" {
55+ resolvedName = r .TemplateDefaults .CellTemplate
2356 }
24- if name == "" {
25- name = FallbackCellTemplate
57+ if resolvedName == "" {
58+ resolvedName = FallbackCellTemplate
2659 isImplicitFallback = true
2760 }
2861
2962 tpl := & multigresv1alpha1.CellTemplate {}
30- err := r .Client .Get (ctx , types.NamespacedName {Name : name , Namespace : r .Namespace }, tpl )
63+ err := r .Client .Get (ctx , types.NamespacedName {Name : resolvedName , Namespace : r .Namespace }, tpl )
3164 if err != nil {
3265 if errors .IsNotFound (err ) {
3366 if isImplicitFallback {
67+ // We return an empty struct instead of nil to satisfy tests expecting non-nil structure.
3468 return & multigresv1alpha1.CellTemplate {}, nil
3569 }
36- return nil , fmt .Errorf ("referenced CellTemplate '%s' not found: %w" , name , err )
70+ return nil , fmt .Errorf ("referenced CellTemplate '%s' not found: %w" , resolvedName , err )
3771 }
3872 return nil , fmt .Errorf ("failed to get CellTemplate: %w" , err )
3973 }
4074 return tpl , nil
4175}
4276
43- // MergeCellConfig merges a template spec with overrides and an inline spec to produce the final configuration .
44- func MergeCellConfig (
77+ // mergeCellConfig merges a template spec with overrides and an inline spec.
78+ func mergeCellConfig (
4579 template * multigresv1alpha1.CellTemplate ,
4680 overrides * multigresv1alpha1.CellOverrides ,
4781 inline * multigresv1alpha1.CellInlineSpec ,
@@ -65,6 +99,10 @@ func MergeCellConfig(
6599 }
66100
67101 if inline != nil {
102+ // Inline spec completely replaces the template for the components it defines
103+ // However, for Multigres 'Spec' blocks, usually 'Spec' is exclusive to 'TemplateRef'.
104+ // The design allows "Inline Spec" OR "Template + Overrides".
105+ // If Inline Spec is present, we generally prefer it entirely.
68106 gw := inline .MultiGateway .DeepCopy ()
69107 var topo * multigresv1alpha1.LocalTopoServerSpec
70108 if inline .LocalTopoServer != nil {
0 commit comments