Skip to content

Commit 8aaf8ff

Browse files
committed
fix: unable to process all CRDs
Signed-off-by: Souvik Kar Mahapatra <[email protected]>
1 parent e2db515 commit 8aaf8ff

File tree

1 file changed

+133
-15
lines changed

1 file changed

+133
-15
lines changed

utils/helm/helm.go

+133-15
Original file line numberDiff line numberDiff line change
@@ -145,29 +145,147 @@ func LoadHelmChart(path string, w io.Writer, extractOnlyCrds bool, kubeVersion s
145145
if err != nil {
146146
return ErrLoadHelmChart(err, path)
147147
}
148-
if extractOnlyCrds {
149-
crds := chart.CRDObjects()
150-
size := len(crds)
151-
for index, crd := range crds {
152-
_, err := w.Write(crd.File.Data)
153-
if err != nil {
154-
errs = append(errs, err)
155-
continue
156-
}
157-
if index == size-1 {
158-
break
159-
}
160-
_, _ = w.Write([]byte("\n---\n"))
161-
}
162-
} else {
148+
149+
if !extractOnlyCrds {
163150
manifests, err := DryRunHelmChart(chart, kubeVersion)
164151
if err != nil {
165152
return ErrLoadHelmChart(err, path)
166153
}
167154
_, err = w.Write(manifests)
155+
return err
156+
}
157+
158+
// Look for all the yaml file in the helm dir that is a CRD
159+
err = filepath.WalkDir(path, func(filePath string, d fs.DirEntry, err error) error {
168160
if err != nil {
169161
return ErrLoadHelmChart(err, path)
170162
}
163+
if !d.IsDir() && (strings.HasSuffix(filePath, ".yaml") || strings.HasSuffix(filePath, ".yml")) {
164+
data, err := os.ReadFile(filePath)
165+
if err != nil {
166+
return err
167+
}
168+
169+
if isCRDFile(data) {
170+
data = RemoveHelmPlaceholders(data)
171+
if err := writeToWriter(w, data); err != nil {
172+
errs = append(errs, err)
173+
}
174+
}
175+
}
176+
return nil
177+
})
178+
179+
if err != nil {
180+
errs = append(errs, err)
171181
}
182+
172183
return utils.CombineErrors(errs, "\n")
173184
}
185+
186+
func writeToWriter(w io.Writer, data []byte) error {
187+
trimmedData := bytes.TrimSpace(data)
188+
189+
if len(trimmedData) == 0 {
190+
return nil
191+
}
192+
193+
// Check if the document already starts with separators
194+
startsWithSeparator := bytes.HasPrefix(trimmedData, []byte("---"))
195+
196+
// If it doesn't start with ---, add one
197+
if !startsWithSeparator {
198+
if _, err := w.Write([]byte("---\n")); err != nil {
199+
return err
200+
}
201+
}
202+
203+
if _, err := w.Write(trimmedData); err != nil {
204+
return err
205+
}
206+
207+
_, err := w.Write([]byte("\n"))
208+
return err
209+
}
210+
211+
// checks if the content is a CRD
212+
// NOTE: kubernetes.IsCRD(manifest string) already exists however using that leads to cyclic dependency
213+
func isCRDFile(content []byte) bool {
214+
str := string(content)
215+
return strings.Contains(str, "kind: CustomResourceDefinition")
216+
}
217+
218+
// RemoveHelmPlaceholders - replaces helm templates placeholder with YAML compatible empty value
219+
// since these templates cause YAML parsing error
220+
// NOTE: this is a quick fix
221+
func RemoveHelmPlaceholders(data []byte) []byte {
222+
content := string(data)
223+
224+
// Regular expressions to match different Helm template patterns
225+
// Match multiline template blocks that start with {{- and end with }}
226+
multilineRegex := regexp.MustCompile(`(?s){{-?\s*.*?\s*}}`)
227+
228+
// Match single line template expressions
229+
singleLineRegex := regexp.MustCompile(`{{-?\s*[^}]*}}`)
230+
231+
// Process the content line by line to maintain YAML structure
232+
lines := strings.Split(content, "\n")
233+
var processedLines []string
234+
235+
for _, line := range lines {
236+
trimmedLine := strings.TrimSpace(line)
237+
if trimmedLine == "" {
238+
processedLines = append(processedLines, line)
239+
continue
240+
}
241+
242+
// Handle multiline template blocks first
243+
if multilineRegex.MatchString(line) {
244+
// If line starts with indentation + list marker
245+
if listMatch := regexp.MustCompile(`^(\s*)- `).FindStringSubmatch(line); listMatch != nil {
246+
// Convert list item to empty map to maintain structure
247+
processedLines = append(processedLines, listMatch[1]+"- {}")
248+
continue
249+
}
250+
251+
// If it's a value assignment with multiline template
252+
if valueMatch := regexp.MustCompile(`^(\s*)(\w+):\s*{{`).FindStringSubmatch(line); valueMatch != nil {
253+
// Preserve the key with empty map value
254+
processedLines = append(processedLines, valueMatch[1]+valueMatch[2]+": {}")
255+
continue
256+
}
257+
258+
// For other multiline templates, replace with empty line
259+
processedLines = append(processedLines, "")
260+
continue
261+
}
262+
263+
// Handle single line template expressions
264+
if singleLineRegex.MatchString(line) {
265+
// If line contains a key-value pair
266+
if keyMatch := regexp.MustCompile(`^(\s*)(\w+):\s*{{`).FindStringSubmatch(line); keyMatch != nil {
267+
// Preserve the key with empty string value
268+
processedLines = append(processedLines, keyMatch[1]+keyMatch[2]+": ")
269+
continue
270+
}
271+
272+
// If line is a list item
273+
if listMatch := regexp.MustCompile(`^(\s*)- `).FindStringSubmatch(line); listMatch != nil {
274+
// Convert to empty map to maintain list structure
275+
processedLines = append(processedLines, listMatch[1]+"- {}")
276+
continue
277+
}
278+
279+
// For standalone template expressions, remove them (includes, control statements)
280+
line = singleLineRegex.ReplaceAllString(line, "")
281+
if strings.TrimSpace(line) != "" {
282+
processedLines = append(processedLines, line)
283+
}
284+
continue
285+
}
286+
287+
processedLines = append(processedLines, line)
288+
}
289+
290+
return []byte(strings.Join(processedLines, "\n"))
291+
}

0 commit comments

Comments
 (0)