mirror of
https://github.com/yannh/kubeconform.git
synced 2026-02-25 20:51:57 +00:00
Add support for -exit-on-error
This commit is contained in:
parent
7e66425b02
commit
358f145023
5 changed files with 54 additions and 15 deletions
|
|
@ -1,6 +1,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/xeipuuv/gojsonschema"
|
"github.com/xeipuuv/gojsonschema"
|
||||||
"os"
|
"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
|
success := true
|
||||||
result := make(chan bool)
|
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")
|
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
|
result <- success
|
||||||
close(result)
|
close(result)
|
||||||
}()
|
}()
|
||||||
|
|
@ -144,12 +153,13 @@ func realMain() int {
|
||||||
var errors <-chan error
|
var errors <-chan error
|
||||||
validationResults := make(chan validator.Result)
|
validationResults := make(chan validator.Result)
|
||||||
|
|
||||||
successChan := processResults(o, validationResults)
|
ctx := context.Background()
|
||||||
|
successChan := processResults(ctx, o, validationResults, cfg.ExitOnError)
|
||||||
|
|
||||||
if isStdin {
|
if isStdin {
|
||||||
resourcesChan, errors = resource.FromStream("stdin", os.Stdin)
|
resourcesChan, errors = resource.FromStream(ctx, "stdin", os.Stdin)
|
||||||
} else {
|
} else {
|
||||||
resourcesChan, errors = resource.FromFiles(cfg.Files...)
|
resourcesChan, errors = resource.FromFiles(ctx, cfg.Files...)
|
||||||
}
|
}
|
||||||
|
|
||||||
c := cache.New()
|
c := cache.New()
|
||||||
|
|
@ -165,10 +175,13 @@ func realMain() int {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
for err := range errors {
|
for err := range errors {
|
||||||
if err != nil {
|
if err == nil {
|
||||||
if err, ok := err.(resource.DiscoveryError); ok {
|
continue
|
||||||
validationResults <- validator.NewError(err.Path, err.Err)
|
}
|
||||||
}
|
|
||||||
|
if err, ok := err.(resource.DiscoveryError); ok {
|
||||||
|
validationResults <- validator.NewError(err.Path, err.Err)
|
||||||
|
ctx.Done()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
wg.Done()
|
wg.Done()
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
|
ExitOnError bool
|
||||||
Files []string
|
Files []string
|
||||||
SchemaLocations []string
|
SchemaLocations []string
|
||||||
SkipKinds map[string]bool
|
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.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.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.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.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.BoolVar(&c.Summary, "summary", false, "print a summary at the end")
|
||||||
flags.IntVar(&c.NumberOfWorkers, "n", 4, "number of goroutines to run concurrently")
|
flags.IntVar(&c.NumberOfWorkers, "n", 4, "number of goroutines to run concurrently")
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@ package resource
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
@ -25,17 +27,25 @@ func (de DiscoveryError) Error() string {
|
||||||
return de.Err.Error()
|
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)
|
resources := make(chan Resource)
|
||||||
errors := make(chan error)
|
errors := make(chan error)
|
||||||
|
stop := false
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
<-ctx.Done()
|
||||||
|
stop = true
|
||||||
|
}()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for _, path := range paths {
|
for _, path := range paths {
|
||||||
// we handle errors in the walk function directly
|
// we handle errors in the walk function directly
|
||||||
// so it should be safe to discard the outer error
|
// 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 {
|
if err != nil {
|
||||||
errors <- DiscoveryError{p, err}
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -45,13 +55,11 @@ func FromFiles(paths ...string) (<-chan Resource, <-chan error) {
|
||||||
|
|
||||||
f, err := os.Open(p)
|
f, err := os.Open(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errors <- DiscoveryError{p, err}
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
b, err := ioutil.ReadAll(f)
|
b, err := ioutil.ReadAll(f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errors <- DiscoveryError{p, err}
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -61,6 +69,10 @@ func FromFiles(paths ...string) (<-chan Resource, <-chan error) {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
errors <- DiscoveryError{path, err}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
close(resources)
|
close(resources)
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,20 @@ package resource
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"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)
|
resources := make(chan Resource)
|
||||||
errors := make(chan error)
|
errors := make(chan error)
|
||||||
|
stop := false
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
<-ctx.Done()
|
||||||
|
stop = true
|
||||||
|
}()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
data, err := ioutil.ReadAll(r)
|
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"))
|
rawResources := bytes.Split(data, []byte("---\n"))
|
||||||
for _, rawResource := range rawResources {
|
for _, rawResource := range rawResources {
|
||||||
|
if stop == true {
|
||||||
|
break
|
||||||
|
}
|
||||||
resources <- Resource{Path: path, Bytes: rawResource}
|
resources <- Resource{Path: path, Bytes: rawResource}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package resource_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"github.com/yannh/kubeconform/pkg/resource"
|
"github.com/yannh/kubeconform/pkg/resource"
|
||||||
"io"
|
"io"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
@ -143,7 +144,8 @@ kind: Deployment
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, testCase := range testCases {
|
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
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
wg.Add(2)
|
wg.Add(2)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue