Files
Go.Rig-Operator/deploy/rig-operator/internal/builder/master.go
2026-01-15 09:58:01 +00:00

135 lines
3.9 KiB
Go

package builder
import (
"context"
"encoding/json"
"fmt"
"gopkg.in/yaml.v3"
"vanderlande.com/ittp/appstack/rig-operator/api/v1alpha1"
"vanderlande.com/ittp/appstack/rig-operator/internal/provider"
)
// ChartConfig holds the helm settings extracted from the YAML _defaults
// The Controller needs this to know WHICH chart to fetch.
type ChartConfig struct {
Repo string
Name string
Version string
}
type MasterBuilder struct {
strategy provider.Strategy
baseTemplate []byte
chartConfig ChartConfig
}
func NewMasterBuilder(strategy provider.Strategy, baseTemplate []byte) *MasterBuilder {
b := &MasterBuilder{
strategy: strategy,
baseTemplate: baseTemplate,
// Safe defaults
chartConfig: ChartConfig{
Name: "oci://ghcr.io/rancherfederal/charts/rancher-cluster-templates",
},
}
return b
}
// GetChartConfig returns the chart details found in the template.
func (b *MasterBuilder) GetChartConfig() ChartConfig {
return b.chartConfig
}
// Build orchestrates the values generation process
func (b *MasterBuilder) Build(ctx context.Context, cbp *v1alpha1.ClusterBlueprint, credentialSecret string) (map[string]interface{}, error) {
values := make(map[string]interface{})
if err := yaml.Unmarshal(b.baseTemplate, &values); err != nil {
return nil, fmt.Errorf("failed to unmarshal base template: %w", err)
}
// 1. Extract Chart Config from _defaults (Legacy Logic Ported)
// We do this so the Controller knows what version to install.
if defaults, ok := values["_defaults"].(map[string]interface{}); ok {
if chartCfg, ok := defaults["helmChart"].(map[string]interface{}); ok {
if v, ok := chartCfg["repo"].(string); ok {
b.chartConfig.Repo = v
}
if v, ok := chartCfg["name"].(string); ok {
b.chartConfig.Name = v
}
if v, ok := chartCfg["version"].(string); ok {
b.chartConfig.Version = v
}
}
}
// 2. Generate Node Pools (Delegated to Strategy)
// [DIFFERENCE]: We don't loop here. The Strategy knows how to map CBP -> Provider NodePools.
nodePools, err := b.strategy.GenerateNodePools(ctx, cbp)
if err != nil {
return nil, fmt.Errorf("strategy failed to generate node pools: %w", err)
}
// 3. Get Global Overrides (Delegated to Strategy)
// [DIFFERENCE]: We don't hardcode "cloud_provider_name" here. The Strategy returns it.
overrides, err := b.strategy.GetGlobalOverrides(ctx, cbp, credentialSecret)
if err != nil {
return nil, fmt.Errorf("strategy failed to get global overrides: %w", err)
}
// 4. Inject Logic into the Helm Structure
if clusterMap, ok := values["cluster"].(map[string]interface{}); ok {
clusterMap["name"] = cbp.Name
if configMap, ok := clusterMap["config"].(map[string]interface{}); ok {
configMap["kubernetesVersion"] = cbp.Spec.KubernetesVersion
// Ensure globalConfig exists
if _, ok := configMap["globalConfig"]; !ok {
configMap["globalConfig"] = make(map[string]interface{})
}
globalConfig := configMap["globalConfig"].(map[string]interface{})
// Inject Overrides
for k, v := range overrides {
// A. Handle specific Global Config keys
if k == "cloud_provider_name" || k == "cloud_provider_config" {
globalConfig[k] = v
continue
}
// B. Handle Chart Values (CCM/CSI Addons)
if k == "chartValues" {
if existingChartVals, ok := configMap["chartValues"].(map[string]interface{}); ok {
if newChartVals, ok := v.(map[string]interface{}); ok {
for ck, cv := range newChartVals {
existingChartVals[ck] = cv
}
}
} else {
configMap["chartValues"] = v
}
continue
}
// C. Default: Inject at Root level
values[k] = v
}
}
}
// 5. Inject Node Pools
// We marshal/unmarshal to ensure JSON tags from the Strategy structs are respected
tempJSON, _ := json.Marshal(nodePools)
var cleanNodePools interface{}
_ = json.Unmarshal(tempJSON, &cleanNodePools)
values["nodepools"] = cleanNodePools
// 6. Cleanup internal keys
delete(values, "_defaults")
return values, nil
}