Skip to content

Commit 56c0d41

Browse files
committed
Support reference chart-based manifest generation before saving the app chart
**Details**: - Modified `patchReleaseAttributes` to read `release-values.yaml` and `.image_descriptor_template.json` directly from the reference chart directory. - Added helper methods to handle file discovery and fallback logic for reference chart overrides. - Updated `resolveImageDescriptorTemplate` to handle missing DB entries using reference chart files. - Removed unused `ReleaseOverride` field from chart beans and adaptors.
1 parent 98c522b commit 56c0d41

4 files changed

Lines changed: 104 additions & 8 deletions

File tree

pkg/chart/adaptor/adaptor.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ func ChartAdaptor(chartModel *chartRepoRepository.Chart,
4141
IsCustomGitRepository: deploymentConfig.ConfigType == bean2.CUSTOM.String(),
4242
ImageDescriptorTemplate: chartModel.ImageDescriptorTemplate,
4343
TargetRevision: targetRevision,
44-
ReleaseOverride: chartModel.ReleaseOverride,
4544
}
4645
if chartModel.Latest {
4746
templateRequest.LatestChartVersion = chartModel.ChartVersion

pkg/chart/bean/bean.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ type TemplateRequest struct {
5151
UserId int32 `json:"-"`
5252
LatestChartVersion string `json:"-"`
5353
ImageDescriptorTemplate string `json:"-"`
54-
ReleaseOverride string `json:"-"`
5554
}
5655

5756
type ChartUpgradeRequest struct {

pkg/generateManifest/DeploymentTemplateService.go

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -489,7 +489,7 @@ func (impl DeploymentTemplateServiceImpl) GenerateManifest(ctx context.Context,
489489
if len(request.DeploymentAppName) != 0 {
490490
releaseName = request.DeploymentAppName
491491
}
492-
mergedValuesYaml := impl.patchReleaseAttributes(request, valuesYaml)
492+
mergedValuesYaml := impl.patchReleaseAttributes(request, valuesYaml, refChartPath)
493493
installReleaseRequest := &gRPC.InstallReleaseRequest{
494494
AppName: request.AppName,
495495
ChartName: refChart.Name,
@@ -531,7 +531,7 @@ func (impl DeploymentTemplateServiceImpl) GenerateManifest(ctx context.Context,
531531
return response, nil
532532
}
533533

534-
func (impl DeploymentTemplateServiceImpl) patchReleaseAttributes(request *DeploymentTemplateRequest, valuesYaml string) (mergedValuesYaml string) {
534+
func (impl DeploymentTemplateServiceImpl) patchReleaseAttributes(request *DeploymentTemplateRequest, valuesYaml string, refChartPath string) (mergedValuesYaml string) {
535535
mergedValuesYaml = valuesYaml
536536

537537
valuesJsonByte, err := yaml.YAMLToJSON([]byte(valuesYaml))
@@ -540,14 +540,13 @@ func (impl DeploymentTemplateServiceImpl) patchReleaseAttributes(request *Deploy
540540
return
541541
}
542542

543-
chartDto, err := impl.chartReadService.GetByAppIdAndChartRefId(request.AppId, request.ChartRefId)
543+
imageDescriptorTemplate, err := impl.resolveImageDescriptorTemplate(request.AppId, request.ChartRefId, refChartPath)
544544
if err != nil {
545-
impl.Logger.Errorw("error in getting the chart using appId and chartRefId", "appId", request.AppId, "chartRefId", request.ChartRefId, "err", err)
546545
return
547546
}
548547

549548
releaseAttributeJson, err := app.NewReleaseAttributes("", "", request.PipelineName, "",
550-
request.AppId, request.EnvId, 0, pointer.Bool(false)).RenderJson(chartDto.ImageDescriptorTemplate)
549+
request.AppId, request.EnvId, 0, pointer.Bool(false)).RenderJson(imageDescriptorTemplate)
551550

552551
if err != nil {
553552
impl.Logger.Errorw("error in rendering release attributes into image descriptor template ", "releaseAttributeJson", releaseAttributeJson, "err", err)
@@ -567,7 +566,11 @@ func (impl DeploymentTemplateServiceImpl) patchReleaseAttributes(request *Deploy
567566

568567
mergedValuesYaml = string(mergedYamlBytes)
569568

570-
mergedValuesYaml = impl.mergeReleaseOverrideIntoValuesYaml(mergedValuesYaml, chartDto.ReleaseOverride)
569+
// Read release-values.yaml straight from the reference chart dir so the manifest
570+
// can be rendered before an app-level chart is saved (chartDto.ReleaseOverride is
571+
// only populated post-save).
572+
releaseOverrideJson := impl.readReleaseValuesJsonFromRefChart(refChartPath)
573+
mergedValuesYaml = impl.mergeReleaseOverrideIntoValuesYaml(mergedValuesYaml, releaseOverrideJson)
571574

572575
return mergedValuesYaml
573576
}

pkg/generateManifest/helper.go

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,113 @@ package generateManifest
1818

1919
import (
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"
3036
k8sYaml "sigs.k8s.io/yaml"
3137
)
3238

39+
const (
40+
releaseValuesYamlFile = "release-values.yaml"
41+
releaseValuesYmlFile = "release-values.yml"
42+
imageDescriptorTemplateFile = ".image_descriptor_template.json"
43+
)
44+
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+
33128
// mergeReleaseOverrideIntoValuesYaml merges the chart's ReleaseOverride JSON
34129
// on top of the given values YAML. On any failure the input valuesYaml is
35130
// returned unchanged so template rendering can still proceed.

0 commit comments

Comments
 (0)