Skip to content

Commit 0f5a041

Browse files
authored
Merge branch 'main' into taint_addition
2 parents eac1556 + f176162 commit 0f5a041

File tree

10 files changed

+1640
-237
lines changed

10 files changed

+1640
-237
lines changed

pkg/bundler/deployer/helm/helm.go

Lines changed: 8 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,12 @@ import (
2323
"path/filepath"
2424
"sort"
2525
"strings"
26-
"text/template"
2726
"time"
2827

29-
"gopkg.in/yaml.v3"
30-
3128
"github.com/NVIDIA/eidos/pkg/bundler/checksum"
3229
"github.com/NVIDIA/eidos/pkg/bundler/deployer/shared"
3330
"github.com/NVIDIA/eidos/pkg/errors"
31+
"github.com/NVIDIA/eidos/pkg/manifest"
3432
"github.com/NVIDIA/eidos/pkg/recipe"
3533
)
3634

@@ -323,7 +321,13 @@ func (g *Generator) generateComponentDirectories(ctx context.Context, input *Gen
323321
fmt.Sprintf("invalid manifest filename %q in component %s", filename, comp.Name))
324322
}
325323

326-
rendered, renderErr := renderManifest(content, comp, input.ComponentValues[comp.Name])
324+
rendered, renderErr := manifest.Render(content, manifest.RenderInput{
325+
ComponentName: comp.Name,
326+
Namespace: comp.Namespace,
327+
ChartName: comp.ChartName,
328+
ChartVersion: comp.ChartVersion,
329+
Values: input.ComponentValues[comp.Name],
330+
})
327331
if renderErr != nil {
328332
return nil, 0, errors.WrapWithContext(errors.ErrCodeInternal, "failed to render manifest template", renderErr,
329333
map[string]any{"component": comp.Name, "filename": filename})
@@ -480,88 +484,6 @@ func reverseComponents(components []ComponentData) []ComponentData {
480484
return reversed
481485
}
482486

483-
// manifestData provides Helm-compatible template data for rendering manifests.
484-
type manifestData struct {
485-
ComponentData
486-
Values map[string]any
487-
Release releaseData
488-
Chart chartData
489-
}
490-
491-
type releaseData struct {
492-
Namespace string
493-
Service string
494-
}
495-
496-
type chartData struct {
497-
Name string
498-
Version string
499-
}
500-
501-
// helmFuncMap returns Helm-compatible template functions for manifest rendering.
502-
func helmFuncMap() template.FuncMap {
503-
return template.FuncMap{
504-
"toYaml": func(v any) string {
505-
out, err := yaml.Marshal(v)
506-
if err != nil {
507-
return ""
508-
}
509-
return strings.TrimSuffix(string(out), "\n")
510-
},
511-
"nindent": func(indent int, s string) string {
512-
pad := strings.Repeat(" ", indent)
513-
lines := strings.Split(s, "\n")
514-
for i, line := range lines {
515-
if line != "" {
516-
lines[i] = pad + line
517-
}
518-
}
519-
return "\n" + strings.Join(lines, "\n")
520-
},
521-
"toString": func(v any) string {
522-
return fmt.Sprintf("%v", v)
523-
},
524-
"default": func(def, val any) any {
525-
if val == nil {
526-
return def
527-
}
528-
if s, ok := val.(string); ok && s == "" {
529-
return def
530-
}
531-
return val
532-
},
533-
}
534-
}
535-
536-
// renderManifest renders manifest content as a Go template with Helm-compatible
537-
// data and functions. Manifests can use .Values, .Release, .Chart, and functions
538-
// like toYaml, nindent, toString, and default.
539-
func renderManifest(content []byte, data ComponentData, values map[string]any) ([]byte, error) {
540-
tmpl, err := template.New("manifest").Funcs(helmFuncMap()).Parse(string(content))
541-
if err != nil {
542-
return nil, err
543-
}
544-
545-
md := manifestData{
546-
ComponentData: data,
547-
Values: map[string]any{data.Name: values},
548-
Release: releaseData{
549-
Namespace: data.Namespace,
550-
Service: "Helm",
551-
},
552-
Chart: chartData{
553-
Name: data.ChartName,
554-
Version: data.ChartVersion,
555-
},
556-
}
557-
558-
var buf strings.Builder
559-
if err := tmpl.Execute(&buf, md); err != nil {
560-
return nil, err
561-
}
562-
return []byte(buf.String()), nil
563-
}
564-
565487
// hasYAMLObjects returns true if content contains at least one YAML object
566488
// (a non-comment, non-blank, non-separator line).
567489
func hasYAMLObjects(content []byte) bool {

pkg/bundler/deployer/helm/helm_test.go

Lines changed: 0 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -1098,142 +1098,3 @@ func TestReverseComponents(t *testing.T) {
10981098
})
10991099
}
11001100
}
1101-
1102-
func TestRenderManifest(t *testing.T) {
1103-
tests := []struct {
1104-
name string
1105-
content string
1106-
data ComponentData
1107-
values map[string]any
1108-
wantErr bool
1109-
wantSub string // substring expected in output
1110-
}{
1111-
{
1112-
name: "valid template with values",
1113-
content: "namespace: {{ .Release.Namespace }}",
1114-
data: ComponentData{Name: "gpu-operator", Namespace: "gpu-operator"},
1115-
values: map[string]any{"enabled": true},
1116-
wantSub: "namespace: gpu-operator",
1117-
},
1118-
{
1119-
name: "invalid template syntax",
1120-
content: "{{ .Invalid {{ }}",
1121-
data: ComponentData{Name: "test"},
1122-
values: nil,
1123-
wantErr: true,
1124-
},
1125-
{
1126-
name: "chart data rendered",
1127-
content: "chart: {{ .Chart.Name }}-{{ .Chart.Version }}",
1128-
data: ComponentData{Name: "gpu-operator", ChartName: "gpu-operator", ChartVersion: "25.3.3"},
1129-
values: nil,
1130-
wantSub: "chart: gpu-operator-25.3.3",
1131-
},
1132-
{
1133-
name: "nil values map",
1134-
content: "name: {{ .Name }}",
1135-
data: ComponentData{Name: "test-comp"},
1136-
values: nil,
1137-
wantSub: "name: test-comp",
1138-
},
1139-
{
1140-
name: "toYaml function",
1141-
content: "config: {{ toYaml .Values.mycomp }}",
1142-
data: ComponentData{Name: "mycomp"},
1143-
values: map[string]any{"key": "value"},
1144-
wantSub: "key: value",
1145-
},
1146-
{
1147-
name: "default function",
1148-
content: `ns: {{ default "fallback" .Release.Namespace }}`,
1149-
data: ComponentData{Name: "test", Namespace: ""},
1150-
values: nil,
1151-
wantSub: "ns: fallback",
1152-
},
1153-
}
1154-
1155-
for _, tt := range tests {
1156-
t.Run(tt.name, func(t *testing.T) {
1157-
result, err := renderManifest([]byte(tt.content), tt.data, tt.values)
1158-
if (err != nil) != tt.wantErr {
1159-
t.Fatalf("error = %v, wantErr %v", err, tt.wantErr)
1160-
}
1161-
if err == nil && tt.wantSub != "" {
1162-
if !strings.Contains(string(result), tt.wantSub) {
1163-
t.Errorf("output %q does not contain %q", string(result), tt.wantSub)
1164-
}
1165-
}
1166-
})
1167-
}
1168-
}
1169-
1170-
func TestHelmFuncMap(t *testing.T) {
1171-
funcs := helmFuncMap()
1172-
1173-
t.Run("toYaml", func(t *testing.T) {
1174-
fn := funcs["toYaml"].(func(any) string)
1175-
1176-
// map input
1177-
got := fn(map[string]string{"key": "value"})
1178-
if !strings.Contains(got, "key: value") {
1179-
t.Errorf("toYaml(map) = %q, want to contain 'key: value'", got)
1180-
}
1181-
1182-
// string input
1183-
got = fn("hello")
1184-
if got != "hello" {
1185-
t.Errorf("toYaml(string) = %q, want 'hello'", got)
1186-
}
1187-
1188-
// nil input
1189-
got = fn(nil)
1190-
if got != "null" {
1191-
t.Errorf("toYaml(nil) = %q, want 'null'", got)
1192-
}
1193-
})
1194-
1195-
t.Run("nindent", func(t *testing.T) {
1196-
fn := funcs["nindent"].(func(int, string) string)
1197-
got := fn(4, "line1\nline2")
1198-
if !strings.Contains(got, " line1") {
1199-
t.Errorf("nindent should indent line1: got %q", got)
1200-
}
1201-
if !strings.Contains(got, " line2") {
1202-
t.Errorf("nindent should indent line2: got %q", got)
1203-
}
1204-
if got[0] != '\n' {
1205-
t.Error("nindent should start with newline")
1206-
}
1207-
})
1208-
1209-
t.Run("toString", func(t *testing.T) {
1210-
fn := funcs["toString"].(func(any) string)
1211-
if got := fn(42); got != "42" {
1212-
t.Errorf("toString(42) = %q, want '42'", got)
1213-
}
1214-
if got := fn(nil); got != "<nil>" {
1215-
t.Errorf("toString(nil) = %q, want '<nil>'", got)
1216-
}
1217-
})
1218-
1219-
t.Run("default", func(t *testing.T) {
1220-
fn := funcs["default"].(func(any, any) any)
1221-
1222-
// nil value returns default
1223-
if got := fn("fallback", nil); got != "fallback" {
1224-
t.Errorf("default(fallback, nil) = %v, want 'fallback'", got)
1225-
}
1226-
// empty string returns default
1227-
if got := fn("fallback", ""); got != "fallback" {
1228-
t.Errorf("default(fallback, empty) = %v, want 'fallback'", got)
1229-
}
1230-
// non-empty returns value
1231-
if got := fn("fallback", "actual"); got != "actual" {
1232-
t.Errorf("default(fallback, actual) = %v, want 'actual'", got)
1233-
}
1234-
// non-string non-nil returns value
1235-
if got := fn("fallback", 42); got != 42 {
1236-
t.Errorf("default(fallback, 42) = %v, want 42", got)
1237-
}
1238-
})
1239-
}

