Add line numbers for debugging invalid yaml

Signed-off-by: Artem Mikhalitsin <artemmikhalitsin@gmail.com>
pull/8699/head
Artem Mikhalitsin 4 years ago
parent 1ec0aacb88
commit 70b45a8a90

@ -1,13 +1,13 @@
---
# Source: chart-with-template-with-invalid-yaml/templates/alpine-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: "release-name-my-alpine"
spec:
containers:
- name: waiter
image: "alpine:3.9"
command: ["/bin/sleep","9000"]
invalid
1 apiVersion: v1
2 kind: Pod
3 metadata:
4 name: "release-name-my-alpine"
5 spec:
6 containers:
7 - name: waiter
8 image: "alpine:3.9"
9 command: ["/bin/sleep","9000"]
10 invalid
Error: YAML parse error on chart-with-template-with-invalid-yaml/templates/alpine-pod.yaml: error converting YAML to JSON: yaml: line 11: could not find expected ':'

@ -17,12 +17,14 @@ limitations under the License.
package action
import (
"bufio"
"bytes"
"fmt"
"os"
"path"
"path/filepath"
"regexp"
"strconv"
"strings"
"github.com/pkg/errors"
@ -173,6 +175,7 @@ func (cfg *Configuration) renderResources(ch *chart.Chart, values chartutil.Valu
if strings.TrimSpace(content) == "" {
continue
}
content = addLineNumbers(content)
fmt.Fprintf(b, "---\n# Source: %s\n%s\n", name, content)
}
return hs, b, "", err
@ -225,6 +228,45 @@ func (cfg *Configuration) renderResources(ch *chart.Chart, values chartutil.Valu
return hs, b, notes, nil
}
// getResourceLines separates a resource into individual lines
func getResourceLines(resource string) []string {
result := []string{}
in := strings.NewReader(resource)
scanner := bufio.NewScanner(in)
for scanner.Scan() {
result = append(result, scanner.Text())
}
return result
}
// addLineNumbers numbers the lines of a resource
func addLineNumbers(resource string) string {
resourceLines := getResourceLines(resource)
lineFormat := getNumberedLineFormat(len(resourceLines))
var result bytes.Buffer
for index, line := range resourceLines {
lineNumber := index + 1
lineWithNum := fmt.Sprintf(lineFormat, lineNumber, line)
fmt.Fprintln(&result, lineWithNum)
}
return result.String()
}
// getNumberedLineFormat determines which format to use
// when printing lines of a resource based on the
// max possible number of digits
func getNumberedLineFormat(maxLineNumber int) string {
strBuilder := strings.Builder{}
numDigits := len(strconv.Itoa(maxLineNumber))
// Format is like "%3d %s"
strBuilder.WriteString("%")
strBuilder.WriteString(strconv.Itoa(numDigits))
strBuilder.WriteString("d %s")
return strBuilder.String()
}
// RESTClientGetter gets the rest client
type RESTClientGetter interface {
ToRESTConfig() (*rest.Config, error)

@ -217,6 +217,16 @@ func withSampleIncludingIncorrectTemplates() chartOption {
}
}
func withBadYaml() chartOption {
return func(opts *chartOptions) {
badYamlTemplate := []*chart.File{
{Name: "templates/goodyaml", Data: []byte("goodbye:world")},
{Name: "templates/badyaml", Data: []byte("this:isn't:\nhow:you_write_yaml")},
}
opts.Templates = append(opts.Templates, badYamlTemplate...)
}
}
func withMultipleManifestTemplate() chartOption {
return func(opts *chartOptions) {
sampleTemplates := []*chart.File{

@ -291,6 +291,34 @@ func TestInstallReleaseIncorrectTemplate_DryRun(t *testing.T) {
}
}
func TestInstallRelease_LineNumbers(t *testing.T) {
is := assert.New(t)
t.Run("Should print line numbers on incorrect yaml when flag is set", func(t *testing.T) {
instAction := installAction(t)
instAction.DryRun = true
dummyVals := map[string]interface{}{}
badChart := buildChart(withBadYaml())
res, _ := instAction.Run(badChart, dummyVals)
// Should have line numbers for bad yaml
is.Contains(res.Manifest, "1 this:isn't:")
is.Contains(res.Manifest, "2 how:you_write_yaml")
// Should have line numbers for every other resource
is.Contains(res.Manifest, "1 kind: ConfigMap")
})
t.Run("Should not print line numbers on well-formatted yaml", func(t *testing.T) {
instAction := installAction(t)
instAction.DryRun = true
dummyVals := map[string]interface{}{}
goodChart := buildChart(withSampleTemplates())
res, _ := instAction.Run(goodChart, dummyVals)
is.Contains(res.Manifest, "hello: world")
is.NotContains(res.Manifest, "1 hello: Earth")
})
}
func TestInstallRelease_NoHooks(t *testing.T) {
is := assert.New(t)
instAction := installAction(t)

Loading…
Cancel
Save