diff --git a/pkg/resource/files.go b/pkg/resource/files.go index aa8ed13..429c8ad 100644 --- a/pkg/resource/files.go +++ b/pkg/resource/files.go @@ -87,21 +87,16 @@ func findFilesInFolders(ctx context.Context, paths []string, ignoreFilePatterns return files, errors } -func findResourcesInFile(p string, resources chan<- Resource, errors chan<- error, buf []byte) { - f, err := os.Open(p) - defer f.Close() - if err != nil { - errors <- DiscoveryError{p, err} - return - } - +func findResourcesInReader(p string, f io.Reader, resources chan<- Resource, errors chan<- error, buf []byte) { scanner := bufio.NewScanner(f) scanner.Buffer(buf, len(buf)) scanner.Split(SplitYAMLDocument) nRes := 0 for res := scanner.Scan(); res != false; res = scanner.Scan() { - resources <- Resource{Path: p, Bytes: []byte(scanner.Text())} - nRes++ + if len(scanner.Text()) > 0 { + resources <- Resource{Path: p, Bytes: []byte(scanner.Text())} + nRes++ + } } if err := scanner.Err(); err != nil { errors <- DiscoveryError{p, err} @@ -111,6 +106,18 @@ func findResourcesInFile(p string, resources chan<- Resource, errors chan<- erro } } +func findResourcesInFile(p string, resources chan<- Resource, errors chan<- error, buf []byte) { + f, err := os.Open(p) + defer f.Close() + + if err != nil { + errors <- DiscoveryError{p, err} + return + } + + findResourcesInReader(p, f, resources, errors, buf) +} + func FromFiles(ctx context.Context, paths []string, ignoreFilePatterns []string) (<-chan Resource, <-chan error) { resources := make(chan Resource) diff --git a/pkg/resource/files_test.go b/pkg/resource/files_test.go index 9aa38d2..fa1ca2e 100644 --- a/pkg/resource/files_test.go +++ b/pkg/resource/files_test.go @@ -2,6 +2,8 @@ package resource import ( "os" + "strings" + "sync" "testing" "time" ) @@ -87,3 +89,116 @@ func TestIsJSONFile(t *testing.T) { } } } + +func TestFindResourcesInReader(t *testing.T) { + maxResourceSize := 4 * 1024 * 1024 // 4MB ought to be enough for everybody + buf := make([]byte, maxResourceSize) // We reuse this to avoid multiple large memory allocations + + for i, testCase := range []struct { + filePath string + yamlData string + res []Resource + errs []error + }{ + { + "manifest.yaml", + ``, + []Resource{ + { + Path: "manifest.yaml", + Bytes: nil, + sig: nil, + }, + }, + nil, + }, + { + "manifest.yaml", + `--- +foo: bar +`, + []Resource{ + { + Path: "manifest.yaml", + Bytes: []byte("---\nfoo: bar\n"), + sig: nil, + }, + }, + nil, + }, + { + "manifest.yaml", + `--- +foo: bar +--- +lorem: ipsum +`, + []Resource{ + { + Path: "manifest.yaml", + Bytes: []byte("---\nfoo: bar"), + sig: nil, + }, + { + Path: "manifest.yaml", + Bytes: []byte("lorem: ipsum\n"), + sig: nil, + }, + }, + nil, + }, + } { + res := make(chan Resource) + errs := make(chan error) + receivedResources := []Resource{} + receivedErrs := []error{} + var wg sync.WaitGroup + wg.Add(1) + + go func() { + for { + select { + case receivedResource, ok := <-res: + if ok { + receivedResources = append(receivedResources, receivedResource) + continue + } + res = nil + + case receivedErr, ok := <-errs: + if ok { + receivedErrs = append(receivedErrs, receivedErr) + continue + } + errs = nil + } + + if res == nil && errs == nil { + break + } + } + wg.Done() + }() + + r := strings.NewReader(testCase.yamlData) + findResourcesInReader(testCase.filePath, r, res, errs, buf) + close(res) + close(errs) + wg.Wait() + + if len(receivedResources) != len(testCase.res) { + t.Errorf("test %d: expected %d resources, received %d: %+v", i, len(testCase.res), len(receivedResources), receivedResources) + continue + } + + for j, r := range receivedResources { + if r.Path != testCase.res[j].Path { + t.Errorf("test %d, resource %d, expected path %s, received %s", i, j, testCase.res[j].Path, r.Path) + } + + if string(r.Bytes) != string(testCase.res[j].Bytes) { + t.Errorf("test %d, resource %d, expected Bytes %s, received %s", i, j, string(testCase.res[j].Bytes), string(r.Bytes)) + } + } + } +}