pkg/component/helpers.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,10 @@ func MarshalYAMLWithHeader(v any, header ValuesHeader) ([]byte, error) {
5757
var buf bytes.Buffer
5858

5959
// Write header comments
60-
buf.WriteString(fmt.Sprintf("# %s Helm Values\n", header.ComponentName))
60+
fmt.Fprintf(&buf, "# %s Helm Values\n", header.ComponentName)
6161
buf.WriteString("# Generated from Cloud Native Stack Recipe\n")
62-
buf.WriteString(fmt.Sprintf("# Bundler Version: %s\n", header.BundlerVersion))
63-
buf.WriteString(fmt.Sprintf("# Recipe Version: %s\n", header.RecipeVersion))
62+
fmt.Fprintf(&buf, "# Bundler Version: %s\n", header.BundlerVersion)
63+
fmt.Fprintf(&buf, "# Recipe Version: %s\n", header.RecipeVersion)
6464
buf.WriteString("\n")
6565

6666
// Serialize the values

pkg/defaults/timeouts.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,10 @@ const (
131131
// ResourceVerificationTimeout is the timeout for verifying individual
132132
// expected resources exist and are healthy during deployment validation.
133133
ResourceVerificationTimeout = 10 * time.Second
134+
135+
// ComponentRenderTimeout is the maximum time to render a single component
136+
// via helm template or manifest file rendering during resource discovery.
137+
ComponentRenderTimeout = 60 * time.Second
134138
)
135139

136140
// Pod operation timeouts for validation and agent operations.

0 commit comments

Comments
 (0)