Skip to content

Commit

Permalink
pkg.parser: add the ability to parse manifests with comments
Browse files Browse the repository at this point in the history
Signed-off-by: Muvaffak Onus <[email protected]>
  • Loading branch information
muvaf committed Aug 8, 2023
1 parent b689131 commit 0393f96
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 18 deletions.
42 changes: 24 additions & 18 deletions pkg/parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import (
"context"
"io"
"strings"
"unicode"

"github.com/spf13/afero"
corev1 "k8s.io/api/core/v1"
Expand Down Expand Up @@ -91,7 +90,7 @@ func New(meta, obj ObjectCreaterTyper) *PackageParser {
// decode objects recognized by the meta scheme, then attempts to decode objects
// recognized by the object scheme. Objects not recognized by either scheme
// return an error rather than being skipped.
func (p *PackageParser) Parse(_ context.Context, reader io.ReadCloser) (*Package, error) { //nolint:gocyclo // Only at 11.
func (p *PackageParser) Parse(_ context.Context, reader io.ReadCloser) (*Package, error) {
pkg := NewPackage()
if reader == nil {
return pkg, nil
Expand All @@ -101,27 +100,25 @@ func (p *PackageParser) Parse(_ context.Context, reader io.ReadCloser) (*Package
dm := json.NewSerializerWithOptions(json.DefaultMetaFactory, p.metaScheme, p.metaScheme, json.SerializerOptions{Yaml: true})
do := json.NewSerializerWithOptions(json.DefaultMetaFactory, p.objScheme, p.objScheme, json.SerializerOptions{Yaml: true})
for {
bytes, err := yr.Read()
content, err := yr.Read()
if err != nil && !errors.Is(err, io.EOF) {
return pkg, err
}
if errors.Is(err, io.EOF) {
break
}
if len(bytes) == 0 {
content = cleanYAML(content)
if len(content) == 0 {
continue
}
if isWhiteSpace(bytes) {
continue
}
m, _, err := dm.Decode(bytes, nil, nil)
m, _, err := dm.Decode(content, nil, nil)
if err != nil {
// NOTE(hasheddan): we only try to decode with object scheme if the
// error is due the object not being registered in the meta scheme.
if !runtime.IsNotRegisteredError(err) {
return pkg, annotateErr(err, reader)
}
o, _, err := do.Decode(bytes, nil, nil)
o, _, err := do.Decode(content, nil, nil)
if err != nil {
return pkg, annotateErr(err, reader)
}
Expand All @@ -133,17 +130,26 @@ func (p *PackageParser) Parse(_ context.Context, reader io.ReadCloser) (*Package
return pkg, nil
}

// isWhiteSpace determines whether the passed in bytes are all unicode white
// space.
func isWhiteSpace(bytes []byte) bool {
empty := true
for _, b := range bytes {
if !unicode.IsSpace(rune(b)) {
empty = false
break
// cleanYAML cleans up YAML by removing empty and commented out lines which
// cause issues with decoding.
func cleanYAML(y []byte) []byte {
lines := []string{}
for _, line := range strings.Split(string(y), "\n") {
trimmed := strings.TrimSpace(line)
if trimmed == "" || strings.HasPrefix(trimmed, "#") {
continue
}
lines = append(lines, line)
}
// If all left are just separators and/or terminators, then there is no
// content.
for _, c := range lines {
trimmed := strings.TrimSpace(c)
if trimmed != "---" && trimmed != "..." {
return []byte(strings.Join(lines, "\n"))
}
}
return empty
return nil
}

// annotateErr annotates an error if the reader is an AnnotatedReadCloser.
Expand Down
32 changes: 32 additions & 0 deletions pkg/parser/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,17 @@ metadata:

deployBytes = []byte(`apiVersion: apps/v1
kind: Deployment
metadata:
name: test`)

commentedOutBytes = []byte(`# apiVersion: apps/v1
# kind: Deployment
# metadata:
# name: test`)
manifestWithComments = []byte(`
apiVersion: apiextensions.k8s.io/v1beta1
# Some Comment
kind: CustomResourceDefinition
metadata:
name: test`)

Expand All @@ -79,6 +90,9 @@ func TestParser(t *testing.T) {
emptyFs := afero.NewMemMapFs()
_ = afero.WriteFile(emptyFs, "empty.yaml", []byte(""), 0o644)
_ = afero.WriteFile(emptyFs, "bad.yam", []byte("definitely not yaml"), 0o644)
commentedFs := afero.NewMemMapFs()
_ = afero.WriteFile(commentedFs, "commented.yaml", commentedOutBytes, 0o644)
_ = afero.WriteFile(commentedFs, ".crossplane/realmanifest.yaml", manifestWithComments, 0o644)
objScheme := runtime.NewScheme()
_ = apiextensions.AddToScheme(objScheme)
metaScheme := runtime.NewScheme()
Expand Down Expand Up @@ -128,6 +142,24 @@ func TestParser(t *testing.T) {
objects: []runtime.Object{crd, crd, crd, crd},
},
},
"FsBackendCommentedOut": {
reason: "should parse filesystem successfully even if all the files are commented out",
parser: New(metaScheme, objScheme),
backend: NewFsBackend(commentedFs, FsDir("."), FsFilters(SkipDirs(), SkipNotYAML(), SkipPath(".crossplane/*"))),
pkg: &Package{
meta: nil,
objects: nil,
},
},
"FsBackendWithComments": {
reason: "should parse filesystem successfully when some of the manifests contain comments",
parser: New(metaScheme, objScheme),
backend: NewFsBackend(commentedFs, FsDir("."), FsFilters(SkipDirs(), SkipNotYAML())),
pkg: &Package{
meta: nil,
objects: []runtime.Object{crd},
},
},
"FsBackendAll": {
reason: "should parse filesystem successfully with multiple objects in single file",
parser: New(metaScheme, objScheme),
Expand Down

0 comments on commit 0393f96

Please sign in to comment.