66 "os"
77 "path/filepath"
88
9+ "github.com/go-git/go-billy/v5"
910 "github.com/go-git/go-git/v5"
1011
1112 gitconfig "github.com/openmcp-project/bootstrapper/internal/git-config"
@@ -15,7 +16,11 @@ import (
1516)
1617
1718const (
18- openMCPOperatorComponentName = "openmcp-operator"
19+ OpenMCPOperatorComponentName = "openmcp-operator"
20+ TemplateOverlaysDir = "overlays"
21+ TemplateEnvsDir = "envs"
22+ TemplateResourcesDir = "resources"
23+ TemplateCRDsDir = "crds"
1924)
2025
2126// DeploymentRepoManager manages the deployment repository by applying templates and committing changes.
@@ -66,6 +71,8 @@ type DeploymentRepoManager struct {
6671 gitRepo * git.Repository
6772
6873 openMCPOperatorCV * ocmcli.ComponentVersion
74+
75+ crdFiles []string
6976}
7077
7178// NewDeploymentRepoManager creates a new DeploymentRepoManager with the specified parameters.
@@ -154,9 +161,61 @@ func (m *DeploymentRepoManager) Complete(ctx context.Context) (*DeploymentRepoMa
154161 return m , fmt .Errorf ("failed to download templates resource: %w" , err )
155162 }
156163
164+ overlaysDir := filepath .Join (m .templatesDir , "templates" , TemplateOverlaysDir )
165+ if _ , err = os .Stat (overlaysDir ); err != nil {
166+ if os .IsNotExist (err ) {
167+ return m , fmt .Errorf ("no overlays directory found in templates resource" )
168+ }
169+ return m , fmt .Errorf ("failed to stat overlays directory: %w" , err )
170+ }
171+
172+ // move overlays to <templatesDir>/envs/<deploymentRepositoryBranch>
173+ envsDir := filepath .Join (m .templatesDir , TemplateEnvsDir )
174+ err = os .Mkdir (envsDir , 0o755 )
175+ if err != nil {
176+ return m , fmt .Errorf ("failed to create envs directory: %w" , err )
177+ }
178+
179+ err = os .Rename (overlaysDir , filepath .Join (envsDir , m .DeploymentRepositoryBranch ))
180+ if err != nil {
181+ return m , fmt .Errorf ("failed to move overlays to envs directory: %w" , err )
182+ }
183+
184+ // move resources to <templatesDir>/resources
185+ resourcesDir := filepath .Join (m .templatesDir , "templates" , TemplateResourcesDir )
186+ if _ , err = os .Stat (resourcesDir ); err != nil {
187+ if os .IsNotExist (err ) {
188+ return m , fmt .Errorf ("no resources directory found in templates resource" )
189+ }
190+ return m , fmt .Errorf ("failed to stat resources directory: %w" , err )
191+ }
192+
193+ err = os .Rename (resourcesDir , filepath .Join (m .templatesDir , TemplateResourcesDir ))
194+ if err != nil {
195+ return m , fmt .Errorf ("failed to move resources to resources directory: %w" , err )
196+ }
197+
198+ // remove Chart.yaml if it exists
199+ chartFile := filepath .Join (m .templatesDir , "Chart.yaml" )
200+ if _ , err = os .Stat (chartFile ); err == nil {
201+ err = os .Remove (chartFile )
202+ if err != nil {
203+ return m , fmt .Errorf ("failed to remove Chart.yaml from templates directory: %w" , err )
204+ }
205+ }
206+
207+ // remove values.yaml if it exists
208+ valuesFile := filepath .Join (m .templatesDir , "values.yaml" )
209+ if _ , err = os .Stat (valuesFile ); err == nil {
210+ err = os .Remove (valuesFile )
211+ if err != nil {
212+ return m , fmt .Errorf ("failed to remove values.yaml from templates directory: %w" , err )
213+ }
214+ }
215+
157216 logger .Infof ("Fetching openmcp-operator component version" )
158217
159- m .openMCPOperatorCV , err = m .compGetter .GetReferencedComponentVersion (ctx , m .compGetter .RootComponentVersion (), openMCPOperatorComponentName )
218+ m .openMCPOperatorCV , err = m .compGetter .GetReferencedComponentVersion (ctx , m .compGetter .RootComponentVersion (), OpenMCPOperatorComponentName )
160219 if err != nil {
161220 logger .Infof ("No openmcp-operator component version found: %v" , err )
162221 } else {
@@ -167,7 +226,8 @@ func (m *DeploymentRepoManager) Complete(ctx context.Context) (*DeploymentRepoMa
167226 if err != nil {
168227 return m , fmt .Errorf ("failed to parse git config: %w" , err )
169228 }
170- if err = m .gitConfig .Validate (); err != nil {
229+ err = m .gitConfig .Validate ()
230+ if err != nil {
171231 return m , fmt .Errorf ("invalid git config: %w" , err )
172232 }
173233
@@ -211,11 +271,24 @@ func (m *DeploymentRepoManager) ApplyTemplates(ctx context.Context) error {
211271 return fmt .Errorf ("no image resource found for openmcp-operator component version %s:%s" , m .openMCPOperatorCV .Component .Name , m .openMCPOperatorCV .Component .Version )
212272 }
213273
274+ imageName , imageTag , imageDigest , err := util .ParseImageVersionAndTag (* imageResources [0 ].Access .ImageReference )
275+ if err != nil {
276+ return fmt .Errorf ("failed to parse image reference %s: %w" , * imageResources [0 ].Access .ImageReference , err )
277+ }
278+
214279 templateInput ["openmcpOperator" ] = map [string ]interface {}{
215280 "version" : m .openMCPOperatorCV .Component .Version ,
216- "image" : * imageResources [ 0 ]. Access . ImageReference ,
281+ "image" : imageName ,
217282 "imagePullSecrets" : m .ImagePullSecrets ,
218283 }
284+
285+ if len (imageTag ) > 0 {
286+ templateInput ["openmcpOperator" ].(map [string ]interface {})["tag" ] = imageTag
287+ }
288+ if len (imageDigest ) > 0 {
289+ templateInput ["openmcpOperator" ].(map [string ]interface {})["digest" ] = imageDigest
290+ }
291+
219292 }
220293
221294 err := TemplateDir (m .templatesDir , templateInput , m .gitRepo )
@@ -253,7 +326,15 @@ func (m *DeploymentRepoManager) ApplyCustomResourceDefinitions(ctx context.Conte
253326
254327 logger .Infof ("Applying Custom Resource Definitions to deployment repository" )
255328
256- crdsDownloadDir := filepath .Join (m .gitRepoDir , "crds" )
329+ crdsDownloadDir := filepath .Join (m .gitRepoDir , TemplateResourcesDir , TemplateCRDsDir )
330+
331+ // if the CRDs directory already exists, remove it to ensure a clean state
332+ if _ , err := os .Stat (crdsDownloadDir ); err == nil {
333+ err = os .RemoveAll (crdsDownloadDir )
334+ if err != nil {
335+ return fmt .Errorf ("failed to remove existing CRD directory: %w" , err )
336+ }
337+ }
257338
258339 err := os .Mkdir (crdsDownloadDir , 0o755 )
259340 if err != nil {
@@ -265,19 +346,103 @@ func (m *DeploymentRepoManager) ApplyCustomResourceDefinitions(ctx context.Conte
265346 return fmt .Errorf ("failed to download CRD resource: %w" , err )
266347 }
267348
349+ // List all YAML files in the CRDs download directory
350+ entries , err := os .ReadDir (crdsDownloadDir )
351+ if err != nil {
352+ return fmt .Errorf ("failed to read CRD download directory: %w" , err )
353+ }
354+
355+ m .crdFiles = make ([]string , 0 )
356+ for _ , entry := range entries {
357+ if ! entry .IsDir () {
358+ fileName := entry .Name ()
359+ // Check if file has .yaml or .yml extension
360+ if filepath .Ext (fileName ) == ".yaml" || filepath .Ext (fileName ) == ".yml" {
361+ filePath := filepath .Join (crdsDownloadDir , fileName )
362+ m .crdFiles = append (m .crdFiles , filePath )
363+ logger .Tracef ("Added CRD file: %s" , filePath )
364+ }
365+ }
366+ }
367+
368+ logger .Infof ("Found %d CRD files" , len (m .crdFiles ))
369+
268370 workTree , err := m .gitRepo .Worktree ()
269371 if err != nil {
270372 return fmt .Errorf ("failed to get worktree: %w" , err )
271373 }
272374
273- _ , err = workTree .Add ("crds" )
375+ _ , err = workTree .Add (filepath . Join ( TemplateResourcesDir , TemplateCRDsDir ) )
274376 if err != nil {
275377 return fmt .Errorf ("failed to add CRD files: %w" , err )
276378 }
277379
278380 return nil
279381}
280382
383+ func (m * DeploymentRepoManager ) UpdateResourcesKustomization () error {
384+ files := make ([]string , 0 , len (m .PlatformServices )+ len (m .ServiceProviders )+ len (m .ClusterProviders )+ len (m .crdFiles ))
385+
386+ for _ , clusterProvider := range m .ClusterProviders {
387+ files = append (files , filepath .Join ("cluster-providers" , clusterProvider + ".yaml" ))
388+ }
389+
390+ for _ , serviceProvider := range m .ServiceProviders {
391+ files = append (files , filepath .Join ("service-providers" , serviceProvider + ".yaml" ))
392+ }
393+
394+ for _ , platformService := range m .PlatformServices {
395+ files = append (files , filepath .Join ("platform-services" , platformService + ".yaml" ))
396+ }
397+
398+ for _ , crdFile := range m .crdFiles {
399+ files = append (files , filepath .Join (TemplateCRDsDir , filepath .Base (crdFile )))
400+ }
401+
402+ // open resources root customization
403+ resourcesRootKustomizationPath := filepath .Join (TemplateResourcesDir , "kustomization.yaml" )
404+
405+ workTree , err := m .gitRepo .Worktree ()
406+ if err != nil {
407+ return fmt .Errorf ("failed to get worktree: %w" , err )
408+ }
409+
410+ fileInWorkTree , err := workTree .Filesystem .OpenFile (resourcesRootKustomizationPath , os .O_RDWR , 0644 )
411+ if err != nil {
412+ return fmt .Errorf ("failed to open file %s in worktree: %w" , resourcesRootKustomizationPath , err )
413+ }
414+
415+ defer func (pathInRepo billy.File ) {
416+ err := pathInRepo .Close ()
417+ if err != nil {
418+ _ , _ = fmt .Fprintf (os .Stderr , "failed to close file in worktree: %v\n " , err )
419+ }
420+ }(fileInWorkTree )
421+
422+ resourcesRootKustomization , err := ParseKustomizationFromFile (fileInWorkTree )
423+ if err != nil {
424+ return fmt .Errorf ("failed to parse resources root kustomization: %w" , err )
425+ }
426+
427+ _ , err = fileInWorkTree .Seek (0 , 0 )
428+ if err != nil {
429+ return fmt .Errorf ("failed to seek resources root kustomization: %w" , err )
430+ }
431+
432+ AddResourcesToKustomization (resourcesRootKustomization , files )
433+
434+ err = WriteKustomizationToFile (fileInWorkTree , resourcesRootKustomization )
435+ if err != nil {
436+ return fmt .Errorf ("failed to write resources root kustomization: %w" , err )
437+ }
438+
439+ if _ , err = workTree .Add (resourcesRootKustomizationPath ); err != nil {
440+ return fmt .Errorf ("failed to add resources root kustomization to git index: %w" , err )
441+ }
442+
443+ return nil
444+ }
445+
281446// CommitAndPushChanges commits all changes in the deployment repository and pushes them to the remote repository.
282447// If there are no changes to commit, it does nothing.
283448func (m * DeploymentRepoManager ) CommitAndPushChanges (_ context.Context ) error {
0 commit comments