From bdac64473a7e1a12daa42f6f48c6cd1873df2a9e Mon Sep 17 00:00:00 2001 From: Robert Graeff Date: Tue, 9 Sep 2025 17:06:37 +0200 Subject: [PATCH 1/4] feat: deploy flux command --- cmd/deploy_flux.go | 81 ++++--- docs/technical/flux-deployer.md | 50 ++++ internal/flux_deployer/constants.go | 8 +- internal/flux_deployer/deployer.go | 281 +++++++++++++++------- internal/flux_deployer/deployer_test.go | 19 +- internal/flux_deployer/git_credentials.go | 3 +- internal/flux_deployer/templating.go | 99 ++++++++ internal/template/template_input.go | 57 +++++ 8 files changed, 468 insertions(+), 130 deletions(-) create mode 100644 docs/technical/flux-deployer.md create mode 100644 internal/flux_deployer/templating.go create mode 100644 internal/template/template_input.go diff --git a/cmd/deploy_flux.go b/cmd/deploy_flux.go index 4470dda..e9c75c9 100644 --- a/cmd/deploy_flux.go +++ b/cmd/deploy_flux.go @@ -3,41 +3,53 @@ package cmd import ( "fmt" - "github.com/openmcp-project/controller-utils/pkg/clusters" "github.com/spf13/cobra" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime" + cfg "github.com/openmcp-project/bootstrapper/internal/config" "github.com/openmcp-project/bootstrapper/internal/flux_deployer" logging "github.com/openmcp-project/bootstrapper/internal/log" -) - -const ( - flagOCMConfig = "ocm-config" - flagGitCredentials = "git-credentials" - flagKubeconfig = "kubeconfig" - flagFluxCDNamespace = "fluxcd-namespace" + "github.com/openmcp-project/bootstrapper/internal/util" ) // deployFluxCmd represents the "deploy flux" command var deployFluxCmd = &cobra.Command{ - Use: "deploy-flux source target", - Short: "Transfer an OCM component from a source to a target location", - Long: `Transfers the specified OCM component version from the source location to the target location.`, - Args: cobra.ExactArgs(5), + Use: "deploy-flux", + Short: "Deploys Flux controllers on the platform cluster, and establishes synchronization with a Git repository", + Long: `Deploys Flux controllers on the platform cluster, and establishes synchronization with a Git repository.`, + Args: cobra.ExactArgs(1), ArgAliases: []string{ - "component-location", - "deployment-templates", - "deployment-repository", - "deployment-repository-branch", - "deployment-repository-path", + "configFile", }, RunE: func(cmd *cobra.Command, args []string) error { + configFilePath := args[0] + log := logging.GetLogger() - log.Infof("Starting flux deployment with component-location: %s, deployment-templates: %s, "+ - "deployment-repository: %s, deployment-repository-branch: %s, deployment-repository-path: %s", - args[0], args[1], args[2], args[3], args[4]) + log.Infof("Starting deployment of Flux controllers with config file: %s.", configFilePath) - platformKubeconfig := cmd.Flag(flagKubeconfig).Value.String() - platformCluster := clusters.New("platform").WithConfigPath(platformKubeconfig) + // Configuration + config := &cfg.BootstrapperConfig{} + err := config.ReadFromFile(configFilePath) + if err != nil { + return fmt.Errorf("failed to read config file: %w", err) + } + config.SetDefaults() + err = config.Validate() + if err != nil { + return fmt.Errorf("invalid config file: %w", err) + } + + // Platform cluster + scheme := runtime.NewScheme() + if err := v1.AddToScheme(scheme); err != nil { + return fmt.Errorf("error adding corev1 to scheme: %w", err) + } + + platformCluster, err := util.GetCluster(cmd.Flag(FlagKubeConfig).Value.String(), "platform", scheme) + if err != nil { + return fmt.Errorf("failed to get platform cluster: %w", err) + } if err := platformCluster.InitializeRESTConfig(); err != nil { return fmt.Errorf("error initializing REST config for platform cluster: %w", err) } @@ -45,28 +57,25 @@ var deployFluxCmd = &cobra.Command{ return fmt.Errorf("error initializing client for platform cluster: %w", err) } - d := flux_deployer.NewFluxDeployer(args[0], args[1], args[2], args[3], args[4], - cmd.Flag(flagOCMConfig).Value.String(), - cmd.Flag(flagGitCredentials).Value.String(), - cmd.Flag(flagFluxCDNamespace).Value.String(), - platformKubeconfig, - platformCluster, log) - err := d.Deploy(cmd.Context()) - if err != nil { - log.Errorf("Flux deployment failed: %v", err) + d := flux_deployer.NewFluxDeployer(config, cmd.Flag(FlagGitConfig).Value.String(), cmd.Flag(FlagOcmConfig).Value.String(), platformCluster, log) + if err = d.Deploy(cmd.Context()); err != nil { + log.Errorf("Deployment of flux controllers failed: %v", err) return err } - log.Info("Flux deployment completed") + log.Info("Deployment of flux controllers completed") return nil }, } func init() { RootCmd.AddCommand(deployFluxCmd) + deployFluxCmd.Flags().SortFlags = false + deployFluxCmd.Flags().String(FlagOcmConfig, "", "OCM configuration file") + deployFluxCmd.Flags().String(FlagGitConfig, "", "Git credentials configuration file that configures basic auth or ssh private key. This will be used in the fluxcd GitSource for spec.secretRef to authenticate against the deploymentRepository. If not set, no authentication will be configured.") + deployFluxCmd.Flags().String(FlagKubeConfig, "", "Kubernetes configuration file") - deployFluxCmd.Flags().StringP(flagOCMConfig, "c", "", "ocm configuration file") - deployFluxCmd.Flags().StringP(flagGitCredentials, "g", "", "git credentials configuration file that configures basic auth, personal access token, ssh private key. This will be used in the fluxcd GitSource for spec.secretRef to authenticate against the deploymentRepository. If not set, no authentication will be configured.") - deployFluxCmd.Flags().StringP(flagKubeconfig, "k", "", "kubeconfig of the Kubernetes cluster on which the flux deployment will be created/updated. If not set, the current context will be used.") - deployFluxCmd.Flags().StringP(flagFluxCDNamespace, "n", "", "namespace on the Kubernetes cluster in which the namespaced fluxcd resources will be deployed. Default 'flux-system'.") + if err := deployFluxCmd.MarkFlagRequired(FlagGitConfig); err != nil { + panic(err) + } } diff --git a/docs/technical/flux-deployer.md b/docs/technical/flux-deployer.md new file mode 100644 index 0000000..5b35056 --- /dev/null +++ b/docs/technical/flux-deployer.md @@ -0,0 +1,50 @@ +# Flux Deployer + +The flux deployer creates a temporary working directory with subdirectories `download`, `templates`, and `repo`. + +The final directory structure looks like this: + +```shell +WORKDIR +├── download +│ ├── Chart.yaml +│ ├── templates +│ │ ├── overlays +│ │ │ ├── flux-kustomization.yaml +│ │ │ ├── gitrepo.yaml +│ │ │ └── kustomization.yaml +│ │ └── resources +│ │ ├── components.yaml +│ │ ├── flux-kustomization.yaml +│ │ ├── gitrepo.yaml +│ │ └── kustomization.yaml +│ └── values.yaml +│ +├── templates +│ ├── envs +│ │ └── dev +│ │ └── fluxcd +│ │ ├── flux-kustomization.yaml +│ │ ├── gitrepo.yaml +│ │ └── kustomization.yaml +│ └── resources +│ └── fluxcd +│ ├── components.yaml +│ ├── flux-kustomization.yaml +│ ├── gitrepo.yaml +│ └── kustomization.yaml +│ +└── repo # same structure as in WORKDIR/templates + ├── envs + │ └── dev + │ └── fluxcd # entry point for the kustomization + │ ├── flux-kustomization.yaml + │ ├── gitrepo.yaml + │ └── kustomization.yaml + └── resources + └── fluxcd + ├── components.yaml + ├── flux-kustomization.yaml + ├── gitrepo.yaml + └── kustomization.yaml +``` diff --git a/internal/flux_deployer/constants.go b/internal/flux_deployer/constants.go index 3d5f681..b50db21 100644 --- a/internal/flux_deployer/constants.go +++ b/internal/flux_deployer/constants.go @@ -1,8 +1,14 @@ package flux_deployer const ( - // Names of ocm resources of the root component + // FluxSystemNamespace is the namespace on the platform cluster in which the flux controllers are deployed. + FluxSystemNamespace = "flux-system" + + // GitSecretName is the name of the secret in the flux system namespace that contains the git credentials for accessing the deployment repository. + // The secret is references in the GitRepository resource which establishes the synchronization with the deployment git repository. + GitSecretName = "git" + // Names of ocm resources of the root component FluxcdHelmController = "fluxcd-helm-controller" FluxcdKustomizeController = "fluxcd-kustomize-controller" FluxcdSourceController = "fluxcd-source-controller" diff --git a/internal/flux_deployer/deployer.go b/internal/flux_deployer/deployer.go index d526276..2b23362 100644 --- a/internal/flux_deployer/deployer.go +++ b/internal/flux_deployer/deployer.go @@ -5,166 +5,271 @@ import ( "fmt" "os" "path" + "path/filepath" "github.com/openmcp-project/controller-utils/pkg/clusters" "github.com/sirupsen/logrus" + "sigs.k8s.io/kustomize/api/krusty" + "sigs.k8s.io/kustomize/kyaml/filesys" + cfg "github.com/openmcp-project/bootstrapper/internal/config" ocmcli "github.com/openmcp-project/bootstrapper/internal/ocm-cli" "github.com/openmcp-project/bootstrapper/internal/template" "github.com/openmcp-project/bootstrapper/internal/util" ) +const ( + EnvsDirectoryName = "envs" + FluxCDDirectoryName = "fluxcd" + ResourcesDirectoryName = "resources" + TemplatesDirectoryName = "templates" + OverlaysDirectoryName = "overlays" + + FluxCDSourceControllerResourceName = "fluxcd-source-controller" + FluxCDKustomizationControllerResourceName = "fluxcd-kustomize-controller" + FluxCDHelmControllerResourceName = "fluxcd-helm-controller" +) + type FluxDeployer struct { - componentLocation string - - // deploymentTemplates is the path to the deployment templates in the templates component. - // It consists of segments separated by slashes. All segments except the last one are names of component references. - // The last segment is the name of a resource in the component, which is reached by starting at the root component and going through the component references. - // For example, "gitops-templates/fluxcd" means: start at the root component, go to the component reference "gitops-templates", and then use the resource named "fluxcd" in that component. - deploymentTemplates string - deploymentRepository string - deploymentRepositoryBranch string - deploymentRepositoryPath string - ocmConfig string - gitCredentials string - fluxcdNamespace string - platformKubeconfig string - platformCluster *clusters.Cluster - log *logrus.Logger -} + Config *cfg.BootstrapperConfig + + // GitConfigPath is the path to the Git configuration file + GitConfigPath string + // OcmConfigPath is the path to the OCM configuration file + OcmConfigPath string + + platformCluster *clusters.Cluster + fluxNamespace string + // fluxcdCV is the component version of the fluxcd source controller component + fluxcdCV *ocmcli.ComponentVersion + log *logrus.Logger -func NewFluxDeployer(componentLocation, deploymentTemplates, deploymentRepository, deploymentRepositoryBranch, deploymentRepositoryPath, - ocmConfig, gitCredentials, fluxcdNamespace, platformKubeconfig string, platformCluster *clusters.Cluster, log *logrus.Logger) *FluxDeployer { + workDir string + downloadDir string + templatesDir string + repoDir string +} +func NewFluxDeployer(config *cfg.BootstrapperConfig, gitConfigPath, ocmConfigPath string, platformCluster *clusters.Cluster, log *logrus.Logger) *FluxDeployer { return &FluxDeployer{ - componentLocation: componentLocation, - deploymentTemplates: deploymentTemplates, - deploymentRepository: deploymentRepository, - deploymentRepositoryBranch: deploymentRepositoryBranch, - deploymentRepositoryPath: deploymentRepositoryPath, - ocmConfig: ocmConfig, - gitCredentials: gitCredentials, - fluxcdNamespace: fluxcdNamespace, - platformKubeconfig: platformKubeconfig, - platformCluster: platformCluster, - log: log, + Config: config, + GitConfigPath: gitConfigPath, + OcmConfigPath: ocmConfigPath, + platformCluster: platformCluster, + fluxNamespace: FluxSystemNamespace, + log: log, } } -func (d *FluxDeployer) Deploy(ctx context.Context) error { +func (d *FluxDeployer) Deploy(ctx context.Context) (err error) { - // Get root component and gitops-templates component. - d.log.Info("Loading root component and gitops-templates component") - componentGetter := ocmcli.NewComponentGetter(d.componentLocation, d.deploymentTemplates, d.ocmConfig) - if err := componentGetter.InitializeComponents(ctx); err != nil { + if err := CreateGitCredentialsSecret(ctx, d.log, d.GitConfigPath, GitSecretName, d.fluxNamespace, d.platformCluster.Client()); err != nil { return err } - // Create a temporary directory to store the downloaded resource - d.log.Info("Creating download directory for gitops-templates") - downloadDir, err := os.MkdirTemp("", "flux-resource-") + // Create temporary working directory + d.log.Info("Creating working directory for gitops-templates") + d.workDir, err = util.CreateTempDir() if err != nil { - return fmt.Errorf("error creating temporary download directory for flux resource: %w", err) + return fmt.Errorf("error creating temporary working directory for flux resource: %w", err) } defer func() { - if err := os.RemoveAll(downloadDir); err != nil { - fmt.Printf("error removing temporary download directory for flux resource: %v\n", err) + err := util.DeleteTempDir(d.workDir) + if err != nil { + fmt.Printf("error removing temporary working directory for flux resource: %v\n", err) } }() - d.log.Debugf("Download directory: %s", downloadDir) + d.log.Tracef("Created working directory: %s", d.workDir) + + d.downloadDir = filepath.Join(d.workDir, "download") + d.log.Tracef("Creating download directory: %s", d.downloadDir) + err = os.MkdirAll(d.downloadDir, 0755) + if err != nil { + return fmt.Errorf("failed to create download directory: %w", err) + } + d.log.Tracef("Created download directory: %s", d.downloadDir) + + d.templatesDir = filepath.Join(d.workDir, "templates") + d.log.Tracef("Creating templates directory: %s", d.templatesDir) + err = os.MkdirAll(d.templatesDir, 0755) + if err != nil { + return fmt.Errorf("failed to create templates directory: %w", err) + } + d.log.Tracef("Created templates directory: %s", d.templatesDir) + + d.repoDir = filepath.Join(d.workDir, "repo") + d.log.Tracef("Creating repo directory: %s", d.repoDir) + err = os.MkdirAll(d.repoDir, 0755) + if err != nil { + return fmt.Errorf("failed to create repo directory: %w", err) + } + d.log.Tracef("Created repo directory: %s", d.repoDir) + + // Get components + // - root component + // - gitops-templates component + // - that contains the image resources for fluxcd source controller component + d.log.Info("Loading root component and gitops-templates component") + componentGetter := ocmcli.NewComponentGetter(d.Config.Component.OpenMCPComponentLocation, d.Config.Component.FluxcdTemplateResourcePath, d.OcmConfigPath) + if err := componentGetter.InitializeComponents(ctx); err != nil { + return err + } + + d.fluxcdCV, err = componentGetter.GetComponentVersionForResourceRecursive(ctx, componentGetter.RootComponentVersion(), FluxCDSourceControllerResourceName) + if err != nil { + return fmt.Errorf("failed to get fluxcd source controller component version: %w", err) + } // Download resource from gitops-templates component into the download directory d.log.Info("Downloading gitops-templates") - if err := componentGetter.DownloadTemplatesResource(ctx, downloadDir); err != nil { + if err := componentGetter.DownloadTemplatesResource(ctx, d.downloadDir); err != nil { return fmt.Errorf("error downloading templates: %w", err) } - if err := d.DeployFluxControllers(ctx, componentGetter.RootComponentVersion(), downloadDir); err != nil { - return fmt.Errorf("error deploying flux controllers: %w", err) + // Copy files from /download to /templates, re-arranging the directory structure as needed for kustomize + if err := d.ArrangeTemplates(); err != nil { + return fmt.Errorf("error arranging templates directory: %w", err) } - if err := d.establishFluxSync(ctx, downloadDir); err != nil { - return fmt.Errorf("error establishing flux synchronization: %w", err) + // Template all files in /templates, and write the result to /repo + if err := d.Template(); err != nil { + return fmt.Errorf("error templating files: %w", err) + } + + // Kustomize /repo/envs//fluxcd + fluxCDEnvDir := filepath.Join(d.repoDir, EnvsDirectoryName, d.Config.Environment, FluxCDDirectoryName) + manifests, err := d.Kustomize(fluxCDEnvDir) + if err != nil { + return fmt.Errorf("error kustomizing templated files: %w", err) + } + + // Apply manifests to the platform cluster + d.log.Info("Applying flux deployment objects") + if err := util.ApplyManifests(ctx, d.platformCluster, manifests); err != nil { + return err } return nil } -func (d *FluxDeployer) DeployFluxControllers(ctx context.Context, rootComponentVersion *ocmcli.ComponentVersion, downloadDir string) error { - d.log.Info("Deploying flux") +// ArrangeTemplates fills the templates directory with the files from the download directory, adjusting the directory structure as needed for the kustomization. +func (d *FluxDeployer) ArrangeTemplates() (err error) { + d.log.Info("Arranging template files") - images, err := GetFluxCDImages(rootComponentVersion) + // Create directory /envs//fluxcd + fluxCDEnvDir := filepath.Join(d.templatesDir, EnvsDirectoryName, d.Config.Environment, FluxCDDirectoryName) + err = os.MkdirAll(fluxCDEnvDir, 0755) if err != nil { - return fmt.Errorf("error getting images for flux controllers: %w", err) + return fmt.Errorf("failed to create fluxcd environment directory: %w", err) } - // Read manifest file - filepath := path.Join(downloadDir, "resources", "gotk-components.yaml") - d.log.Debugf("Reading flux deployment objects from file %s", filepath) - manifestTpl, err := d.readFileContent(filepath) + // Create directory /resources/fluxcd + fluxCDResourcesDir := filepath.Join(d.templatesDir, ResourcesDirectoryName, FluxCDDirectoryName) + err = os.MkdirAll(fluxCDResourcesDir, 0755) if err != nil { - return fmt.Errorf("error reading flux deployment objects from file %s: %w", filepath, err) + return fmt.Errorf("failed to create fluxcd resources directory: %w", err) } - // Template - values := map[string]any{ - "Values": map[string]any{ - "namespace": d.fluxcdNamespace, - "images": images, - }, + d.log.Debug("Copying template files to target directories") + + // copy all files from /templates/overlays to /envs//fluxcd + err = util.CopyDir(filepath.Join(d.downloadDir, TemplatesDirectoryName, OverlaysDirectoryName), fluxCDEnvDir) + if err != nil { + return fmt.Errorf("failed to copy fluxcd overlays: %w", err) } - d.log.Debug("Templating flux deployment objects") - manifest, err := template.NewTemplateExecution().Execute("flux-deployment", string(manifestTpl), values) + + // copy all files from /templates/resources to /resources/fluxcd + err = util.CopyDir(filepath.Join(d.downloadDir, TemplatesDirectoryName, ResourcesDirectoryName), fluxCDResourcesDir) if err != nil { - return fmt.Errorf("error templating flux deployment objects: %w", err) + return fmt.Errorf("failed to copy fluxcd resources: %w", err) } - // Apply - d.log.Debug("Applying flux deployment objects") - if err := util.ApplyManifests(ctx, d.platformCluster, manifest); err != nil { - return err + d.log.Info("Arranged template files") + return nil +} + +// Template templates the files in the templates directory, replacing placeholders with actual values. +// The resulting files are written to the repo directory. +func (d *FluxDeployer) Template() (err error) { + d.log.Infof("Applying templates from %s to deployment repository", d.Config.Component.FluxcdTemplateResourcePath) + templateInput := template.NewTemplateInput() + + templateInput.SetImagePullSecrets(d.Config.ImagePullSecrets) + + templateInput["fluxCDEnvPath"] = "./" + EnvsDirectoryName + "/" + d.Config.Environment + "/" + FluxCDDirectoryName + templateInput["gitRepoEnvBranch"] = d.Config.DeploymentRepository.RepoBranch + templateInput["fluxCDResourcesPath"] = "../../../" + ResourcesDirectoryName + "/" + FluxCDDirectoryName + + templateInput.SetGitRepo(d.Config.DeploymentRepository) + + if err = templateInput.AddImageResource(d.fluxcdCV, FluxCDSourceControllerResourceName, "sourceController"); err != nil { + return fmt.Errorf("failed to apply fluxcd source controller template input: %w", err) + } + if err = templateInput.AddImageResource(d.fluxcdCV, FluxCDKustomizationControllerResourceName, "kustomizeController"); err != nil { + return fmt.Errorf("failed to apply fluxcd kustomize controller template input: %w", err) + } + if err = templateInput.AddImageResource(d.fluxcdCV, FluxCDHelmControllerResourceName, "helmController"); err != nil { + return fmt.Errorf("failed to apply fluxcd helm controller template input: %w", err) + } + + if err = TemplateDirectory(d.templatesDir, templateInput, d.repoDir, d.log); err != nil { + return fmt.Errorf("failed to apply templates from directory %s: %w", d.templatesDir, err) } return nil } -func (d *FluxDeployer) establishFluxSync(ctx context.Context, downloadDir string) error { - d.log.Info("Establishing flux synchronization with deployment repository") +// Kustomize runs kustomize on the given directory and returns the resulting yaml as a byte slice. +func (d *FluxDeployer) Kustomize(dir string) ([]byte, error) { + d.log.Infof("Kustomizing files in directory: %s", dir) + fs := filesys.MakeFsOnDisk() + opts := krusty.MakeDefaultOptions() + kustomizer := krusty.MakeKustomizer(opts) - const secretName = "git" + resourceMap, err := kustomizer.Run(fs, dir) + if err != nil { + return nil, fmt.Errorf("error running kustomization: %w", err) + } - if err := CreateGitCredentialsSecret(ctx, d.log, d.gitCredentials, secretName, d.fluxcdNamespace, d.platformCluster.Client()); err != nil { - return err + resourcesYaml, err := resourceMap.AsYaml() + if err != nil { + return nil, fmt.Errorf("error converting resources to yaml: %w", err) + } + + return resourcesYaml, nil +} + +func (d *FluxDeployer) DeployFluxControllers(ctx context.Context, rootComponentVersion *ocmcli.ComponentVersion, downloadDir string) error { + d.log.Info("Deploying flux") + + images, err := GetFluxCDImages(rootComponentVersion) + if err != nil { + return fmt.Errorf("error getting images for flux controllers: %w", err) } // Read manifest file - filepath := path.Join(downloadDir, "resources", "gotk-sync.yaml") - d.log.Debugf("Reading flux synchronization objects from file %s", filepath) + filepath := path.Join(downloadDir, "resources", "gotk-components.yaml") + d.log.Debugf("Reading flux deployment objects from file %s", filepath) manifestTpl, err := d.readFileContent(filepath) if err != nil { - return fmt.Errorf("error reading manifests for flux sync: %w", err) + return fmt.Errorf("error reading flux deployment objects from file %s: %w", filepath, err) } // Template - d.log.Debug("Templating flux synchronization objects") values := map[string]any{ "Values": map[string]any{ - "namespace": d.fluxcdNamespace, - "git": map[string]any{ - "repoUrl": d.deploymentRepository, - "mainBranch": d.deploymentRepositoryBranch, - "path": d.deploymentRepositoryPath, - "secretName": secretName, - }, + "namespace": d.fluxNamespace, + "images": images, }, } - manifest, err := template.NewTemplateExecution().Execute("flux-sync", string(manifestTpl), values) + d.log.Debug("Templating flux deployment objects") + manifest, err := template.NewTemplateExecution().Execute("flux-deployment", string(manifestTpl), values) if err != nil { - return fmt.Errorf("error templating flux synchronization objects: %w", err) + return fmt.Errorf("error templating flux deployment objects: %w", err) } // Apply - d.log.Debug("Applying flux synchronization objects") + d.log.Debug("Applying flux deployment objects") if err := util.ApplyManifests(ctx, d.platformCluster, manifest); err != nil { return err } diff --git a/internal/flux_deployer/deployer_test.go b/internal/flux_deployer/deployer_test.go index 969fa15..695ad63 100644 --- a/internal/flux_deployer/deployer_test.go +++ b/internal/flux_deployer/deployer_test.go @@ -9,6 +9,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" + cfg "github.com/openmcp-project/bootstrapper/internal/config" "github.com/openmcp-project/bootstrapper/internal/flux_deployer" logging "github.com/openmcp-project/bootstrapper/internal/log" ocmcli "github.com/openmcp-project/bootstrapper/internal/ocm-cli" @@ -21,10 +22,20 @@ func TestDeployFluxController(t *testing.T) { platformClient := fake.NewClientBuilder().Build() platformCluster := clusters.NewTestClusterFromClient("platform", platformClient) - namespace := "flux-system-test" - - d := flux_deployer.NewFluxDeployer("", "", "", - "", "", ocmcli.NoOcmConfig, "", namespace, "", platformCluster, logging.GetLogger()) + namespace := flux_deployer.FluxSystemNamespace + + config := &cfg.BootstrapperConfig{ + Component: cfg.Component{ + OpenMCPComponentLocation: "./testdata/01/root-component-version-1.yaml", + }, + DeploymentRepository: cfg.DeploymentRepository{}, + Providers: cfg.Providers{}, + ImagePullSecrets: nil, + OpenMCPOperator: cfg.OpenMCPOperator{}, + Environment: "", + } + + d := flux_deployer.NewFluxDeployer(config, "", ocmcli.NoOcmConfig, platformCluster, logging.GetLogger()) // Create a deployment err := d.DeployFluxControllers(t.Context(), rootComponentVersion1, downloadDir) diff --git a/internal/flux_deployer/git_credentials.go b/internal/flux_deployer/git_credentials.go index d843f58..fa4c19b 100644 --- a/internal/flux_deployer/git_credentials.go +++ b/internal/flux_deployer/git_credentials.go @@ -26,7 +26,7 @@ const ( // The file should contain a YAML of a map[string]string, whose keys are described // in https://fluxcd.io/flux/components/source/gitrepositories/#secret-reference, e.g. username and password. func CreateGitCredentialsSecret(ctx context.Context, log *logrus.Logger, gitCredentialsPath string, secretName, secretNamespace string, platformClient client.Client) error { - log.Debug("Creating or updating git credentials secret") + log.Infof("Creating/updating git credentials secret %s/%s", secretNamespace, secretName) gitCredentialsData := map[string][]byte{} @@ -80,5 +80,6 @@ func CreateGitCredentialsSecret(ctx context.Context, log *logrus.Logger, gitCred return fmt.Errorf("error creating or updating git credentials secret: %w", err) } + log.Infof("Created/updated git credentials secret %s/%s", secretNamespace, secretName) return nil } diff --git a/internal/flux_deployer/templating.go b/internal/flux_deployer/templating.go new file mode 100644 index 0000000..3b45fe0 --- /dev/null +++ b/internal/flux_deployer/templating.go @@ -0,0 +1,99 @@ +package flux_deployer + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/sirupsen/logrus" + + "github.com/openmcp-project/bootstrapper/internal/template" +) + +// TemplateDirectory processes the template files in the specified directory and writes +// the rendered content to the corresponding files in the Git repository's worktree. +// It uses the provided template directory and Git repository to perform the operations. +func TemplateDirectory(templateDirectory string, templateInput template.TemplateInput, repo string, logger *logrus.Logger) error { + + templateDir, err := os.Open(templateDirectory) + if err != nil { + return fmt.Errorf("failed to open template directory: %w", err) + } + defer func() { + if err = templateDir.Close(); err != nil { + _, _ = fmt.Fprintf(os.Stderr, "failed to close template directory: %v\n", err) + } + }() + + te := template.NewTemplateExecution().WithMissingKeyOption("zero") + + // Recursively walk through all files in the template directory + err = filepath.WalkDir(templateDirectory, func(path string, d os.DirEntry, walkError error) error { + var ( + errInWalk error + + templateFromFile []byte + templateResult []byte + + relativePath string + fileInWorkTree *os.File + ) + + if walkError != nil { + return walkError + } + + relativePath, errInWalk = filepath.Rel(templateDirectory, path) + if errInWalk != nil { + return fmt.Errorf("failed to get relative path for %s: %w", path, errInWalk) + } + pathInWorkTree := filepath.Join(repo, relativePath) + + if d.IsDir() { + err = os.MkdirAll(pathInWorkTree, 0755) + if err != nil { + return fmt.Errorf("failed to create directory %s: %w", path, err) + } + } else { + logger.Debugf("Found template file: %s", relativePath) + + templateFromFile, errInWalk = os.ReadFile(path) + if errInWalk != nil { + return fmt.Errorf("failed to read template file %s: %w", relativePath, err) + } + + wrappedTemplateInput := map[string]interface{}{ + "Values": templateInput, + } + + templateResult, errInWalk = te.Execute(path, string(templateFromFile), wrappedTemplateInput) + if errInWalk != nil { + return fmt.Errorf("failed to execute template %s: %w", relativePath, errInWalk) + } + + fileInWorkTree, errInWalk = os.Create(pathInWorkTree) + if errInWalk != nil { + return fmt.Errorf("failed to open file in worktree %s: %w", relativePath, errInWalk) + } + defer func(pathInRepo *os.File) { + err := pathInRepo.Close() + if err != nil { + _, _ = fmt.Fprintf(os.Stderr, "failed to close file in worktree %s: %v\n", relativePath, err) + } + }(fileInWorkTree) + + _, errInWalk = fileInWorkTree.Write(templateResult) + if errInWalk != nil { + return fmt.Errorf("failed to write to file in worktree %s: %w", relativePath, errInWalk) + } + } + + return nil + }) + + if err != nil { + return fmt.Errorf("failed to walk template directory: %w", err) + } + + return nil +} diff --git a/internal/template/template_input.go b/internal/template/template_input.go new file mode 100644 index 0000000..5ea38ad --- /dev/null +++ b/internal/template/template_input.go @@ -0,0 +1,57 @@ +package template + +import ( + "fmt" + + "github.com/openmcp-project/bootstrapper/internal/config" + ocmcli "github.com/openmcp-project/bootstrapper/internal/ocm-cli" + "github.com/openmcp-project/bootstrapper/internal/util" +) + +func NewTemplateInput() TemplateInput { + return make(TemplateInput) +} + +type TemplateInput map[string]any + +func (t TemplateInput) AddImageResource(cv *ocmcli.ComponentVersion, resourceName, key string) error { + resource, err := cv.GetResource(resourceName) + if err != nil { + return fmt.Errorf("failed to get resource %s: %w", resourceName, err) + } + imageName, imageTag, imageDigest, err := util.ParseImageVersionAndTag(*resource.Access.ImageReference) + if err != nil { + return fmt.Errorf("failed to parse image reference %s: %w", *resource.Access.ImageReference, err) + } + + if _, found := t["images"]; !found { + t["images"] = make(map[string]any) + } + t["images"].(map[string]any)[key] = map[string]any{ + "version": imageTag, + "image": imageName, + "tag": imageTag, + "digest": imageDigest, + } + return nil +} + +func (t TemplateInput) SetImagePullSecrets(imagePullSecrets []string) { + if len(imagePullSecrets) == 0 { + return + } + + t["imagePullSecrets"] = make([]map[string]string, 0, len(imagePullSecrets)) + for _, secret := range imagePullSecrets { + t["imagePullSecrets"] = append(t["imagePullSecrets"].([]map[string]string), map[string]string{ + "name": secret, + }) + } +} + +func (t TemplateInput) SetGitRepo(repo config.DeploymentRepository) { + t["git"] = map[string]interface{}{ + "repoUrl": repo.RepoURL, + "mainBranch": repo.RepoBranch, + } +} From 9f428d358b35d6fce397a70e214bccbd1c357f00 Mon Sep 17 00:00:00 2001 From: Robert Graeff Date: Thu, 11 Sep 2025 09:11:21 +0200 Subject: [PATCH 2/4] feat: template input --- internal/common/constants.go | 13 +++++ .../{template => common}/template_input.go | 52 ++++++++++++------- internal/flux_deployer/deployer.go | 41 ++++----------- internal/flux_deployer/templating.go | 9 ++-- 4 files changed, 60 insertions(+), 55 deletions(-) create mode 100644 internal/common/constants.go rename internal/{template => common}/template_input.go (52%) diff --git a/internal/common/constants.go b/internal/common/constants.go new file mode 100644 index 0000000..2e4ac6b --- /dev/null +++ b/internal/common/constants.go @@ -0,0 +1,13 @@ +package common + +const ( + EnvsDirectoryName = "envs" + FluxCDDirectoryName = "fluxcd" + ResourcesDirectoryName = "resources" + TemplatesDirectoryName = "templates" + OverlaysDirectoryName = "overlays" + + FluxCDSourceControllerResourceName = "fluxcd-source-controller" + FluxCDKustomizationControllerResourceName = "fluxcd-kustomize-controller" + FluxCDHelmControllerResourceName = "fluxcd-helm-controller" +) diff --git a/internal/template/template_input.go b/internal/common/template_input.go similarity index 52% rename from internal/template/template_input.go rename to internal/common/template_input.go index 5ea38ad..7961e25 100644 --- a/internal/template/template_input.go +++ b/internal/common/template_input.go @@ -1,4 +1,4 @@ -package template +package common import ( "fmt" @@ -8,11 +8,39 @@ import ( "github.com/openmcp-project/bootstrapper/internal/util" ) +type TemplateInput map[string]any + func NewTemplateInput() TemplateInput { return make(TemplateInput) } -type TemplateInput map[string]any +func NewTemplateInputFromConfig(c *config.BootstrapperConfig) TemplateInput { + t := TemplateInput{ + "fluxCDEnvPath": "./" + EnvsDirectoryName + "/" + c.Environment + "/" + FluxCDDirectoryName, + "fluxCDResourcesPath": "../../../" + ResourcesDirectoryName + "/" + FluxCDDirectoryName, + + "git": map[string]interface{}{ + "repoUrl": c.DeploymentRepository.RepoURL, + "mainBranch": c.DeploymentRepository.RepoBranch, + }, + "gitRepoEnvBranch": c.DeploymentRepository.RepoBranch, + + "imagePullSecrets": func() []map[string]string { + if len(c.ImagePullSecrets) == 0 { + return nil + } + secrets := make([]map[string]string, 0, len(c.ImagePullSecrets)) + for _, secret := range c.ImagePullSecrets { + secrets = append(secrets, map[string]string{ + "name": secret, + }) + } + return secrets + }(), + } + + return t +} func (t TemplateInput) AddImageResource(cv *ocmcli.ComponentVersion, resourceName, key string) error { resource, err := cv.GetResource(resourceName) @@ -36,22 +64,8 @@ func (t TemplateInput) AddImageResource(cv *ocmcli.ComponentVersion, resourceNam return nil } -func (t TemplateInput) SetImagePullSecrets(imagePullSecrets []string) { - if len(imagePullSecrets) == 0 { - return - } - - t["imagePullSecrets"] = make([]map[string]string, 0, len(imagePullSecrets)) - for _, secret := range imagePullSecrets { - t["imagePullSecrets"] = append(t["imagePullSecrets"].([]map[string]string), map[string]string{ - "name": secret, - }) - } -} - -func (t TemplateInput) SetGitRepo(repo config.DeploymentRepository) { - t["git"] = map[string]interface{}{ - "repoUrl": repo.RepoURL, - "mainBranch": repo.RepoBranch, +func (t TemplateInput) ValuesWrapper() map[string]any { + return map[string]any{ + "Values": t, } } diff --git a/internal/flux_deployer/deployer.go b/internal/flux_deployer/deployer.go index 2b23362..0298a24 100644 --- a/internal/flux_deployer/deployer.go +++ b/internal/flux_deployer/deployer.go @@ -12,24 +12,13 @@ import ( "sigs.k8s.io/kustomize/api/krusty" "sigs.k8s.io/kustomize/kyaml/filesys" + "github.com/openmcp-project/bootstrapper/internal/common" cfg "github.com/openmcp-project/bootstrapper/internal/config" ocmcli "github.com/openmcp-project/bootstrapper/internal/ocm-cli" "github.com/openmcp-project/bootstrapper/internal/template" "github.com/openmcp-project/bootstrapper/internal/util" ) -const ( - EnvsDirectoryName = "envs" - FluxCDDirectoryName = "fluxcd" - ResourcesDirectoryName = "resources" - TemplatesDirectoryName = "templates" - OverlaysDirectoryName = "overlays" - - FluxCDSourceControllerResourceName = "fluxcd-source-controller" - FluxCDKustomizationControllerResourceName = "fluxcd-kustomize-controller" - FluxCDHelmControllerResourceName = "fluxcd-helm-controller" -) - type FluxDeployer struct { Config *cfg.BootstrapperConfig @@ -115,7 +104,7 @@ func (d *FluxDeployer) Deploy(ctx context.Context) (err error) { return err } - d.fluxcdCV, err = componentGetter.GetComponentVersionForResourceRecursive(ctx, componentGetter.RootComponentVersion(), FluxCDSourceControllerResourceName) + d.fluxcdCV, err = componentGetter.GetComponentVersionForResourceRecursive(ctx, componentGetter.RootComponentVersion(), common.FluxCDSourceControllerResourceName) if err != nil { return fmt.Errorf("failed to get fluxcd source controller component version: %w", err) } @@ -137,7 +126,7 @@ func (d *FluxDeployer) Deploy(ctx context.Context) (err error) { } // Kustomize /repo/envs//fluxcd - fluxCDEnvDir := filepath.Join(d.repoDir, EnvsDirectoryName, d.Config.Environment, FluxCDDirectoryName) + fluxCDEnvDir := filepath.Join(d.repoDir, common.EnvsDirectoryName, d.Config.Environment, common.FluxCDDirectoryName) manifests, err := d.Kustomize(fluxCDEnvDir) if err != nil { return fmt.Errorf("error kustomizing templated files: %w", err) @@ -157,14 +146,14 @@ func (d *FluxDeployer) ArrangeTemplates() (err error) { d.log.Info("Arranging template files") // Create directory /envs//fluxcd - fluxCDEnvDir := filepath.Join(d.templatesDir, EnvsDirectoryName, d.Config.Environment, FluxCDDirectoryName) + fluxCDEnvDir := filepath.Join(d.templatesDir, common.EnvsDirectoryName, d.Config.Environment, common.FluxCDDirectoryName) err = os.MkdirAll(fluxCDEnvDir, 0755) if err != nil { return fmt.Errorf("failed to create fluxcd environment directory: %w", err) } // Create directory /resources/fluxcd - fluxCDResourcesDir := filepath.Join(d.templatesDir, ResourcesDirectoryName, FluxCDDirectoryName) + fluxCDResourcesDir := filepath.Join(d.templatesDir, common.ResourcesDirectoryName, common.FluxCDDirectoryName) err = os.MkdirAll(fluxCDResourcesDir, 0755) if err != nil { return fmt.Errorf("failed to create fluxcd resources directory: %w", err) @@ -173,13 +162,13 @@ func (d *FluxDeployer) ArrangeTemplates() (err error) { d.log.Debug("Copying template files to target directories") // copy all files from /templates/overlays to /envs//fluxcd - err = util.CopyDir(filepath.Join(d.downloadDir, TemplatesDirectoryName, OverlaysDirectoryName), fluxCDEnvDir) + err = util.CopyDir(filepath.Join(d.downloadDir, common.TemplatesDirectoryName, common.OverlaysDirectoryName), fluxCDEnvDir) if err != nil { return fmt.Errorf("failed to copy fluxcd overlays: %w", err) } // copy all files from /templates/resources to /resources/fluxcd - err = util.CopyDir(filepath.Join(d.downloadDir, TemplatesDirectoryName, ResourcesDirectoryName), fluxCDResourcesDir) + err = util.CopyDir(filepath.Join(d.downloadDir, common.TemplatesDirectoryName, common.ResourcesDirectoryName), fluxCDResourcesDir) if err != nil { return fmt.Errorf("failed to copy fluxcd resources: %w", err) } @@ -192,23 +181,15 @@ func (d *FluxDeployer) ArrangeTemplates() (err error) { // The resulting files are written to the repo directory. func (d *FluxDeployer) Template() (err error) { d.log.Infof("Applying templates from %s to deployment repository", d.Config.Component.FluxcdTemplateResourcePath) - templateInput := template.NewTemplateInput() - - templateInput.SetImagePullSecrets(d.Config.ImagePullSecrets) - - templateInput["fluxCDEnvPath"] = "./" + EnvsDirectoryName + "/" + d.Config.Environment + "/" + FluxCDDirectoryName - templateInput["gitRepoEnvBranch"] = d.Config.DeploymentRepository.RepoBranch - templateInput["fluxCDResourcesPath"] = "../../../" + ResourcesDirectoryName + "/" + FluxCDDirectoryName - - templateInput.SetGitRepo(d.Config.DeploymentRepository) + templateInput := common.NewTemplateInputFromConfig(d.Config) - if err = templateInput.AddImageResource(d.fluxcdCV, FluxCDSourceControllerResourceName, "sourceController"); err != nil { + if err = templateInput.AddImageResource(d.fluxcdCV, common.FluxCDSourceControllerResourceName, "sourceController"); err != nil { return fmt.Errorf("failed to apply fluxcd source controller template input: %w", err) } - if err = templateInput.AddImageResource(d.fluxcdCV, FluxCDKustomizationControllerResourceName, "kustomizeController"); err != nil { + if err = templateInput.AddImageResource(d.fluxcdCV, common.FluxCDKustomizationControllerResourceName, "kustomizeController"); err != nil { return fmt.Errorf("failed to apply fluxcd kustomize controller template input: %w", err) } - if err = templateInput.AddImageResource(d.fluxcdCV, FluxCDHelmControllerResourceName, "helmController"); err != nil { + if err = templateInput.AddImageResource(d.fluxcdCV, common.FluxCDHelmControllerResourceName, "helmController"); err != nil { return fmt.Errorf("failed to apply fluxcd helm controller template input: %w", err) } diff --git a/internal/flux_deployer/templating.go b/internal/flux_deployer/templating.go index 3b45fe0..2d1c880 100644 --- a/internal/flux_deployer/templating.go +++ b/internal/flux_deployer/templating.go @@ -7,13 +7,14 @@ import ( "github.com/sirupsen/logrus" + "github.com/openmcp-project/bootstrapper/internal/common" "github.com/openmcp-project/bootstrapper/internal/template" ) // TemplateDirectory processes the template files in the specified directory and writes // the rendered content to the corresponding files in the Git repository's worktree. // It uses the provided template directory and Git repository to perform the operations. -func TemplateDirectory(templateDirectory string, templateInput template.TemplateInput, repo string, logger *logrus.Logger) error { +func TemplateDirectory(templateDirectory string, templateInput common.TemplateInput, repo string, logger *logrus.Logger) error { templateDir, err := os.Open(templateDirectory) if err != nil { @@ -62,11 +63,7 @@ func TemplateDirectory(templateDirectory string, templateInput template.Template return fmt.Errorf("failed to read template file %s: %w", relativePath, err) } - wrappedTemplateInput := map[string]interface{}{ - "Values": templateInput, - } - - templateResult, errInWalk = te.Execute(path, string(templateFromFile), wrappedTemplateInput) + templateResult, errInWalk = te.Execute(path, string(templateFromFile), templateInput.ValuesWrapper()) if errInWalk != nil { return fmt.Errorf("failed to execute template %s: %w", relativePath, errInWalk) } From 76ed1ad7efa7dfce6c26122248052b423e5cec23 Mon Sep 17 00:00:00 2001 From: Robert Graeff Date: Thu, 11 Sep 2025 12:42:00 +0200 Subject: [PATCH 3/4] feat: template input --- internal/common/constants.go | 13 -------- internal/flux_deployer/constants.go | 16 +++++++--- internal/flux_deployer/deployer.go | 21 +++++++------ internal/flux_deployer/images.go | 2 +- internal/flux_deployer/images_test.go | 6 ++-- .../{templating.go => template.go} | 3 +- .../template_input.go | 30 +++++++++---------- 7 files changed, 42 insertions(+), 49 deletions(-) delete mode 100644 internal/common/constants.go rename internal/flux_deployer/{templating.go => template.go} (93%) rename internal/{common => flux_deployer}/template_input.go (69%) diff --git a/internal/common/constants.go b/internal/common/constants.go deleted file mode 100644 index 2e4ac6b..0000000 --- a/internal/common/constants.go +++ /dev/null @@ -1,13 +0,0 @@ -package common - -const ( - EnvsDirectoryName = "envs" - FluxCDDirectoryName = "fluxcd" - ResourcesDirectoryName = "resources" - TemplatesDirectoryName = "templates" - OverlaysDirectoryName = "overlays" - - FluxCDSourceControllerResourceName = "fluxcd-source-controller" - FluxCDKustomizationControllerResourceName = "fluxcd-kustomize-controller" - FluxCDHelmControllerResourceName = "fluxcd-helm-controller" -) diff --git a/internal/flux_deployer/constants.go b/internal/flux_deployer/constants.go index b50db21..f4d575a 100644 --- a/internal/flux_deployer/constants.go +++ b/internal/flux_deployer/constants.go @@ -8,8 +8,16 @@ const ( // The secret is references in the GitRepository resource which establishes the synchronization with the deployment git repository. GitSecretName = "git" - // Names of ocm resources of the root component - FluxcdHelmController = "fluxcd-helm-controller" - FluxcdKustomizeController = "fluxcd-kustomize-controller" - FluxcdSourceController = "fluxcd-source-controller" + // Directory names + EnvsDirectoryName = "envs" + FluxCDDirectoryName = "fluxcd" + OpenMCPDirectoryName = "openmcp" + ResourcesDirectoryName = "resources" + TemplatesDirectoryName = "templates" + OverlaysDirectoryName = "overlays" + + // Resource names + FluxCDSourceControllerResourceName = "fluxcd-source-controller" + FluxCDKustomizationControllerResourceName = "fluxcd-kustomize-controller" + FluxCDHelmControllerResourceName = "fluxcd-helm-controller" ) diff --git a/internal/flux_deployer/deployer.go b/internal/flux_deployer/deployer.go index 0298a24..d9cc1c5 100644 --- a/internal/flux_deployer/deployer.go +++ b/internal/flux_deployer/deployer.go @@ -12,7 +12,6 @@ import ( "sigs.k8s.io/kustomize/api/krusty" "sigs.k8s.io/kustomize/kyaml/filesys" - "github.com/openmcp-project/bootstrapper/internal/common" cfg "github.com/openmcp-project/bootstrapper/internal/config" ocmcli "github.com/openmcp-project/bootstrapper/internal/ocm-cli" "github.com/openmcp-project/bootstrapper/internal/template" @@ -104,7 +103,7 @@ func (d *FluxDeployer) Deploy(ctx context.Context) (err error) { return err } - d.fluxcdCV, err = componentGetter.GetComponentVersionForResourceRecursive(ctx, componentGetter.RootComponentVersion(), common.FluxCDSourceControllerResourceName) + d.fluxcdCV, err = componentGetter.GetComponentVersionForResourceRecursive(ctx, componentGetter.RootComponentVersion(), FluxCDSourceControllerResourceName) if err != nil { return fmt.Errorf("failed to get fluxcd source controller component version: %w", err) } @@ -126,7 +125,7 @@ func (d *FluxDeployer) Deploy(ctx context.Context) (err error) { } // Kustomize /repo/envs//fluxcd - fluxCDEnvDir := filepath.Join(d.repoDir, common.EnvsDirectoryName, d.Config.Environment, common.FluxCDDirectoryName) + fluxCDEnvDir := filepath.Join(d.repoDir, EnvsDirectoryName, d.Config.Environment, FluxCDDirectoryName) manifests, err := d.Kustomize(fluxCDEnvDir) if err != nil { return fmt.Errorf("error kustomizing templated files: %w", err) @@ -146,14 +145,14 @@ func (d *FluxDeployer) ArrangeTemplates() (err error) { d.log.Info("Arranging template files") // Create directory /envs//fluxcd - fluxCDEnvDir := filepath.Join(d.templatesDir, common.EnvsDirectoryName, d.Config.Environment, common.FluxCDDirectoryName) + fluxCDEnvDir := filepath.Join(d.templatesDir, EnvsDirectoryName, d.Config.Environment, FluxCDDirectoryName) err = os.MkdirAll(fluxCDEnvDir, 0755) if err != nil { return fmt.Errorf("failed to create fluxcd environment directory: %w", err) } // Create directory /resources/fluxcd - fluxCDResourcesDir := filepath.Join(d.templatesDir, common.ResourcesDirectoryName, common.FluxCDDirectoryName) + fluxCDResourcesDir := filepath.Join(d.templatesDir, ResourcesDirectoryName, FluxCDDirectoryName) err = os.MkdirAll(fluxCDResourcesDir, 0755) if err != nil { return fmt.Errorf("failed to create fluxcd resources directory: %w", err) @@ -162,13 +161,13 @@ func (d *FluxDeployer) ArrangeTemplates() (err error) { d.log.Debug("Copying template files to target directories") // copy all files from /templates/overlays to /envs//fluxcd - err = util.CopyDir(filepath.Join(d.downloadDir, common.TemplatesDirectoryName, common.OverlaysDirectoryName), fluxCDEnvDir) + err = util.CopyDir(filepath.Join(d.downloadDir, TemplatesDirectoryName, OverlaysDirectoryName), fluxCDEnvDir) if err != nil { return fmt.Errorf("failed to copy fluxcd overlays: %w", err) } // copy all files from /templates/resources to /resources/fluxcd - err = util.CopyDir(filepath.Join(d.downloadDir, common.TemplatesDirectoryName, common.ResourcesDirectoryName), fluxCDResourcesDir) + err = util.CopyDir(filepath.Join(d.downloadDir, TemplatesDirectoryName, ResourcesDirectoryName), fluxCDResourcesDir) if err != nil { return fmt.Errorf("failed to copy fluxcd resources: %w", err) } @@ -181,15 +180,15 @@ func (d *FluxDeployer) ArrangeTemplates() (err error) { // The resulting files are written to the repo directory. func (d *FluxDeployer) Template() (err error) { d.log.Infof("Applying templates from %s to deployment repository", d.Config.Component.FluxcdTemplateResourcePath) - templateInput := common.NewTemplateInputFromConfig(d.Config) + templateInput := NewTemplateInputFromConfig(d.Config) - if err = templateInput.AddImageResource(d.fluxcdCV, common.FluxCDSourceControllerResourceName, "sourceController"); err != nil { + if err = templateInput.AddImageResource(d.fluxcdCV, FluxCDSourceControllerResourceName, "sourceController"); err != nil { return fmt.Errorf("failed to apply fluxcd source controller template input: %w", err) } - if err = templateInput.AddImageResource(d.fluxcdCV, common.FluxCDKustomizationControllerResourceName, "kustomizeController"); err != nil { + if err = templateInput.AddImageResource(d.fluxcdCV, FluxCDKustomizationControllerResourceName, "kustomizeController"); err != nil { return fmt.Errorf("failed to apply fluxcd kustomize controller template input: %w", err) } - if err = templateInput.AddImageResource(d.fluxcdCV, common.FluxCDHelmControllerResourceName, "helmController"); err != nil { + if err = templateInput.AddImageResource(d.fluxcdCV, FluxCDHelmControllerResourceName, "helmController"); err != nil { return fmt.Errorf("failed to apply fluxcd helm controller template input: %w", err) } diff --git a/internal/flux_deployer/images.go b/internal/flux_deployer/images.go index af851e1..9b0d05b 100644 --- a/internal/flux_deployer/images.go +++ b/internal/flux_deployer/images.go @@ -8,7 +8,7 @@ import ( // GetFluxCDImages retrieves the images of the FluxCD controllers from the root component version. func GetFluxCDImages(rootCV *ocmcli.ComponentVersion) (map[string]any, error) { - return GetImages(rootCV, FluxcdHelmController, FluxcdKustomizeController, FluxcdSourceController) + return GetImages(rootCV, FluxCDHelmControllerResourceName, FluxCDKustomizationControllerResourceName, FluxCDSourceControllerResourceName) } // GetImages retrieves the images references for a list of resources of a component version. diff --git a/internal/flux_deployer/images_test.go b/internal/flux_deployer/images_test.go index 38e91b9..d527304 100644 --- a/internal/flux_deployer/images_test.go +++ b/internal/flux_deployer/images_test.go @@ -13,13 +13,13 @@ func TestGetFluxCDImages(t *testing.T) { imageMap, err := flux_deployer.GetFluxCDImages(cv) assert.NoError(t, err, "error getting images") - img, found := imageMap[flux_deployer.FluxcdSourceController] + img, found := imageMap[flux_deployer.FluxCDSourceControllerResourceName] assert.True(t, found, "fluxcd source controller image should be found") assert.Equal(t, "test-source-controller-image:v0.0.1", img, "fluxcd source controller image should match") - img, found = imageMap[flux_deployer.FluxcdHelmController] + img, found = imageMap[flux_deployer.FluxCDHelmControllerResourceName] assert.True(t, found, "fluxcd helm controller image should be found") assert.Equal(t, "test-helm-controller-image:v0.0.1", img, "fluxcd helm controller image should match") - img, found = imageMap[flux_deployer.FluxcdKustomizeController] + img, found = imageMap[flux_deployer.FluxCDKustomizationControllerResourceName] assert.True(t, found, "fluxcd kustomize controller image should be found") assert.Equal(t, "test-kustomize-controller-image:v0.0.1", img, "fluxcd kustomize controller image should match") } diff --git a/internal/flux_deployer/templating.go b/internal/flux_deployer/template.go similarity index 93% rename from internal/flux_deployer/templating.go rename to internal/flux_deployer/template.go index 2d1c880..50c8a95 100644 --- a/internal/flux_deployer/templating.go +++ b/internal/flux_deployer/template.go @@ -7,14 +7,13 @@ import ( "github.com/sirupsen/logrus" - "github.com/openmcp-project/bootstrapper/internal/common" "github.com/openmcp-project/bootstrapper/internal/template" ) // TemplateDirectory processes the template files in the specified directory and writes // the rendered content to the corresponding files in the Git repository's worktree. // It uses the provided template directory and Git repository to perform the operations. -func TemplateDirectory(templateDirectory string, templateInput common.TemplateInput, repo string, logger *logrus.Logger) error { +func TemplateDirectory(templateDirectory string, templateInput TemplateInput, repo string, logger *logrus.Logger) error { templateDir, err := os.Open(templateDirectory) if err != nil { diff --git a/internal/common/template_input.go b/internal/flux_deployer/template_input.go similarity index 69% rename from internal/common/template_input.go rename to internal/flux_deployer/template_input.go index 7961e25..f1b6517 100644 --- a/internal/common/template_input.go +++ b/internal/flux_deployer/template_input.go @@ -1,4 +1,4 @@ -package common +package flux_deployer import ( "fmt" @@ -16,8 +16,9 @@ func NewTemplateInput() TemplateInput { func NewTemplateInputFromConfig(c *config.BootstrapperConfig) TemplateInput { t := TemplateInput{ - "fluxCDEnvPath": "./" + EnvsDirectoryName + "/" + c.Environment + "/" + FluxCDDirectoryName, - "fluxCDResourcesPath": "../../../" + ResourcesDirectoryName + "/" + FluxCDDirectoryName, + "fluxCDEnvPath": "./" + EnvsDirectoryName + "/" + c.Environment + "/" + FluxCDDirectoryName, + "fluxCDResourcesPath": "../../../" + ResourcesDirectoryName + "/" + FluxCDDirectoryName, + "openMCPResourcesPath": "../../../" + ResourcesDirectoryName + "/" + OpenMCPDirectoryName, "git": map[string]interface{}{ "repoUrl": c.DeploymentRepository.RepoURL, @@ -25,18 +26,7 @@ func NewTemplateInputFromConfig(c *config.BootstrapperConfig) TemplateInput { }, "gitRepoEnvBranch": c.DeploymentRepository.RepoBranch, - "imagePullSecrets": func() []map[string]string { - if len(c.ImagePullSecrets) == 0 { - return nil - } - secrets := make([]map[string]string, 0, len(c.ImagePullSecrets)) - for _, secret := range c.ImagePullSecrets { - secrets = append(secrets, map[string]string{ - "name": secret, - }) - } - return secrets - }(), + "imagePullSecrets": wrapImagePullSecrets(c.ImagePullSecrets), } return t @@ -69,3 +59,13 @@ func (t TemplateInput) ValuesWrapper() map[string]any { "Values": t, } } + +func wrapImagePullSecrets(secrets []string) []map[string]string { + wrappedSecrets := make([]map[string]string, len(secrets)) + for i, secret := range secrets { + wrappedSecrets[i] = map[string]string{ + "name": secret, + } + } + return wrappedSecrets +} From c84580d22274d2d97505dd89fec7b6d9756fdff2 Mon Sep 17 00:00:00 2001 From: Robert Graeff Date: Thu, 11 Sep 2025 15:10:25 +0200 Subject: [PATCH 4/4] feat: ensure flux system namespace exists --- internal/flux_deployer/deployer.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/internal/flux_deployer/deployer.go b/internal/flux_deployer/deployer.go index d9cc1c5..797643f 100644 --- a/internal/flux_deployer/deployer.go +++ b/internal/flux_deployer/deployer.go @@ -8,6 +8,7 @@ import ( "path/filepath" "github.com/openmcp-project/controller-utils/pkg/clusters" + "github.com/openmcp-project/controller-utils/pkg/resources" "github.com/sirupsen/logrus" "sigs.k8s.io/kustomize/api/krusty" "sigs.k8s.io/kustomize/kyaml/filesys" @@ -50,6 +51,11 @@ func NewFluxDeployer(config *cfg.BootstrapperConfig, gitConfigPath, ocmConfigPat } func (d *FluxDeployer) Deploy(ctx context.Context) (err error) { + d.log.Infof("Ensure namespace %s exists", d.fluxNamespace) + namespaceMutator := resources.NewNamespaceMutator(d.fluxNamespace) + if err := resources.CreateOrUpdateResource(ctx, d.platformCluster.Client(), namespaceMutator); err != nil { + return fmt.Errorf("error creating/updating namespace %s: %w", d.fluxNamespace, err) + } if err := CreateGitCredentialsSecret(ctx, d.log, d.GitConfigPath, GitSecretName, d.fluxNamespace, d.platformCluster.Client()); err != nil { return err