Skip to content

Commit 97ef69c

Browse files
prkhrkatAsh-expiamayushm
authored
feat: ArgoCD v2.13.3 support for private chart providers (#6766)
* refactor: update chart handling and dependency management in GitOps operations * refactor: improve chart metadata handling and error logging in GitOps operations * refactor: add support for setting APIVersion in chart metadata * refactor: convert requirements.yaml to JSON before unmarshalling in GitOps operations * refactor: convert chart.yaml to JSON before unmarshalling in GitOps operations * refactor: enhance dependency validation by using unique keys for comparison in GitOps operations * refactor: convert chart metadata from JSON to YAML format for GitOps operations * refactor: update shouldMigrateProxyChartDependencies to accept expected chart metadata and remove requirements.yaml checks * docs: add developer guide for creating API specs and generating Go beans * wip --------- Co-authored-by: Ash-exp <asutosh2000ad@gmail.com> Co-authored-by: ayushmaheshwari <ayush@devtron.ai> Co-authored-by: iamayushm <32041961+iamayushm@users.noreply.github.com>
1 parent 839e287 commit 97ef69c

File tree

13 files changed

+330
-137
lines changed

13 files changed

+330
-137
lines changed

cmd/external-app/wire_gen.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

developers-guide/StructGenerator.md

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# Developer Guide: Creating API Specs and Generating Go Beans
2+
3+
## 1. Write the OpenAPI Spec
4+
5+
- Use OpenAPI 3.0+ YAML format.
6+
- Define:
7+
- `info`, `servers`, `tags`
8+
- `paths` for each endpoint, with HTTP methods, parameters, request bodies, and responses.
9+
- `components.schemas` for all request/response objects.
10+
- Use `x-oapi-codegen-extra-tags` for Go struct tags (e.g., validation).
11+
- The default behavior is to generate the required fields as non-pointer types and optional fields as pointer types.
12+
- Use `x-go-type-skip-optional-pointer: false` to ensure fields are generated as pointer type.
13+
- Use `x-go-json-ignore: true` to add `json:"-"` tag to a field, preventing it from being serialized in JSON.
14+
- Use `$ref` to reuse schema definitions.
15+
16+
**Reference:**
17+
See `specs/bulkEdit/v1beta2/bulk_edit.yaml` for a complete example.
18+
---
19+
20+
## 2. Create oapi-codegen Config
21+
22+
- Create a YAML config file (e.g., `oapi-models-config.yaml`) at the same level as your OpenAPI spec file.
23+
- This file configures the code generation process.
24+
- Set:
25+
- `package`: Go package name for generated code.
26+
- `generate.models`: `true` to generate Go structs.
27+
- `output`: Relative path for the generated file (e.g., `./bean/bean.go`).
28+
- `output-options`: Customize struct naming, skip pruning, etc.
29+
- `exclude-schemas`: Exclude error types if needed.
30+
31+
**Reference:**
32+
```yaml
33+
# yaml-language-server: ...
34+
package: api
35+
generate:
36+
models: true
37+
output-options:
38+
# to make sure that all types are generated
39+
skip-prune: true
40+
name-normalizer: ToCamelCaseWithDigits
41+
prefer-skip-optional-pointer: true
42+
prefer-skip-optional-pointer-on-container-types: true
43+
exclude-schemas:
44+
- "ErrorResponse"
45+
- "ApiError"
46+
output: {{ // relative path to the generator.go file }}
47+
```
48+
---
49+
50+
## 3. Add go:generate Directive
51+
- In your Go file (e.g., `generator.go`), add a `//go:generate` comment:
52+
```go
53+
//go:generate go run github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen "--config=../../../../specs/bulkEdit/v1beta2/oapi-models-config.yaml" "../../../../specs/bulkEdit/v1beta2/bulk_edit.yaml"
54+
```
55+
- Here `--config` is the relative path (from the `generator.go` file) to your config file, i.e. `oapi-models-config.yaml` file.
56+
- And the last argument is the path to your OpenAPI spec file, i.e. `bulk_edit.yaml`.
57+
---
58+
59+
## 4. Generate the Beans
60+
Run: (From the root dir)
61+
```bash
62+
go generate ./pkg/...
63+
```
64+
> This will generate Go structs at the path specified in your config (e.g., bean/bean.go).
65+
---
66+
67+
## 5. Best Practices
68+
Keep OpenAPI specs DRY: use $ref and shared schemas.
69+
Document every field and endpoint.
70+
Use validation tags for all struct fields.
71+
Exclude error response types from bean generation if not needed.
72+
Keep config and spec files versioned and close to your Go code.
73+
---
74+
75+
## Summary:
76+
Write your OpenAPI YAML, create a codegen config, add a go:generate directive, and run go generate to produce Go beans at the desired path.
77+
Follow the structure in bulk_edit.yaml and oapi-models-config.yaml for consistency.

internal/util/ChartTemplateService.go

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"encoding/json"
2323
"fmt"
2424
dockerRegistryRepository "github.com/devtron-labs/devtron/internal/sql/repository/dockerRegistry"
25+
chartRefBean "github.com/devtron-labs/devtron/pkg/deployment/manifest/deploymentTemplate/chartRef/bean"
2526
dirCopy "github.com/otiai10/copy"
2627
"go.opentelemetry.io/otel"
2728
"go.uber.org/zap"
@@ -60,7 +61,7 @@ type ChartCreateRequest struct {
6061

6162
type ChartCreateResponse struct {
6263
BuiltChartPath string
63-
valuesYaml string
64+
ChartMetaData *chart.Metadata
6465
}
6566

6667
type ChartTemplateService interface {
@@ -74,7 +75,7 @@ type ChartTemplateService interface {
7475
LoadChartInBytes(ChartPath string, deleteChart bool) ([]byte, error)
7576
LoadChartFromDir(dir string) (*chart.Chart, error)
7677
CreateZipFileForChart(chart *chart.Chart, outputChartPathDir string) ([]byte, error)
77-
PackageChart(tempReferenceTemplateDir string, chartMetaData *chart.Metadata) (*string, string, error)
78+
PackageChart(tempReferenceTemplateDir string, chartMetaData *chart.Metadata) (*chart.Metadata, *string, string, error)
7879
}
7980

8081
type ChartTemplateServiceImpl struct {
@@ -105,7 +106,7 @@ func (impl ChartTemplateServiceImpl) GetChartVersion(location string) (string, e
105106
return "", fmt.Errorf("%q is not a directory", location)
106107
}
107108

108-
chartYaml := filepath.Join(location, "Chart.yaml")
109+
chartYaml := filepath.Join(location, chartRefBean.CHART_YAML_FILE)
109110
if _, err := os.Stat(chartYaml); os.IsNotExist(err) {
110111
return "", fmt.Errorf("Chart.yaml file not present in the directory %q", location)
111112
}
@@ -135,7 +136,7 @@ func (impl ChartTemplateServiceImpl) FetchValuesFromReferenceChart(chartMetaData
135136
impl.logger.Errorw("error in copying chart for app", "app", chartMetaData.Name, "error", err)
136137
return nil, err
137138
}
138-
archivePath, valuesYaml, err := impl.PackageChart(chartDir, chartMetaData)
139+
_, archivePath, valuesYaml, err := impl.PackageChart(chartDir, chartMetaData)
139140
if err != nil {
140141
impl.logger.Errorw("error in creating archive", "err", err)
141142
return nil, err
@@ -175,7 +176,7 @@ func (impl ChartTemplateServiceImpl) BuildChart(ctx context.Context, chartMetaDa
175176
return "", err
176177
}
177178
_, span := otel.Tracer("orchestrator").Start(ctx, "impl.PackageChart")
178-
_, _, err = impl.PackageChart(tempReferenceTemplateDir, chartMetaData)
179+
_, _, _, err = impl.PackageChart(tempReferenceTemplateDir, chartMetaData)
179180
span.End()
180181
if err != nil {
181182
impl.logger.Errorw("error in creating archive", "err", err)
@@ -190,25 +191,24 @@ func (impl ChartTemplateServiceImpl) BuildChartProxyForHelmApps(chartCreateReque
190191
chartMetaData.APIVersion = "v2" // ensure always v2
191192
dir := impl.GetDir()
192193
chartDir := filepath.Join(CHART_WORKING_DIR_PATH, dir)
193-
impl.logger.Debugw("chart dir ", "chart", chartMetaData.Name, "dir", chartDir)
194+
chartCreateResponse.BuiltChartPath = chartDir
195+
impl.logger.Debugw("temp chart dir", "chart", chartMetaData.Name, "dir", chartDir)
194196
err := os.MkdirAll(chartDir, os.ModePerm) //hack for concurrency handling
195197
if err != nil {
196198
impl.logger.Errorw("err in creating dir", "dir", chartDir, "err", err)
197199
return chartCreateResponse, err
198200
}
199201
err = dirCopy.Copy(chartCreateRequest.ChartPath, chartDir)
200-
201202
if err != nil {
202203
impl.logger.Errorw("error in copying chart for app", "app", chartMetaData.Name, "error", err)
203204
return chartCreateResponse, err
204205
}
205-
_, valuesYaml, err := impl.PackageChart(chartDir, chartMetaData)
206+
chartMetaData, _, _, err = impl.PackageChart(chartDir, chartMetaData)
206207
if err != nil {
207208
impl.logger.Errorw("error in creating archive", "err", err)
208209
return chartCreateResponse, err
209210
}
210-
chartCreateResponse.valuesYaml = valuesYaml
211-
chartCreateResponse.BuiltChartPath = chartDir
211+
chartCreateResponse.ChartMetaData = chartMetaData
212212
return chartCreateResponse, nil
213213
}
214214

@@ -292,58 +292,64 @@ func (impl ChartTemplateServiceImpl) overrideChartMetaDataInDir(chartDir string,
292292
impl.logger.Errorw("error in loading template chart", "chartPath", chartDir, "err", err)
293293
return nil, err
294294
}
295+
if len(chartMetaData.APIVersion) > 0 {
296+
chart.Metadata.APIVersion = chartMetaData.APIVersion
297+
}
295298
if len(chartMetaData.Name) > 0 {
296299
chart.Metadata.Name = chartMetaData.Name
297300
}
298301
if len(chartMetaData.Version) > 0 {
299302
chart.Metadata.Version = chartMetaData.Version
300303
}
304+
if len(chartMetaData.Dependencies) > 0 {
305+
chart.Metadata.Dependencies = append(chart.Metadata.Dependencies, chartMetaData.Dependencies...)
306+
}
301307
chartMetaDataBytes, err := yaml.Marshal(chart.Metadata)
302308
if err != nil {
303309
impl.logger.Errorw("error in marshaling chartMetadata", "err", err)
304310
return chart, err
305311
}
306-
err = ioutil.WriteFile(filepath.Join(chartDir, "Chart.yaml"), chartMetaDataBytes, 0600)
312+
err = os.WriteFile(filepath.Join(chartDir, chartRefBean.CHART_YAML_FILE), chartMetaDataBytes, 0600)
307313
if err != nil {
308314
impl.logger.Errorw("err in writing Chart.yaml", "err", err)
309315
return chart, err
310316
}
311317
return chart, nil
312318
}
313319

314-
func (impl ChartTemplateServiceImpl) PackageChart(tempReferenceTemplateDir string, chartMetaData *chart.Metadata) (*string, string, error) {
320+
func (impl ChartTemplateServiceImpl) PackageChart(tempReferenceTemplateDir string, chartMetaData *chart.Metadata) (*chart.Metadata, *string, string, error) {
315321
valid, err := chartutil.IsChartDir(tempReferenceTemplateDir)
316322
if err != nil {
317323
impl.logger.Errorw("error in validating base chart", "dir", tempReferenceTemplateDir, "err", err)
318-
return nil, "", err
324+
return nil, nil, "", err
319325
}
320326
if !valid {
321327
impl.logger.Errorw("invalid chart at ", "dir", tempReferenceTemplateDir)
322-
return nil, "", fmt.Errorf("invalid base chart")
328+
return nil, nil, "", fmt.Errorf("invalid base chart")
323329
}
324330
chart, err := impl.overrideChartMetaDataInDir(tempReferenceTemplateDir, chartMetaData)
325331
if err != nil {
326332
impl.logger.Errorw("error in overriding chart metadata", "chartPath", tempReferenceTemplateDir, "err", err)
327-
return nil, "", err
333+
return nil, nil, "", err
328334
}
329335
archivePath, err := chartutil.Save(chart, tempReferenceTemplateDir)
330336
if err != nil {
331337
impl.logger.Errorw("error in saving", "err", err, "dir", tempReferenceTemplateDir)
332-
return nil, "", err
338+
return nil, nil, "", err
333339
}
334340
impl.logger.Debugw("chart archive path", "path", archivePath)
335341
var valuesYaml string
336342
byteValues, err := json.Marshal(chart.Values)
337343
if err != nil {
338344
impl.logger.Errorw("error in json Marshal values", "values", chart.Values, "err", err)
339-
return nil, "", err
345+
return nil, nil, "", err
340346
}
341347
if chart.Values != nil {
342348
valuesYaml = string(byteValues)
343349
} else {
344350
impl.logger.Warnw("values.yaml not found in helm chart", "dir", tempReferenceTemplateDir)
345351
}
346-
return &archivePath, valuesYaml, nil
352+
return chart.Metadata, &archivePath, valuesYaml, nil
347353
}
348354

349355
func (impl ChartTemplateServiceImpl) CleanDir(dir string) {
@@ -376,7 +382,7 @@ func (impl ChartTemplateServiceImpl) GetByteArrayRefChart(chartMetaData *chart.M
376382
impl.logger.Errorw("error in copying chart for app", "app", chartMetaData.Name, "error", err)
377383
return nil, err
378384
}
379-
activePath, _, err := impl.PackageChart(tempReferenceTemplateDir, chartMetaData)
385+
_, activePath, _, err := impl.PackageChart(tempReferenceTemplateDir, chartMetaData)
380386
if err != nil {
381387
impl.logger.Errorw("error in creating archive", "err", err)
382388
return nil, err

pkg/appStore/bean/bean.go

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,6 @@ func (chart *InstallAppVersionDTO) GetFluxDeploymentConfig() *bean2.DeploymentCo
298298
}
299299
}
300300

301-
// /
302301
type RefChartProxyDir string
303302

304303
const (
@@ -336,15 +335,6 @@ type AppNames struct {
336335
SuggestedName string `json:"suggestedName,omitempty"`
337336
}
338337

339-
type Dependencies struct {
340-
Dependencies []Dependency `json:"dependencies"`
341-
}
342-
type Dependency struct {
343-
Name string `json:"name"`
344-
Version string `json:"version"`
345-
Repository string `json:"repository"`
346-
}
347-
348338
const REFERENCE_TYPE_DEFAULT string = "DEFAULT"
349339
const REFERENCE_TYPE_TEMPLATE string = "TEMPLATE"
350340
const REFERENCE_TYPE_DEPLOYED string = "DEPLOYED"

pkg/appStore/installedApp/adapter/Adapter.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,13 @@ func ParseChartGitPushRequest(installAppRequestDTO *appStoreBean.InstallAppVersi
4141
}
4242
}
4343

44-
func ParseChartCreateRequest(appName string, includePackageChart bool) *util.ChartCreateRequest {
44+
func ParseChartCreateRequest(appName string, dependencies []*chart.Dependency, includePackageChart bool) *util.ChartCreateRequest {
4545
chartPath := getRefProxyChartPath()
4646
return &util.ChartCreateRequest{
4747
ChartMetaData: &chart.Metadata{
48-
Name: appName,
49-
Version: "1.0.1", // TODO Asutoh: Why not the actual version?
48+
Name: appName,
49+
Version: "1.0.1", // TODO Asutoh: Why not the actual version?
50+
Dependencies: dependencies,
5051
},
5152
ChartPath: chartPath,
5253
IncludePackageChart: includePackageChart,

0 commit comments

Comments
 (0)