freeleaps-ops/operators/freeleaps-gitops-initializer/internal/helm/generator.go
zhenyus 0e256f8708 chore(git): code staging
Signed-off-by: zhenyus <zhenyus@mathmast.com>
2025-02-17 14:02:49 +08:00

217 lines
5.8 KiB
Go

package helm
import (
"embed"
"errors"
"fmt"
"io"
"os"
"text/template"
"freeleaps.com/gitops/initializer/api/v1alpha1"
)
//go:embed templates/*
var embeddedTemplates embed.FS
type HelmGenerator struct {
io.Closer
Instance *v1alpha1.ProjectInitialize
workingDir string
}
const leftDelim = "[["
const rightDelim = "]]"
func (hg *HelmGenerator) prepareWorkingDir() error {
path, err := os.MkdirTemp("", fmt.Sprintf("helm-gen-%s-*", hg.Instance.Name))
if err != nil {
return fmt.Errorf("failed to create helm generator temp dir: %w", err)
}
hg.workingDir = path
return nil
}
func (hg *HelmGenerator) readTemplate(name string) (*template.Template, error) {
tpl := template.New(name)
templateBytes, err := embeddedTemplates.ReadFile(name)
if err != nil {
return nil, fmt.Errorf("failed to read template %s: %w", name, err)
}
tpl, err = tpl.
Delims(leftDelim, rightDelim).
Funcs(funcMap).
Parse(string(templateBytes))
if err != nil {
return nil, fmt.Errorf("failed to parse template %s: %w", name, err)
}
return tpl, nil
}
func (hg *HelmGenerator) createMetaFiles() error {
// create Chart.yaml
tpl, err := hg.readTemplate("templates/metadata/Chart.yaml.tpl")
if err != nil {
return err
}
chartYamlFd, err := os.Create(hg.workingDir + "/Chart.yaml")
if err != nil {
return fmt.Errorf("failed to create Chart.yaml file: %w", err)
}
defer chartYamlFd.Close()
if err := tpl.Execute(chartYamlFd, hg.Instance.Spec.Helm); err != nil {
return fmt.Errorf("failed to rendering Chart.yaml with template: %w", err)
}
// create values.yaml
tpl, err = hg.readTemplate("templates/metadata/values.yaml.tpl")
if err != nil {
return err
}
valuesYamlFd, err := os.Create(hg.workingDir + "/values.yaml")
if err != nil {
return fmt.Errorf("failed to create values.yaml file: %w", err)
}
defer valuesYamlFd.Close()
if err := tpl.Execute(valuesYamlFd, hg.Instance.Spec.Helm); err != nil {
return fmt.Errorf("failed to rendering values.yaml with template: %w", err)
}
return nil
}
func (hg *HelmGenerator) createComponentManifests(component v1alpha1.HelmComponentSpec) error {
workRoot := hg.workingDir + "/templates/" + component.Name
if err := os.MkdirAll(workRoot, 0755); err != nil {
return fmt.Errorf("failed to create component directory: %w", err)
}
// create deployment.yaml
tpl, err := hg.readTemplate("templates/deployment/deployment.yaml.tpl")
if err != nil {
return err
}
deploymentYamlFd, err := os.Create(workRoot + "/deployment.yaml")
if err != nil {
return fmt.Errorf("failed to create deployment.yaml file: %w", err)
}
defer deploymentYamlFd.Close()
if err := tpl.Execute(deploymentYamlFd, component); err != nil {
return fmt.Errorf("failed to rendering deployment.yaml with template: %w", err)
}
// create service.yaml if there has services
if len(component.Services) > 0 {
tpl, err = hg.readTemplate("templates/service/service.yaml.tpl")
if err != nil {
return err
}
serviceYamlFd, err := os.Create(workRoot + "/service.yaml")
if err != nil {
return fmt.Errorf("failed to create service.yaml file: %w", err)
}
defer serviceYamlFd.Close()
if err := tpl.Execute(serviceYamlFd, component); err != nil {
return fmt.Errorf("failed to rendering service.yaml with template: %w", err)
}
}
// create ingress.yaml if there has ingress
if len(component.Ingresses) > 0 {
tpl, err = hg.readTemplate("templates/ingress/ingress.yaml.tpl")
if err != nil {
return err
}
ingressYamlFd, err := os.Create(workRoot + "/ingress.yaml")
if err != nil {
return fmt.Errorf("failed to create ingress.yaml file: %w", err)
}
defer ingressYamlFd.Close()
if err := tpl.Execute(ingressYamlFd, component); err != nil {
return fmt.Errorf("failed to rendering ingress.yaml with template: %w", err)
}
}
// create secret.yaml if there has configs
if len(component.Configs) > 0 {
for _, config := range component.Configs {
tpl, err = hg.readTemplate("templates/secret/secret.yaml.tpl")
if err != nil {
return err
}
secretYamlFd, err := os.Create(workRoot + "/" + config.Name + ".yaml")
if err != nil {
return fmt.Errorf("failed to create secret.yaml file: %w", err)
}
defer secretYamlFd.Close()
if err := tpl.Execute(secretYamlFd, struct {
ComponentName string
Config v1alpha1.HelmComponentConfigSpec
}{
ComponentName: component.Name,
Config: config,
}); err != nil {
return fmt.Errorf("failed to rendering secret.yaml with template: %w", err)
}
}
}
// create certificate if ingress need to sign with tls
if len(component.Ingresses) > 0 {
needSign := false
for _, ingress := range component.Ingresses {
if !ingress.TLS.Exists {
needSign = true
break
}
}
if needSign {
tpl, err = hg.readTemplate("templates/certificate/certificate.yaml.tpl")
if err != nil {
return err
}
certificateYamlFd, err := os.Create(workRoot + "/certificate.yaml")
if err != nil {
return fmt.Errorf("failed to create certificate.yaml file: %w", err)
}
defer certificateYamlFd.Close()
if err := tpl.Execute(certificateYamlFd, component); err != nil {
return fmt.Errorf("failed to rendering certificate.yaml with template: %w", err)
}
}
}
return nil
}
func (hg *HelmGenerator) Generate() error {
if hg.Instance == nil {
return errors.New("instance is nil")
}
// create temp working dir
if err := hg.prepareWorkingDir(); err != nil {
return err
}
// create meta files (Chart.yaml, values.yaml, .helmignore)
if err := hg.createMetaFiles(); err != nil {
return err
}
// create manifests for each component
for _, componentSpec := range hg.Instance.Spec.Helm.Components {
if err := hg.createComponentManifests(componentSpec); err != nil {
return err
}
}
return nil
}
func (hg *HelmGenerator) Close() error {
if hg.workingDir != "" {
return os.RemoveAll(hg.workingDir)
}
return nil
}