@@ -18,17 +18,138 @@ package generateManifest
1818
1919import (
2020 "context"
21+ "errors"
2122 "fmt"
23+ "os"
24+ "path/filepath"
25+ "strings"
26+
2227 "github.com/devtron-labs/common-lib/utils/yaml"
2328 "github.com/devtron-labs/devtron/api/helm-app/gRPC"
2429 "github.com/devtron-labs/devtron/internal/sql/repository/app"
2530 "github.com/devtron-labs/devtron/internal/sql/repository/pipelineConfig"
2631 clusterBean "github.com/devtron-labs/devtron/pkg/cluster/bean"
2732 "github.com/devtron-labs/devtron/pkg/cluster/environment/repository"
33+ "github.com/go-pg/pg"
2834 "go.opentelemetry.io/otel"
2935 "golang.org/x/exp/maps"
36+ k8sYaml "sigs.k8s.io/yaml"
37+ )
38+
39+ const (
40+ releaseValuesYamlFile = "release-values.yaml"
41+ releaseValuesYmlFile = "release-values.yml"
42+ imageDescriptorTemplateFile = ".image_descriptor_template.json"
3043)
3144
45+ // readReleaseValuesJsonFromRefChart looks for release-values.yaml / release-values.yml
46+ // at the root of the reference chart directory and returns its content converted to
47+ // JSON. Returns an empty string (without erroring the caller) when the file is absent
48+ // or unreadable — not every chart defines release overrides, and manifest generation
49+ // must still proceed. This mirrors the file-discovery logic in
50+ // ChartTemplateServiceImpl.getValues so behaviour matches the save-time path.
51+ func (impl DeploymentTemplateServiceImpl ) readReleaseValuesJsonFromRefChart (refChartPath string ) string {
52+ data , ok := impl .readFileFromRefChart (refChartPath , releaseValuesYamlFile , releaseValuesYmlFile )
53+ if ! ok {
54+ return ""
55+ }
56+ jsonBytes , err := k8sYaml .YAMLToJSON (data )
57+ if err != nil {
58+ impl .Logger .Errorw ("error in converting release-values yaml to json" , "refChartPath" , refChartPath , "err" , err )
59+ return ""
60+ }
61+ return string (jsonBytes )
62+ }
63+
64+ // readImageDescriptorTemplateFromRefChart reads .image_descriptor_template.json from
65+ // the reference chart directory. Used when the chart hasn't been saved to the DB yet
66+ // and chartDto.ImageDescriptorTemplate isn't available. Returns an empty string on
67+ // missing file / read error — RenderJson handles an empty template gracefully.
68+ func (impl DeploymentTemplateServiceImpl ) readImageDescriptorTemplateFromRefChart (refChartPath string ) string {
69+ data , ok := impl .readFileFromRefChart (refChartPath , imageDescriptorTemplateFile )
70+ if ! ok {
71+ return ""
72+ }
73+ return string (data )
74+ }
75+
76+ // readFileFromRefChart scans refChartPath (non-recursive) for the first file whose
77+ // name (case-insensitive) matches one of fileNames and returns its contents. ok=false
78+ // means the path was empty, unreadable, or the file was absent — all logged at the
79+ // appropriate level by the caller's context.
80+ func (impl DeploymentTemplateServiceImpl ) readFileFromRefChart (refChartPath string , fileNames ... string ) (data []byte , ok bool ) {
81+ if len (refChartPath ) == 0 {
82+ return nil , false
83+ }
84+ entries , err := os .ReadDir (refChartPath )
85+ if err != nil {
86+ impl .Logger .Errorw ("error in reading ref chart dir" , "refChartPath" , refChartPath , "err" , err )
87+ return nil , false
88+ }
89+ targets := make (map [string ]struct {}, len (fileNames ))
90+ for _ , name := range fileNames {
91+ targets [strings .ToLower (name )] = struct {}{}
92+ }
93+ for _ , entry := range entries {
94+ if entry .IsDir () {
95+ continue
96+ }
97+ if _ , match := targets [strings .ToLower (entry .Name ())]; ! match {
98+ continue
99+ }
100+ path := filepath .Clean (filepath .Join (refChartPath , entry .Name ()))
101+ contents , err := os .ReadFile (path )
102+ if err != nil {
103+ impl .Logger .Errorw ("error in reading file from ref chart" , "path" , path , "err" , err )
104+ return nil , false
105+ }
106+ return contents , true
107+ }
108+ return nil , false
109+ }
110+
111+ // resolveImageDescriptorTemplate returns the image descriptor template for the app's
112+ // chart. It prefers the saved chart in DB; if no chart has been saved for this
113+ // (appId, chartRefId) pair (pg.ErrNoRows), it falls back to the reference chart's
114+ // .image_descriptor_template.json on disk so manifest rendering works before save.
115+ // Any other DB error is returned to the caller.
116+ func (impl DeploymentTemplateServiceImpl ) resolveImageDescriptorTemplate (appId , chartRefId int , refChartPath string ) (string , error ) {
117+ chartDto , err := impl .chartReadService .GetByAppIdAndChartRefId (appId , chartRefId )
118+ if err == nil {
119+ return chartDto .ImageDescriptorTemplate , nil
120+ }
121+ if ! errors .Is (err , pg .ErrNoRows ) {
122+ impl .Logger .Errorw ("error in getting chart by appId and chartRefId" , "appId" , appId , "chartRefId" , chartRefId , "err" , err )
123+ return "" , err
124+ }
125+ return impl .readImageDescriptorTemplateFromRefChart (refChartPath ), nil
126+ }
127+
128+ // mergeReleaseOverrideIntoValuesYaml merges the chart's ReleaseOverride JSON
129+ // on top of the given values YAML. On any failure the input valuesYaml is
130+ // returned unchanged so template rendering can still proceed.
131+ func (impl DeploymentTemplateServiceImpl ) mergeReleaseOverrideIntoValuesYaml (valuesYaml , releaseOverrideJson string ) string {
132+ if len (releaseOverrideJson ) == 0 || releaseOverrideJson == "{}" {
133+ return valuesYaml
134+ }
135+ valuesJsonByte , err := k8sYaml .YAMLToJSON ([]byte (valuesYaml ))
136+ if err != nil {
137+ impl .Logger .Errorw ("error in converting values yaml to json" , "err" , err )
138+ return valuesYaml
139+ }
140+ mergedJsonBytes , err := impl .mergeUtil .JsonPatch (valuesJsonByte , []byte (releaseOverrideJson ))
141+ if err != nil {
142+ impl .Logger .Errorw ("error in merging releaseOverride into values yaml" , "releaseOverrideJson" , releaseOverrideJson , "err" , err )
143+ return valuesYaml
144+ }
145+ mergedYamlBytes , err := k8sYaml .JSONToYAML (mergedJsonBytes )
146+ if err != nil {
147+ impl .Logger .Errorw ("error in converting merged json to yaml" , "err" , err )
148+ return valuesYaml
149+ }
150+ return string (mergedYamlBytes )
151+ }
152+
32153func (impl DeploymentTemplateServiceImpl ) constructRotatePodResponse (templateChartResponse []* gRPC.TemplateChartResponse , appNameToId map [string ]int , environment * repository.Environment ) (* RestartPodResponse , error ) {
33154 appIdToResourceIdentifier := make (map [int ]* ResourceIdentifierResponse )
34155 for _ , tcResp := range templateChartResponse {
0 commit comments