fix: refresh generated template components #8

Merged
eding merged 1 commit from fix/template-refresh-reconcile into main 2026-08-02 21:39:01 +02:00
2 changed files with 138 additions and 24 deletions

View file

@ -70,7 +70,12 @@ var webhookTargetTimeout = 70 * time.Minute
var webhookTargetPollInterval = 2 * time.Second
var templateBaseComponents = []string{"snapshot-crds", "democratic-csi", "cert-manager", "cluster-issuers", "gateway-api", "gateway", "monitoring", "openbao", "external-secrets", "external-dns", "tekton", "tekton-triggers"}
var templateBaseComponents = []string{"snapshot-crds", "democratic-csi", "cert-manager", "cluster-issuers", "gateway-api", "gateway", "monitoring", "openbao", "external-secrets", "cnpg", "external-dns", "tekton", "tekton-triggers"}
var generatedTemplateFiles = map[string]map[string]bool{
"democratic-csi": {"secret.sops.yaml": true},
"openbao": {"unseal.sops.yaml": true},
}
var requiredClusterKustomizations = []string{"snapshot-crds-kustomization.yaml", "democratic-csi-kustomization.yaml", "cert-manager-kustomization.yaml", "cluster-issuers-kustomization.yaml", "gateway-api-kustomization.yaml", "cilium-kustomization.yaml", "cilium-config-kustomization.yaml", "openbao-kustomization.yaml", "external-secrets-kustomization.yaml", "cnpg-kustomization.yaml", "gateway-kustomization.yaml", "external-dns-kustomization.yaml", "cloudflare-tunnel-kustomization.yaml", "monitoring-kustomization.yaml", "tekton-kustomization.yaml", "tekton-triggers-kustomization.yaml", "cicd-manifests-repo.yaml"}
@ -161,19 +166,7 @@ func (r Runner) Run() error {
if err := os.WriteFile(filepath.Join(clusterDir, "democratic-csi-kustomization.yaml"), csiKustomization, 0644); err != nil {
return err
}
if err := copyDir(filepath.Join(cicdTemplateDir, "base", "cilium"), filepath.Join(dir, "base", "cilium"), true); err != nil {
return err
}
if err := copyDir(filepath.Join(cicdTemplateDir, "base", "cilium-config"), filepath.Join(dir, "base", "cilium-config"), true); err != nil {
return err
}
if err := copyDir(filepath.Join(cicdTemplateDir, "base", "democratic-csi"), filepath.Join(dir, "base", "democratic-csi"), true); err != nil {
return err
}
if err := renderCiliumConfig(filepath.Join(dir, "base", "cilium"), r.Config); err != nil {
return err
}
if err := renderCiliumConfig(filepath.Join(dir, "base", "cilium-config"), r.Config); err != nil {
if err := copyAndRenderCiliumBases(cicdTemplateDir, dir, r.Config); err != nil {
return err
}
if err := copyAndRenderDeliveryBases(cicdTemplateDir, dir, r.Config); err != nil {
@ -336,6 +329,19 @@ func copyAndRenderDeliveryBases(templateDir, repoDir string, cfg config.Config)
return nil
}
func copyAndRenderCiliumBases(templateDir, repoDir string, cfg config.Config) error {
for _, base := range []string{"cilium", "cilium-config"} {
baseDir := filepath.Join(repoDir, "base", base)
if err := copyDir(filepath.Join(templateDir, "base", base), baseDir, true); err != nil {
return err
}
if err := renderCiliumConfig(baseDir, cfg); err != nil {
return err
}
}
return nil
}
func writeDemocraticCSISecret(path string, csi config.DemocraticCSIConfig, ageKeyPath string) error {
plaintext, err := renderDemocraticCSISecret(csi)
if err != nil {
@ -645,7 +651,7 @@ func ensureClusterKustomizations(clusterDir string) error {
func copyTemplateBaseComponents(templateDir, repoDir string) error {
for _, component := range templateBaseComponents {
if err := copyDir(filepath.Join(templateDir, "base", component), filepath.Join(repoDir, "base", component), true); err != nil {
if err := copyDirExcept(filepath.Join(templateDir, "base", component), filepath.Join(repoDir, "base", component), true, generatedTemplateFiles[component]); err != nil {
return err
}
}

View file

@ -107,7 +107,7 @@ func TestCopyAndRenderDeliveryBasesOverwritesExistingMigrationOutput(t *testing.
}
}
func TestCopyTemplateBaseComponentsCopiesExternalSecretsConfig(t *testing.T) {
func TestCopyTemplateBaseComponentsRefreshesCNPGAndPreservesGeneratedSecrets(t *testing.T) {
templateDir := t.TempDir()
repoDir := t.TempDir()
for _, component := range templateBaseComponents {
@ -115,24 +115,96 @@ func TestCopyTemplateBaseComponentsCopiesExternalSecretsConfig(t *testing.T) {
t.Fatal(err)
}
}
source := filepath.Join(templateDir, "base", "external-secrets", "config.yaml")
destination := filepath.Join(repoDir, "base", "external-secrets", "config.yaml")
if err := os.WriteFile(source, []byte("store: openbao\n"), 0644); err != nil {
cnpgSource := filepath.Join(templateDir, "base", "cnpg", "kustomization.yaml")
cnpgDestination := filepath.Join(repoDir, "base", "cnpg", "kustomization.yaml")
if err := os.WriteFile(cnpgSource, []byte("resources:\n - operator-kustomization.yaml\n - infrastructure-kustomization.yaml\n"), 0644); err != nil {
t.Fatal(err)
}
if err := os.MkdirAll(filepath.Dir(destination), 0755); err != nil {
if err := os.MkdirAll(filepath.Dir(cnpgDestination), 0755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(destination, []byte("store: stale\n"), 0644); err != nil {
if err := os.WriteFile(cnpgDestination, []byte("resources:\n - release.yaml\n - infrastructure-postgres.yaml\n"), 0644); err != nil {
t.Fatal(err)
}
operatorSource := filepath.Join(templateDir, "base", "cnpg", "operator", "kustomization.yaml")
if err := os.MkdirAll(filepath.Dir(operatorSource), 0755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(operatorSource, []byte("resources:\n - release.yaml\n"), 0644); err != nil {
t.Fatal(err)
}
for _, file := range []string{"democratic-csi/secret.sops.yaml", "openbao/unseal.sops.yaml"} {
if err := os.WriteFile(filepath.Join(templateDir, "base", file), []byte("sops: template\n"), 0600); err != nil {
t.Fatal(err)
}
destination := filepath.Join(repoDir, "base", file)
if err := os.MkdirAll(filepath.Dir(destination), 0755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(destination, []byte("sops: generated\n"), 0600); err != nil {
t.Fatal(err)
}
}
if err := copyTemplateBaseComponents(templateDir, repoDir); err != nil {
t.Fatal(err)
}
content, err := os.ReadFile(destination)
if err != nil || string(content) != "store: openbao\n" {
t.Fatalf("external-secrets configuration was not copied: %q, %v", content, err)
content, err := os.ReadFile(cnpgDestination)
if err != nil || string(content) != "resources:\n - operator-kustomization.yaml\n - infrastructure-kustomization.yaml\n" {
t.Fatalf("CNPG Kustomization was not refreshed: %q, %v", content, err)
}
content, err = os.ReadFile(filepath.Join(repoDir, "base", "cnpg", "operator", "kustomization.yaml"))
if err != nil || string(content) != "resources:\n - release.yaml\n" {
t.Fatalf("CNPG operator child was not copied: %q, %v", content, err)
}
for _, file := range []string{"democratic-csi/secret.sops.yaml", "openbao/unseal.sops.yaml"} {
content, err := os.ReadFile(filepath.Join(repoDir, "base", file))
if err != nil || string(content) != "sops: generated\n" {
t.Fatalf("generated SOPS file %q was overwritten: %q, %v", file, content, err)
}
}
}
func TestCopyAndRenderCiliumBasesRefreshesTemplateWithoutLeavingPlaceholders(t *testing.T) {
templateDir := t.TempDir()
repoDir := t.TempDir()
files := map[string]string{
"cilium/release.yaml": "generation: current\n",
"cilium-config/load-balancer-pool.yaml": "start: ${CILIUM_LB_START}\nstop: ${CILIUM_LB_END}\n",
"cilium-config/l2-policy.yaml": "interface: ${CILIUM_TRAFFIC_INTERFACE}\n",
}
for name, content := range files {
path := filepath.Join(templateDir, "base", name)
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(path, []byte(content), 0644); err != nil {
t.Fatal(err)
}
}
for _, base := range []string{"cilium", "cilium-config"} {
if err := os.MkdirAll(filepath.Join(repoDir, "base", base), 0755); err != nil {
t.Fatal(err)
}
}
stale := filepath.Join(repoDir, "base", "cilium-config", "load-balancer-pool.yaml")
if err := os.WriteFile(stale, []byte("start: stale\n"), 0644); err != nil {
t.Fatal(err)
}
cfg := config.Config{Cilium: config.CiliumConfig{TrafficInterface: "eth1", LoadBalancerStart: "192.168.45.19", LoadBalancerEnd: "192.168.45.30"}}
if err := copyAndRenderCiliumBases(templateDir, repoDir, cfg); err != nil {
t.Fatal(err)
}
for name, want := range map[string]string{
"cilium/release.yaml": "generation: current\n",
"cilium-config/load-balancer-pool.yaml": "start: 192.168.45.19\nstop: 192.168.45.30\n",
"cilium-config/l2-policy.yaml": "interface: eth1\n",
} {
content, err := os.ReadFile(filepath.Join(repoDir, "base", name))
if err != nil || string(content) != want {
t.Fatalf("Cilium file %q was not refreshed and rendered: %q, %v", name, content, err)
}
}
}
@ -418,6 +490,42 @@ func TestCopyDirSkipsGitDirectory(t *testing.T) {
}
}
func TestCopyDirPreservesExistingDeploymentStateWithoutOverwrite(t *testing.T) {
source := t.TempDir()
destination := t.TempDir()
for path, content := range map[string]string{
"apps/production/app.yaml": "template: current\n",
"apps/staging/app.yaml": "template: new\n",
} {
file := filepath.Join(source, path)
if err := os.MkdirAll(filepath.Dir(file), 0755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(file, []byte(content), 0644); err != nil {
t.Fatal(err)
}
}
existing := filepath.Join(destination, "apps", "production", "app.yaml")
if err := os.MkdirAll(filepath.Dir(existing), 0755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(existing, []byte("deployment: existing\n"), 0644); err != nil {
t.Fatal(err)
}
if err := copyDir(source, destination, false); err != nil {
t.Fatal(err)
}
content, err := os.ReadFile(existing)
if err != nil || string(content) != "deployment: existing\n" {
t.Fatalf("existing deployment state was overwritten: %q, %v", content, err)
}
content, err = os.ReadFile(filepath.Join(destination, "apps", "staging", "app.yaml"))
if err != nil || string(content) != "template: new\n" {
t.Fatalf("new template file was not copied: %q, %v", content, err)
}
}
func TestEnsureManifestsKustomizations(t *testing.T) {
dir := t.TempDir()
for _, environment := range []string{"previews", "staging", "production"} {