Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions internal/deployment-repo/deploymentRepoManager.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ const (
FluxCDSourceControllerResourceName = "fluxcd-source-controller"
FluxCDKustomizationControllerResourceName = "fluxcd-kustomize-controller"
FluxCDHelmControllerResourceName = "fluxcd-helm-controller"
FluxCDNotificationControllerName = "fluxcd-notification-controller"
FluxCDImageReflectorControllerName = "fluxcd-image-reflector-controller"
FluxCDImageAutomationControllerName = "fluxcd-image-automation-controller"

EnvsDirectoryName = "envs"
ResourcesDirectoryName = "resources"
Expand Down Expand Up @@ -251,6 +254,21 @@ func (m *DeploymentRepoManager) ApplyTemplates(ctx context.Context) error {
return fmt.Errorf("failed to apply fluxcd helm controller template input: %w", err)
}

err = applyFluxCDTemplateInput(templateInput, m.fluxcdCV, FluxCDNotificationControllerName, "notificationController")
if err != nil {
return fmt.Errorf("failed to apply fluxcd helm controller template input: %w", err)
}

err = applyFluxCDTemplateInput(templateInput, m.fluxcdCV, FluxCDImageReflectorControllerName, "imageReflectorController")
if err != nil {
return fmt.Errorf("failed to apply fluxcd image reflector controller template input: %w", err)
}

err = applyFluxCDTemplateInput(templateInput, m.fluxcdCV, FluxCDImageAutomationControllerName, "imageAutomationController")
if err != nil {
return fmt.Errorf("failed to apply fluxcd image automation controller template input: %w", err)
}

err = TemplateDir(m.templatesDir, templateInput, m.gitRepo)
if err != nil {
return fmt.Errorf("failed to apply templates from directory %s: %w", m.templatesDir, err)
Expand Down
21 changes: 21 additions & 0 deletions internal/deployment-repo/testdata/01/component-constructor.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,27 @@ components:
type: ociArtifact
imageReference: ghcr.io/fluxcd/helm-controller:v1.3.0

- name: fluxcd-notification-controller
version: v1.6.0
type: ociImage
access:
type: ociArtifact
imageReference: ghcr.io/fluxcd/notification-controller:v1.6.0

- name: fluxcd-image-reflector-controller
version: v0.35.2
type: ociImage
access:
type: ociArtifact
imageReference: ghcr.io/fluxcd/image-reflector-controller:v0.35.2

- name: fluxcd-image-automation-controller
version: v0.41.2
type: ociImage
access:
type: ociArtifact
imageReference: ghcr.io/fluxcd/image-automation-controller:v0.41.2

- name: github.com/openmcp-project/openmcp-operator
version: v0.2.1
provider:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,16 @@ images:
newTag: v1.3.0
- name: ghcr.io/fluxcd/image-automation-controller
newName: ghcr.io/fluxcd/image-automation-controller
newTag: v0.41.2
- name: ghcr.io/fluxcd/image-reflector-controller
newName: ghcr.io/fluxcd/image-reflector-controller
newTag: v0.35.2
- name: ghcr.io/fluxcd/kustomize-controller
newName: ghcr.io/fluxcd/kustomize-controller
newTag: v1.6.1
- name: ghcr.io/fluxcd/notification-controller
newName: ghcr.io/fluxcd/notification-controller
newTag: v1.6.0
- name: ghcr.io/fluxcd/source-controller
newName: ghcr.io/fluxcd/source-controller
newTag: v1.6.2
Expand Down
44 changes: 44 additions & 0 deletions internal/flux_deployer/component_manager.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package flux_deployer

import (
"context"

cfg "github.com/openmcp-project/bootstrapper/internal/config"
ocm_cli "github.com/openmcp-project/bootstrapper/internal/ocm-cli"
)

// ComponentManager bundles the OCM logic required by the FluxDeployer.
type ComponentManager interface {
GetComponentWithImageResources(ctx context.Context) (*ocm_cli.ComponentVersion, error)
DownloadTemplatesResource(ctx context.Context, downloadDir string) error
}

type ComponentManagerImpl struct {
Config *cfg.BootstrapperConfig
OCMConfigPath string
ComponentGetter *ocm_cli.ComponentGetter
}

var _ ComponentManager = (*ComponentManagerImpl)(nil)

func NewComponentManager(ctx context.Context, config *cfg.BootstrapperConfig, ocmConfigPath string) (ComponentManager, error) {
m := &ComponentManagerImpl{
Config: config,
OCMConfigPath: ocmConfigPath,
ComponentGetter: ocm_cli.NewComponentGetter(config.Component.OpenMCPComponentLocation, config.Component.FluxcdTemplateResourcePath, ocmConfigPath),
}

if err := m.ComponentGetter.InitializeComponents(ctx); err != nil {
return nil, err
}

return m, nil
}

func (m *ComponentManagerImpl) GetComponentWithImageResources(ctx context.Context) (*ocm_cli.ComponentVersion, error) {
return m.ComponentGetter.GetComponentVersionForResourceRecursive(ctx, m.ComponentGetter.RootComponentVersion(), FluxCDSourceControllerResourceName)
}

func (m *ComponentManagerImpl) DownloadTemplatesResource(ctx context.Context, downloadDir string) error {
return m.ComponentGetter.DownloadTemplatesResource(ctx, downloadDir)
}
42 changes: 42 additions & 0 deletions internal/flux_deployer/component_manager_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package flux_deployer_test

import (
"context"
"fmt"
"os"

"sigs.k8s.io/yaml"

"github.com/openmcp-project/bootstrapper/internal/flux_deployer"
ocmcli "github.com/openmcp-project/bootstrapper/internal/ocm-cli"
"github.com/openmcp-project/bootstrapper/internal/util"
)

// MockComponentManager is a mock implementation of the ComponentManager interface for testing purposes.
type MockComponentManager struct {
ComponentPath string
TemplatesPath string
}

var _ flux_deployer.ComponentManager = (*MockComponentManager)(nil)

func (m MockComponentManager) GetComponentWithImageResources(_ context.Context) (*ocmcli.ComponentVersion, error) {
return loadComponentVersion(m.ComponentPath)
}

func (m MockComponentManager) DownloadTemplatesResource(ctx context.Context, downloadDir string) error {
return util.CopyDir(m.TemplatesPath, downloadDir)
}

func loadComponentVersion(path string) (*ocmcli.ComponentVersion, error) {
cv := &ocmcli.ComponentVersion{}
content, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("error reading component version from file %s: %w", path, err)
}
err = yaml.Unmarshal(content, cv)
if err != nil {
return nil, fmt.Errorf("error unmarshalling component version from file %s: %w", path, err)
}
return cv, nil
}
3 changes: 3 additions & 0 deletions internal/flux_deployer/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,7 @@ const (
FluxCDSourceControllerResourceName = "fluxcd-source-controller"
FluxCDKustomizationControllerResourceName = "fluxcd-kustomize-controller"
FluxCDHelmControllerResourceName = "fluxcd-helm-controller"
FluxCDNotificationControllerName = "fluxcd-notification-controller"
FluxCDImageReflectorControllerName = "fluxcd-image-reflector-controller"
FluxCDImageAutomationControllerName = "fluxcd-image-automation-controller"
)
92 changes: 23 additions & 69 deletions internal/flux_deployer/deployer.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"
"fmt"
"os"
"path"
"path/filepath"

"github.com/openmcp-project/controller-utils/pkg/clusters"
Expand All @@ -15,7 +14,6 @@ import (

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"
)

Expand Down Expand Up @@ -51,6 +49,15 @@ func NewFluxDeployer(config *cfg.BootstrapperConfig, gitConfigPath, ocmConfigPat
}

func (d *FluxDeployer) Deploy(ctx context.Context) (err error) {
componentManager, err := NewComponentManager(ctx, d.Config, d.OcmConfigPath)
if err != nil {
return fmt.Errorf("error creating component manager: %w", err)
}

return d.DeployWithComponentManager(ctx, componentManager)
}

func (d *FluxDeployer) DeployWithComponentManager(ctx context.Context, componentManager ComponentManager) (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 {
Expand Down Expand Up @@ -99,24 +106,15 @@ func (d *FluxDeployer) Deploy(ctx context.Context) (err error) {
}
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)
// Get component which contains the fluxcd images as resources
d.fluxcdCV, err = componentManager.GetComponentWithImageResources(ctx)
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, d.downloadDir); err != nil {
d.log.Info("Downloading templates")
if err := componentManager.DownloadTemplatesResource(ctx, d.downloadDir); err != nil {
return fmt.Errorf("error downloading templates: %w", err)
}

Expand Down Expand Up @@ -197,8 +195,17 @@ func (d *FluxDeployer) Template() (err error) {
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 = templateInput.AddImageResource(d.fluxcdCV, FluxCDNotificationControllerName, "notificationController"); err != nil {
return fmt.Errorf("failed to apply fluxcd notification controller template input: %w", err)
}
if err = templateInput.AddImageResource(d.fluxcdCV, FluxCDImageReflectorControllerName, "imageReflectorController"); err != nil {
return fmt.Errorf("failed to apply fluxcd image reflector controller template input: %w", err)
}
if err = templateInput.AddImageResource(d.fluxcdCV, FluxCDImageAutomationControllerName, "imageAutomationController"); err != nil {
return fmt.Errorf("failed to apply fluxcd image automation controller template input: %w", err)
}

if err = TemplateDirectory(d.templatesDir, templateInput, d.repoDir, d.log); err != nil {
if err = TemplateDirectory(d.templatesDir, d.repoDir, templateInput, d.log); err != nil {
return fmt.Errorf("failed to apply templates from directory %s: %w", d.templatesDir, err)
}

Expand All @@ -224,56 +231,3 @@ func (d *FluxDeployer) Kustomize(dir string) ([]byte, error) {

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-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 flux deployment objects from file %s: %w", filepath, err)
}

// Template
values := map[string]any{
"Values": map[string]any{
"namespace": d.fluxNamespace,
"images": images,
},
}
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 deployment objects: %w", err)
}

// Apply
d.log.Debug("Applying flux deployment objects")
if err := util.ApplyManifests(ctx, d.platformCluster, manifest); err != nil {
return err
}

return nil
}

func (d *FluxDeployer) readFileContent(filepath string) ([]byte, error) {
d.log.Debugf("Reading file: %s", filepath)

if _, err := os.Stat(filepath); os.IsNotExist(err) {
return nil, fmt.Errorf("file does not exist at path: %s", filepath)
}

content, err := os.ReadFile(filepath)
if err != nil {
return nil, fmt.Errorf("error reading file %s: %w", filepath, err)
}

return content, nil
}
31 changes: 18 additions & 13 deletions internal/flux_deployer/deployer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@ import (
)

func TestDeployFluxController(t *testing.T) {
rootComponentVersion1 := LoadComponentVersion(t, "./testdata/01/root-component-version-1.yaml")
rootComponentVersion2 := LoadComponentVersion(t, "./testdata/01/root-component-version-2.yaml")
downloadDir := "./testdata/01/download_dir"

platformClient := fake.NewClientBuilder().Build()
platformCluster := clusters.NewTestClusterFromClient("platform", platformClient)
Expand All @@ -31,28 +28,36 @@ func TestDeployFluxController(t *testing.T) {
DeploymentRepository: cfg.DeploymentRepository{},
Providers: cfg.Providers{},
ImagePullSecrets: nil,
OpenMCPOperator: cfg.OpenMCPOperator{},
Environment: "",
Environment: "test",
}

d := flux_deployer.NewFluxDeployer(config, "", ocmcli.NoOcmConfig, platformCluster, logging.GetLogger())

// Create a deployment
err := d.DeployFluxControllers(t.Context(), rootComponentVersion1, downloadDir)
assert.NoError(t, err, "Error deploying flux controllers")
// Initial deployment
componentManager1 := &MockComponentManager{
ComponentPath: "./testdata/01/component_1.yaml",
TemplatesPath: "./testdata/01/fluxcd_resource",
}

err := d.DeployWithComponentManager(t.Context(), componentManager1)
assert.NoError(t, err, "Error deploying flux controllers")
deployment := &v1.Deployment{}
err = platformClient.Get(t.Context(), client.ObjectKey{Name: "source-controller", Namespace: namespace}, deployment)
assert.NoError(t, err, "Error getting source-controller deployment")
assert.Equal(t, namespace, deployment.Namespace, "Deployment namespace does not match expected namespace")
assert.Equal(t, "test-source-controller-image:v0.0.1", deployment.Spec.Template.Spec.Containers[0].Image, "Deployment image does not match expected image")
assert.Equal(t, "ghcr.io/fluxcd/source-controller:v1.0.0", deployment.Spec.Template.Spec.Containers[0].Image, "Deployment image does not match expected image")

// Update the deployment
err = d.DeployFluxControllers(t.Context(), rootComponentVersion2, downloadDir)
assert.NoError(t, err, "Error updating flux controllers")
// Update deployment
componentManager2 := &MockComponentManager{
ComponentPath: "./testdata/01/component_2.yaml",
TemplatesPath: "./testdata/01/fluxcd_resource",
}

err = d.DeployWithComponentManager(t.Context(), componentManager2)
assert.NoError(t, err, "Error deploying flux controllers")
deployment = &v1.Deployment{}
err = platformClient.Get(t.Context(), client.ObjectKey{Name: "source-controller", Namespace: namespace}, deployment)
assert.NoError(t, err, "Error getting source-controller deployment")
assert.Equal(t, namespace, deployment.Namespace, "Deployment namespace does not match expected namespace")
assert.Equal(t, "test-source-controller-image:v0.0.2", deployment.Spec.Template.Spec.Containers[0].Image, "Deployment image does not match expected image")
assert.Equal(t, "ghcr.io/fluxcd/source-controller:v2.0.0", deployment.Spec.Template.Spec.Containers[0].Image, "Deployment image does not match expected image")
}
Loading