Skip to content

Commit a07aa4c

Browse files
authored
feat: flux controller images (#50)
* feat: add flux images, unit tests * feat: unit tests
1 parent 42fc829 commit a07aa4c

35 files changed

+15510
-228
lines changed

internal/deployment-repo/deploymentRepoManager.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ const (
3030
FluxCDSourceControllerResourceName = "fluxcd-source-controller"
3131
FluxCDKustomizationControllerResourceName = "fluxcd-kustomize-controller"
3232
FluxCDHelmControllerResourceName = "fluxcd-helm-controller"
33+
FluxCDNotificationControllerName = "fluxcd-notification-controller"
34+
FluxCDImageReflectorControllerName = "fluxcd-image-reflector-controller"
35+
FluxCDImageAutomationControllerName = "fluxcd-image-automation-controller"
3336

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

257+
err = applyFluxCDTemplateInput(templateInput, m.fluxcdCV, FluxCDNotificationControllerName, "notificationController")
258+
if err != nil {
259+
return fmt.Errorf("failed to apply fluxcd helm controller template input: %w", err)
260+
}
261+
262+
err = applyFluxCDTemplateInput(templateInput, m.fluxcdCV, FluxCDImageReflectorControllerName, "imageReflectorController")
263+
if err != nil {
264+
return fmt.Errorf("failed to apply fluxcd image reflector controller template input: %w", err)
265+
}
266+
267+
err = applyFluxCDTemplateInput(templateInput, m.fluxcdCV, FluxCDImageAutomationControllerName, "imageAutomationController")
268+
if err != nil {
269+
return fmt.Errorf("failed to apply fluxcd image automation controller template input: %w", err)
270+
}
271+
254272
err = TemplateDir(m.templatesDir, templateInput, m.gitRepo)
255273
if err != nil {
256274
return fmt.Errorf("failed to apply templates from directory %s: %w", m.templatesDir, err)

internal/deployment-repo/testdata/01/component-constructor.yaml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,27 @@ components:
4747
type: ociArtifact
4848
imageReference: ghcr.io/fluxcd/helm-controller:v1.3.0
4949

50+
- name: fluxcd-notification-controller
51+
version: v1.6.0
52+
type: ociImage
53+
access:
54+
type: ociArtifact
55+
imageReference: ghcr.io/fluxcd/notification-controller:v1.6.0
56+
57+
- name: fluxcd-image-reflector-controller
58+
version: v0.35.2
59+
type: ociImage
60+
access:
61+
type: ociArtifact
62+
imageReference: ghcr.io/fluxcd/image-reflector-controller:v0.35.2
63+
64+
- name: fluxcd-image-automation-controller
65+
version: v0.41.2
66+
type: ociImage
67+
access:
68+
type: ociArtifact
69+
imageReference: ghcr.io/fluxcd/image-automation-controller:v0.41.2
70+
5071
- name: github.com/openmcp-project/openmcp-operator
5172
version: v0.2.1
5273
provider:

internal/deployment-repo/testdata/01/expected-repo/resources/fluxcd/kustomization.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,16 @@ images:
99
newTag: v1.3.0
1010
- name: ghcr.io/fluxcd/image-automation-controller
1111
newName: ghcr.io/fluxcd/image-automation-controller
12+
newTag: v0.41.2
1213
- name: ghcr.io/fluxcd/image-reflector-controller
1314
newName: ghcr.io/fluxcd/image-reflector-controller
15+
newTag: v0.35.2
1416
- name: ghcr.io/fluxcd/kustomize-controller
1517
newName: ghcr.io/fluxcd/kustomize-controller
1618
newTag: v1.6.1
1719
- name: ghcr.io/fluxcd/notification-controller
1820
newName: ghcr.io/fluxcd/notification-controller
21+
newTag: v1.6.0
1922
- name: ghcr.io/fluxcd/source-controller
2023
newName: ghcr.io/fluxcd/source-controller
2124
newTag: v1.6.2
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package flux_deployer
2+
3+
import (
4+
"context"
5+
6+
cfg "github.com/openmcp-project/bootstrapper/internal/config"
7+
ocm_cli "github.com/openmcp-project/bootstrapper/internal/ocm-cli"
8+
)
9+
10+
// ComponentManager bundles the OCM logic required by the FluxDeployer.
11+
type ComponentManager interface {
12+
GetComponentWithImageResources(ctx context.Context) (*ocm_cli.ComponentVersion, error)
13+
DownloadTemplatesResource(ctx context.Context, downloadDir string) error
14+
}
15+
16+
type ComponentManagerImpl struct {
17+
Config *cfg.BootstrapperConfig
18+
OCMConfigPath string
19+
ComponentGetter *ocm_cli.ComponentGetter
20+
}
21+
22+
var _ ComponentManager = (*ComponentManagerImpl)(nil)
23+
24+
func NewComponentManager(ctx context.Context, config *cfg.BootstrapperConfig, ocmConfigPath string) (ComponentManager, error) {
25+
m := &ComponentManagerImpl{
26+
Config: config,
27+
OCMConfigPath: ocmConfigPath,
28+
ComponentGetter: ocm_cli.NewComponentGetter(config.Component.OpenMCPComponentLocation, config.Component.FluxcdTemplateResourcePath, ocmConfigPath),
29+
}
30+
31+
if err := m.ComponentGetter.InitializeComponents(ctx); err != nil {
32+
return nil, err
33+
}
34+
35+
return m, nil
36+
}
37+
38+
func (m *ComponentManagerImpl) GetComponentWithImageResources(ctx context.Context) (*ocm_cli.ComponentVersion, error) {
39+
return m.ComponentGetter.GetComponentVersionForResourceRecursive(ctx, m.ComponentGetter.RootComponentVersion(), FluxCDSourceControllerResourceName)
40+
}
41+
42+
func (m *ComponentManagerImpl) DownloadTemplatesResource(ctx context.Context, downloadDir string) error {
43+
return m.ComponentGetter.DownloadTemplatesResource(ctx, downloadDir)
44+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package flux_deployer_test
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"os"
7+
8+
"sigs.k8s.io/yaml"
9+
10+
"github.com/openmcp-project/bootstrapper/internal/flux_deployer"
11+
ocmcli "github.com/openmcp-project/bootstrapper/internal/ocm-cli"
12+
"github.com/openmcp-project/bootstrapper/internal/util"
13+
)
14+
15+
// MockComponentManager is a mock implementation of the ComponentManager interface for testing purposes.
16+
type MockComponentManager struct {
17+
ComponentPath string
18+
TemplatesPath string
19+
}
20+
21+
var _ flux_deployer.ComponentManager = (*MockComponentManager)(nil)
22+
23+
func (m MockComponentManager) GetComponentWithImageResources(_ context.Context) (*ocmcli.ComponentVersion, error) {
24+
return loadComponentVersion(m.ComponentPath)
25+
}
26+
27+
func (m MockComponentManager) DownloadTemplatesResource(ctx context.Context, downloadDir string) error {
28+
return util.CopyDir(m.TemplatesPath, downloadDir)
29+
}
30+
31+
func loadComponentVersion(path string) (*ocmcli.ComponentVersion, error) {
32+
cv := &ocmcli.ComponentVersion{}
33+
content, err := os.ReadFile(path)
34+
if err != nil {
35+
return nil, fmt.Errorf("error reading component version from file %s: %w", path, err)
36+
}
37+
err = yaml.Unmarshal(content, cv)
38+
if err != nil {
39+
return nil, fmt.Errorf("error unmarshalling component version from file %s: %w", path, err)
40+
}
41+
return cv, nil
42+
}

internal/flux_deployer/constants.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,7 @@ const (
2020
FluxCDSourceControllerResourceName = "fluxcd-source-controller"
2121
FluxCDKustomizationControllerResourceName = "fluxcd-kustomize-controller"
2222
FluxCDHelmControllerResourceName = "fluxcd-helm-controller"
23+
FluxCDNotificationControllerName = "fluxcd-notification-controller"
24+
FluxCDImageReflectorControllerName = "fluxcd-image-reflector-controller"
25+
FluxCDImageAutomationControllerName = "fluxcd-image-automation-controller"
2326
)

internal/flux_deployer/deployer.go

Lines changed: 23 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"context"
55
"fmt"
66
"os"
7-
"path"
87
"path/filepath"
98

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

1615
cfg "github.com/openmcp-project/bootstrapper/internal/config"
1716
ocmcli "github.com/openmcp-project/bootstrapper/internal/ocm-cli"
18-
"github.com/openmcp-project/bootstrapper/internal/template"
1917
"github.com/openmcp-project/bootstrapper/internal/util"
2018
)
2119

@@ -51,6 +49,15 @@ func NewFluxDeployer(config *cfg.BootstrapperConfig, gitConfigPath, ocmConfigPat
5149
}
5250

5351
func (d *FluxDeployer) Deploy(ctx context.Context) (err error) {
52+
componentManager, err := NewComponentManager(ctx, d.Config, d.OcmConfigPath)
53+
if err != nil {
54+
return fmt.Errorf("error creating component manager: %w", err)
55+
}
56+
57+
return d.DeployWithComponentManager(ctx, componentManager)
58+
}
59+
60+
func (d *FluxDeployer) DeployWithComponentManager(ctx context.Context, componentManager ComponentManager) (err error) {
5461
d.log.Infof("Ensure namespace %s exists", d.fluxNamespace)
5562
namespaceMutator := resources.NewNamespaceMutator(d.fluxNamespace)
5663
if err := resources.CreateOrUpdateResource(ctx, d.platformCluster.Client(), namespaceMutator); err != nil {
@@ -99,24 +106,15 @@ func (d *FluxDeployer) Deploy(ctx context.Context) (err error) {
99106
}
100107
d.log.Tracef("Created repo directory: %s", d.repoDir)
101108

102-
// Get components
103-
// - root component
104-
// - gitops-templates component
105-
// - that contains the image resources for fluxcd source controller component
106-
d.log.Info("Loading root component and gitops-templates component")
107-
componentGetter := ocmcli.NewComponentGetter(d.Config.Component.OpenMCPComponentLocation, d.Config.Component.FluxcdTemplateResourcePath, d.OcmConfigPath)
108-
if err := componentGetter.InitializeComponents(ctx); err != nil {
109-
return err
110-
}
111-
112-
d.fluxcdCV, err = componentGetter.GetComponentVersionForResourceRecursive(ctx, componentGetter.RootComponentVersion(), FluxCDSourceControllerResourceName)
109+
// Get component which contains the fluxcd images as resources
110+
d.fluxcdCV, err = componentManager.GetComponentWithImageResources(ctx)
113111
if err != nil {
114112
return fmt.Errorf("failed to get fluxcd source controller component version: %w", err)
115113
}
116114

117115
// Download resource from gitops-templates component into the download directory
118-
d.log.Info("Downloading gitops-templates")
119-
if err := componentGetter.DownloadTemplatesResource(ctx, d.downloadDir); err != nil {
116+
d.log.Info("Downloading templates")
117+
if err := componentManager.DownloadTemplatesResource(ctx, d.downloadDir); err != nil {
120118
return fmt.Errorf("error downloading templates: %w", err)
121119
}
122120

@@ -197,8 +195,17 @@ func (d *FluxDeployer) Template() (err error) {
197195
if err = templateInput.AddImageResource(d.fluxcdCV, FluxCDHelmControllerResourceName, "helmController"); err != nil {
198196
return fmt.Errorf("failed to apply fluxcd helm controller template input: %w", err)
199197
}
198+
if err = templateInput.AddImageResource(d.fluxcdCV, FluxCDNotificationControllerName, "notificationController"); err != nil {
199+
return fmt.Errorf("failed to apply fluxcd notification controller template input: %w", err)
200+
}
201+
if err = templateInput.AddImageResource(d.fluxcdCV, FluxCDImageReflectorControllerName, "imageReflectorController"); err != nil {
202+
return fmt.Errorf("failed to apply fluxcd image reflector controller template input: %w", err)
203+
}
204+
if err = templateInput.AddImageResource(d.fluxcdCV, FluxCDImageAutomationControllerName, "imageAutomationController"); err != nil {
205+
return fmt.Errorf("failed to apply fluxcd image automation controller template input: %w", err)
206+
}
200207

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

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

225232
return resourcesYaml, nil
226233
}
227-
228-
func (d *FluxDeployer) DeployFluxControllers(ctx context.Context, rootComponentVersion *ocmcli.ComponentVersion, downloadDir string) error {
229-
d.log.Info("Deploying flux")
230-
231-
images, err := GetFluxCDImages(rootComponentVersion)
232-
if err != nil {
233-
return fmt.Errorf("error getting images for flux controllers: %w", err)
234-
}
235-
236-
// Read manifest file
237-
filepath := path.Join(downloadDir, "resources", "gotk-components.yaml")
238-
d.log.Debugf("Reading flux deployment objects from file %s", filepath)
239-
manifestTpl, err := d.readFileContent(filepath)
240-
if err != nil {
241-
return fmt.Errorf("error reading flux deployment objects from file %s: %w", filepath, err)
242-
}
243-
244-
// Template
245-
values := map[string]any{
246-
"Values": map[string]any{
247-
"namespace": d.fluxNamespace,
248-
"images": images,
249-
},
250-
}
251-
d.log.Debug("Templating flux deployment objects")
252-
manifest, err := template.NewTemplateExecution().Execute("flux-deployment", string(manifestTpl), values)
253-
if err != nil {
254-
return fmt.Errorf("error templating flux deployment objects: %w", err)
255-
}
256-
257-
// Apply
258-
d.log.Debug("Applying flux deployment objects")
259-
if err := util.ApplyManifests(ctx, d.platformCluster, manifest); err != nil {
260-
return err
261-
}
262-
263-
return nil
264-
}
265-
266-
func (d *FluxDeployer) readFileContent(filepath string) ([]byte, error) {
267-
d.log.Debugf("Reading file: %s", filepath)
268-
269-
if _, err := os.Stat(filepath); os.IsNotExist(err) {
270-
return nil, fmt.Errorf("file does not exist at path: %s", filepath)
271-
}
272-
273-
content, err := os.ReadFile(filepath)
274-
if err != nil {
275-
return nil, fmt.Errorf("error reading file %s: %w", filepath, err)
276-
}
277-
278-
return content, nil
279-
}

internal/flux_deployer/deployer_test.go

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,6 @@ import (
1616
)
1717

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

2320
platformClient := fake.NewClientBuilder().Build()
2421
platformCluster := clusters.NewTestClusterFromClient("platform", platformClient)
@@ -31,28 +28,36 @@ func TestDeployFluxController(t *testing.T) {
3128
DeploymentRepository: cfg.DeploymentRepository{},
3229
Providers: cfg.Providers{},
3330
ImagePullSecrets: nil,
34-
OpenMCPOperator: cfg.OpenMCPOperator{},
35-
Environment: "",
31+
Environment: "test",
3632
}
3733

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

40-
// Create a deployment
41-
err := d.DeployFluxControllers(t.Context(), rootComponentVersion1, downloadDir)
42-
assert.NoError(t, err, "Error deploying flux controllers")
36+
// Initial deployment
37+
componentManager1 := &MockComponentManager{
38+
ComponentPath: "./testdata/01/component_1.yaml",
39+
TemplatesPath: "./testdata/01/fluxcd_resource",
40+
}
4341

42+
err := d.DeployWithComponentManager(t.Context(), componentManager1)
43+
assert.NoError(t, err, "Error deploying flux controllers")
4444
deployment := &v1.Deployment{}
4545
err = platformClient.Get(t.Context(), client.ObjectKey{Name: "source-controller", Namespace: namespace}, deployment)
4646
assert.NoError(t, err, "Error getting source-controller deployment")
4747
assert.Equal(t, namespace, deployment.Namespace, "Deployment namespace does not match expected namespace")
48-
assert.Equal(t, "test-source-controller-image:v0.0.1", deployment.Spec.Template.Spec.Containers[0].Image, "Deployment image does not match expected image")
48+
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")
4949

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

56+
err = d.DeployWithComponentManager(t.Context(), componentManager2)
57+
assert.NoError(t, err, "Error deploying flux controllers")
58+
deployment = &v1.Deployment{}
5459
err = platformClient.Get(t.Context(), client.ObjectKey{Name: "source-controller", Namespace: namespace}, deployment)
5560
assert.NoError(t, err, "Error getting source-controller deployment")
5661
assert.Equal(t, namespace, deployment.Namespace, "Deployment namespace does not match expected namespace")
57-
assert.Equal(t, "test-source-controller-image:v0.0.2", deployment.Spec.Template.Spec.Containers[0].Image, "Deployment image does not match expected image")
62+
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")
5863
}

0 commit comments

Comments
 (0)