Skip to content

Commit b59a4c4

Browse files
Merge pull request #6956 from devtron-labs/custom-chart-fix-oss
misc: Custom chart fix oss
2 parents 2dca8fe + 56c0d41 commit b59a4c4

4 files changed

Lines changed: 139 additions & 10 deletions

File tree

pkg/chart/adaptor/adaptor.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@ package adaptor
33
import (
44
"encoding/json"
55
"fmt"
6+
"strings"
7+
68
apiGitOpsBean "github.com/devtron-labs/devtron/api/bean/gitOps"
79
"github.com/devtron-labs/devtron/internal/util"
810
"github.com/devtron-labs/devtron/pkg/chart/bean"
911
chartRepoRepository "github.com/devtron-labs/devtron/pkg/chartRepo/repository"
1012
bean2 "github.com/devtron-labs/devtron/pkg/deployment/common/bean"
1113
util2 "github.com/devtron-labs/devtron/util"
12-
"strings"
1314
)
1415

1516
// ChartAdaptor converts db object chartRepoRepository.Chart to bean.TemplateRequest

pkg/chart/bean/bean.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package bean
1818

1919
import (
2020
"encoding/json"
21+
2122
"github.com/devtron-labs/devtron/internal/sql/models"
2223
bean2 "github.com/devtron-labs/devtron/pkg/pipeline/bean"
2324
)

pkg/generateManifest/DeploymentTemplateService.go

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ package generateManifest
1919
import (
2020
"context"
2121
"fmt"
22+
"net/http"
23+
"regexp"
24+
"strconv"
25+
"sync"
26+
2227
"github.com/caarlos0/env"
2328
"github.com/devtron-labs/common-lib/utils/k8s"
2429
"github.com/devtron-labs/devtron/api/helm-app/gRPC"
@@ -53,11 +58,7 @@ import (
5358
"golang.org/x/exp/maps"
5459
chart2 "helm.sh/helm/v3/pkg/chart"
5560
"k8s.io/utils/pointer"
56-
"net/http"
57-
"regexp"
5861
"sigs.k8s.io/yaml"
59-
"strconv"
60-
"sync"
6162
)
6263

6364
// TODO: Prakash, move this interface to pkg/deployment/manifest/deploymentTemplate, both are same
@@ -488,7 +489,7 @@ func (impl DeploymentTemplateServiceImpl) GenerateManifest(ctx context.Context,
488489
if len(request.DeploymentAppName) != 0 {
489490
releaseName = request.DeploymentAppName
490491
}
491-
mergedValuesYaml := impl.patchReleaseAttributes(request, valuesYaml)
492+
mergedValuesYaml := impl.patchReleaseAttributes(request, valuesYaml, refChartPath)
492493
installReleaseRequest := &gRPC.InstallReleaseRequest{
493494
AppName: request.AppName,
494495
ChartName: refChart.Name,
@@ -530,7 +531,7 @@ func (impl DeploymentTemplateServiceImpl) GenerateManifest(ctx context.Context,
530531
return response, nil
531532
}
532533

533-
func (impl DeploymentTemplateServiceImpl) patchReleaseAttributes(request *DeploymentTemplateRequest, valuesYaml string) (mergedValuesYaml string) {
534+
func (impl DeploymentTemplateServiceImpl) patchReleaseAttributes(request *DeploymentTemplateRequest, valuesYaml string, refChartPath string) (mergedValuesYaml string) {
534535
mergedValuesYaml = valuesYaml
535536

536537
valuesJsonByte, err := yaml.YAMLToJSON([]byte(valuesYaml))
@@ -539,14 +540,13 @@ func (impl DeploymentTemplateServiceImpl) patchReleaseAttributes(request *Deploy
539540
return
540541
}
541542

542-
chartDto, err := impl.chartReadService.GetByAppIdAndChartRefId(request.AppId, request.ChartRefId)
543+
imageDescriptorTemplate, err := impl.resolveImageDescriptorTemplate(request.AppId, request.ChartRefId, refChartPath)
543544
if err != nil {
544-
impl.Logger.Errorw("error in getting the chart using appId and chartRefId", "appId", request.AppId, "chartRefId", request.ChartRefId, "err", err)
545545
return
546546
}
547547

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

551551
if err != nil {
552552
impl.Logger.Errorw("error in rendering release attributes into image descriptor template ", "releaseAttributeJson", releaseAttributeJson, "err", err)
@@ -566,6 +566,12 @@ func (impl DeploymentTemplateServiceImpl) patchReleaseAttributes(request *Deploy
566566

567567
mergedValuesYaml = string(mergedYamlBytes)
568568

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)
574+
569575
return mergedValuesYaml
570576
}
571577

pkg/generateManifest/helper.go

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,138 @@ 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"
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+
32153
func (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

Comments
 (0)