Add support for -exit-on-error

This commit is contained in:
Yann Hamon 2020-11-08 10:08:14 +01:00
parent 7e66425b02
commit 358f145023
5 changed files with 54 additions and 15 deletions

View file

@ -1,6 +1,7 @@
package main
import (
"context"
"fmt"
"github.com/xeipuuv/gojsonschema"
"os"
@ -85,7 +86,7 @@ func ValidateResources(resources <-chan resource.Resource, validationResults cha
}
}
func processResults(o output.Output, validationResults <-chan validator.Result) <-chan bool {
func processResults(ctx context.Context, o output.Output, validationResults <-chan validator.Result, exitOnError bool) <-chan bool {
success := true
result := make(chan bool)
@ -99,7 +100,15 @@ func processResults(o output.Output, validationResults <-chan validator.Result)
fmt.Fprint(os.Stderr, "failed writing log\n")
}
}
if success == false && exitOnError {
ctx.Done() // early exit - signal to stop searching for resources
break
}
}
for range validationResults { // allow resource finders to exit
}
result <- success
close(result)
}()
@ -144,12 +153,13 @@ func realMain() int {
var errors <-chan error
validationResults := make(chan validator.Result)
successChan := processResults(o, validationResults)
ctx := context.Background()
successChan := processResults(ctx, o, validationResults, cfg.ExitOnError)
if isStdin {
resourcesChan, errors = resource.FromStream("stdin", os.Stdin)
resourcesChan, errors = resource.FromStream(ctx, "stdin", os.Stdin)
} else {
resourcesChan, errors = resource.FromFiles(cfg.Files...)
resourcesChan, errors = resource.FromFiles(ctx, cfg.Files...)
}
c := cache.New()
@ -165,10 +175,13 @@ func realMain() int {
wg.Add(1)
go func() {
for err := range errors {
if err != nil {
if err, ok := err.(resource.DiscoveryError); ok {
validationResults <- validator.NewError(err.Path, err.Err)
}
if err == nil {
continue
}
if err, ok := err.(resource.DiscoveryError); ok {
validationResults <- validator.NewError(err.Path, err.Err)
ctx.Done()
}
}
wg.Done()

View file

@ -9,6 +9,7 @@ import (
)
type Config struct {
ExitOnError bool
Files []string
SchemaLocations []string
SkipKinds map[string]bool
@ -59,6 +60,7 @@ func FromFlags(progName string, args []string) (Config, string, error) {
flags.StringVar(&c.KubernetesVersion, "kubernetes-version", "1.18.0", "version of Kubernetes to validate against")
flags.Var(&schemaLocationsParam, "schema-location", "override schemas location search path (can be specified multiple times)")
flags.StringVar(&skipKindsCSV, "skip", "", "comma-separated list of kinds to ignore")
flags.BoolVar(&c.ExitOnError, "exit-on-error", false, "immediately stop execution when the first error is encountered")
flags.BoolVar(&c.IgnoreMissingSchemas, "ignore-missing-schemas", false, "skip files with missing schemas instead of failing")
flags.BoolVar(&c.Summary, "summary", false, "print a summary at the end")
flags.IntVar(&c.NumberOfWorkers, "n", 4, "number of goroutines to run concurrently")

View file

@ -2,6 +2,8 @@ package resource
import (
"bytes"
"context"
"io"
"io/ioutil"
"os"
"path/filepath"
@ -25,17 +27,25 @@ func (de DiscoveryError) Error() string {
return de.Err.Error()
}
func FromFiles(paths ...string) (<-chan Resource, <-chan error) {
func FromFiles(ctx context.Context, paths ...string) (<-chan Resource, <-chan error) {
resources := make(chan Resource)
errors := make(chan error)
stop := false
go func() {
<-ctx.Done()
stop = true
}()
go func() {
for _, path := range paths {
// we handle errors in the walk function directly
// so it should be safe to discard the outer error
_ = filepath.Walk(path, func(p string, i os.FileInfo, err error) error {
err := filepath.Walk(path, func(p string, i os.FileInfo, err error) error {
if stop == true {
return io.EOF
}
if err != nil {
errors <- DiscoveryError{p, err}
return err
}
@ -45,13 +55,11 @@ func FromFiles(paths ...string) (<-chan Resource, <-chan error) {
f, err := os.Open(p)
if err != nil {
errors <- DiscoveryError{p, err}
return err
}
b, err := ioutil.ReadAll(f)
if err != nil {
errors <- DiscoveryError{p, err}
return err
}
@ -61,6 +69,10 @@ func FromFiles(paths ...string) (<-chan Resource, <-chan error) {
return nil
})
if err != nil && err != io.EOF {
errors <- DiscoveryError{path, err}
}
}
close(resources)

View file

@ -2,13 +2,20 @@ package resource
import (
"bytes"
"context"
"io"
"io/ioutil"
)
func FromStream(path string, r io.Reader) (<-chan Resource, <-chan error) {
func FromStream(ctx context.Context, path string, r io.Reader) (<-chan Resource, <-chan error) {
resources := make(chan Resource)
errors := make(chan error)
stop := false
go func() {
<-ctx.Done()
stop = true
}()
go func() {
data, err := ioutil.ReadAll(r)
@ -18,6 +25,9 @@ func FromStream(path string, r io.Reader) (<-chan Resource, <-chan error) {
rawResources := bytes.Split(data, []byte("---\n"))
for _, rawResource := range rawResources {
if stop == true {
break
}
resources <- Resource{Path: path, Bytes: rawResource}
}

View file

@ -2,6 +2,7 @@ package resource_test
import (
"bytes"
"context"
"github.com/yannh/kubeconform/pkg/resource"
"io"
"reflect"
@ -143,7 +144,8 @@ kind: Deployment
}
for _, testCase := range testCases {
resChan, errChan := resource.FromStream(testCase.Have.Path, testCase.Have.Reader)
ctx := context.Background()
resChan, errChan := resource.FromStream(ctx, testCase.Have.Path, testCase.Have.Reader)
var wg sync.WaitGroup
wg.Add(2)