mirror of
https://github.com/yannh/kubeconform.git
synced 2026-02-19 01:47:02 +00:00
better logic mgmt in output plugins, go fmt
This commit is contained in:
parent
224e9ca17d
commit
8eb297d4c4
11 changed files with 93 additions and 79 deletions
13
main.go
13
main.go
|
|
@ -26,7 +26,7 @@ type validationResult struct {
|
||||||
|
|
||||||
// filter returns true if the file should be skipped
|
// filter returns true if the file should be skipped
|
||||||
// Returning an array, this Reader might container multiple resources
|
// Returning an array, this Reader might container multiple resources
|
||||||
func validateFile(f io.Reader, regs []registry.Registry, k8sVersion string, skip func(signature resource.Signature)bool) []validationResult {
|
func validateFile(f io.Reader, regs []registry.Registry, k8sVersion string, skip func(signature resource.Signature) bool) []validationResult {
|
||||||
rawResource, err := ioutil.ReadAll(f)
|
rawResource, err := ioutil.ReadAll(f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []validationResult{{err: fmt.Errorf("failed reading file: %s", err)}}
|
return []validationResult{{err: fmt.Errorf("failed reading file: %s", err)}}
|
||||||
|
|
@ -99,13 +99,13 @@ func realMain() int {
|
||||||
}
|
}
|
||||||
|
|
||||||
splitKinds := strings.Split(skipKinds, ",")
|
splitKinds := strings.Split(skipKinds, ",")
|
||||||
kinds := map[string]bool {}
|
kinds := map[string]bool{}
|
||||||
for _, kind := range splitKinds {
|
for _, kind := range splitKinds {
|
||||||
kinds[kind] = true
|
kinds[kind] = true
|
||||||
}
|
}
|
||||||
filter := func(signature resource.Signature) bool {
|
filter := func(signature resource.Signature) bool {
|
||||||
isSkipKind, ok := kinds[signature.Kind]
|
isSkipKind, ok := kinds[signature.Kind]
|
||||||
return ok && isSkipKind
|
return ok && isSkipKind
|
||||||
}
|
}
|
||||||
|
|
||||||
registries := []registry.Registry{}
|
registries := []registry.Registry{}
|
||||||
|
|
@ -135,7 +135,7 @@ func realMain() int {
|
||||||
}()
|
}()
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
for i:=0; i<nWorkers; i++ {
|
for i := 0; i < nWorkers; i++ {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
@ -151,7 +151,6 @@ func realMain() int {
|
||||||
res := validateFile(f, registries, k8sVersion, filter)
|
res := validateFile(f, registries, k8sVersion, filter)
|
||||||
f.Close()
|
f.Close()
|
||||||
|
|
||||||
|
|
||||||
for _, resourceValidation := range res {
|
for _, resourceValidation := range res {
|
||||||
o.Write(filename, resourceValidation.err, resourceValidation.skipped)
|
o.Write(filename, resourceValidation.err, resourceValidation.skipped)
|
||||||
}
|
}
|
||||||
|
|
@ -168,4 +167,4 @@ func realMain() int {
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
os.Exit(realMain())
|
os.Exit(realMain())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
8
pkg/cache/main.go
vendored
8
pkg/cache/main.go
vendored
|
|
@ -10,15 +10,15 @@ import (
|
||||||
var mu sync.Mutex
|
var mu sync.Mutex
|
||||||
var schemas map[string]*gojsonschema.Schema
|
var schemas map[string]*gojsonschema.Schema
|
||||||
|
|
||||||
func init () {
|
func init() {
|
||||||
schemas = map[string]*gojsonschema.Schema{}
|
schemas = map[string]*gojsonschema.Schema{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithCache(downloadSchema func(string, string, string) (*gojsonschema.Schema, error)) (func (string, string, string) (*gojsonschema.Schema, error)) {
|
func WithCache(downloadSchema func(string, string, string) (*gojsonschema.Schema, error)) func(string, string, string) (*gojsonschema.Schema, error) {
|
||||||
return func(resourceKind, resourceAPIVersion, k8sVersion string) (*gojsonschema.Schema, error) {
|
return func(resourceKind, resourceAPIVersion, k8sVersion string) (*gojsonschema.Schema, error) {
|
||||||
cacheKey := fmt.Sprintf("%s-%s-%s", resourceKind, resourceAPIVersion, k8sVersion)
|
cacheKey := fmt.Sprintf("%s-%s-%s", resourceKind, resourceAPIVersion, k8sVersion)
|
||||||
mu.Lock()
|
mu.Lock()
|
||||||
cachedSchema, ok := schemas[cacheKey];
|
cachedSchema, ok := schemas[cacheKey]
|
||||||
mu.Unlock()
|
mu.Unlock()
|
||||||
if ok {
|
if ok {
|
||||||
return cachedSchema, nil
|
return cachedSchema, nil
|
||||||
|
|
@ -39,4 +39,4 @@ func WithCache(downloadSchema func(string, string, string) (*gojsonschema.Schema
|
||||||
|
|
||||||
return schema, err
|
return schema, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,4 +29,4 @@ func FindYamlInDir(dir string, fileBatches chan<- []string, batchSize int) error
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,45 +3,44 @@ package output
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/yannh/kubeconform/pkg/validator"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type result struct {
|
type result struct {
|
||||||
Filename string `json:"filename"`
|
Filename string `json:"filename"`
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
Msg string `json:"msg"`
|
Msg string `json:"msg"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type JSONOutput struct {
|
type JSONOutput struct {
|
||||||
withSummary bool
|
withSummary bool
|
||||||
results []result
|
results []result
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewJSONOutput(withSummary bool) Output{
|
func NewJSONOutput(withSummary bool) Output {
|
||||||
return &JSONOutput{
|
return &JSONOutput{
|
||||||
withSummary: withSummary,
|
withSummary: withSummary,
|
||||||
results: []result{},
|
results: []result{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *JSONOutput) Write(filename string,err error, skipped bool) {
|
func (o *JSONOutput) Write(filename string, err error, skipped bool) {
|
||||||
status := "VALID"
|
msg, st := "", ""
|
||||||
msg := ""
|
|
||||||
|
|
||||||
if err != nil {
|
s := status(err, skipped)
|
||||||
|
switch {
|
||||||
|
case s == VALID:
|
||||||
|
st = "VALID"
|
||||||
|
case s == INVALID:
|
||||||
|
st = "INVALID"
|
||||||
msg = err.Error()
|
msg = err.Error()
|
||||||
if _, ok := err.(validator.InvalidResourceError); ok {
|
case s == ERROR:
|
||||||
status = "INVALID"
|
st = "ERROR"
|
||||||
} else {
|
msg = err.Error()
|
||||||
status = "ERROR"
|
case s == SKIPPED:
|
||||||
}
|
st = "SKIPPED"
|
||||||
}
|
}
|
||||||
|
|
||||||
if skipped {
|
o.results = append(o.results, result{Filename: filename, Status: st, Msg: msg})
|
||||||
status = "SKIPPED"
|
|
||||||
}
|
|
||||||
|
|
||||||
o.results = append(o.results, result{Filename: filename, Status: status, Msg: msg})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *JSONOutput) Flush() {
|
func (o *JSONOutput) Flush() {
|
||||||
|
|
@ -51,13 +50,13 @@ func (o *JSONOutput) Flush() {
|
||||||
if o.withSummary {
|
if o.withSummary {
|
||||||
jsonObj := struct {
|
jsonObj := struct {
|
||||||
Resources []result `json:"resources"`
|
Resources []result `json:"resources"`
|
||||||
Summary struct {
|
Summary struct {
|
||||||
Valid int `json:"valid"`
|
Valid int `json:"valid"`
|
||||||
Invalid int `json:"invalid"`
|
Invalid int `json:"invalid"`
|
||||||
Errors int `json:"errors"`
|
Errors int `json:"errors"`
|
||||||
Skipped int `json:"skipped"`
|
Skipped int `json:"skipped"`
|
||||||
} `json:"summary"`
|
} `json:"summary"`
|
||||||
} {
|
}{
|
||||||
Resources: o.results,
|
Resources: o.results,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -74,15 +73,15 @@ func (o *JSONOutput) Flush() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err = json.MarshalIndent(jsonObj,"", " ")
|
res, err = json.MarshalIndent(jsonObj, "", " ")
|
||||||
} else {
|
} else {
|
||||||
jsonObj := struct {
|
jsonObj := struct {
|
||||||
Resources []result
|
Resources []result
|
||||||
} {
|
}{
|
||||||
Resources: o.results,
|
Resources: o.results,
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err = json.MarshalIndent(jsonObj,"", " ")
|
res, err = json.MarshalIndent(jsonObj, "", " ")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,31 @@
|
||||||
package output
|
package output
|
||||||
|
|
||||||
|
import "github.com/yannh/kubeconform/pkg/validator"
|
||||||
|
|
||||||
|
const (
|
||||||
|
VALID = iota
|
||||||
|
INVALID = iota
|
||||||
|
ERROR = iota
|
||||||
|
SKIPPED = iota
|
||||||
|
)
|
||||||
|
|
||||||
type Output interface {
|
type Output interface {
|
||||||
Write (filename string, err error, skipped bool)
|
Write(filename string, err error, skipped bool)
|
||||||
Flush ()
|
Flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func status(err error, skipped bool) int {
|
||||||
|
if err != nil {
|
||||||
|
if _, ok := err.(validator.InvalidResourceError); ok {
|
||||||
|
return INVALID
|
||||||
|
} else {
|
||||||
|
return ERROR
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if skipped {
|
||||||
|
return SKIPPED
|
||||||
|
}
|
||||||
|
|
||||||
|
return VALID
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,39 +2,32 @@ package output
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/yannh/kubeconform/pkg/validator"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type TextOutput struct {
|
type TextOutput struct {
|
||||||
withSummary bool
|
withSummary bool
|
||||||
nValid, nInvalid, nErrors, nSkipped int
|
nValid, nInvalid, nErrors, nSkipped int
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTextOutput(withSummary bool) Output {
|
func NewTextOutput(withSummary bool) Output {
|
||||||
return &TextOutput{withSummary, 0,0,0, 0}
|
return &TextOutput{withSummary, 0, 0, 0, 0}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *TextOutput) Write(filename string,err error, skipped bool) {
|
func (o *TextOutput) Write(filename string, err error, skipped bool) {
|
||||||
if skipped {
|
s := status(err, skipped)
|
||||||
fmt.Printf("skipping resource\n")
|
switch {
|
||||||
o.nSkipped++
|
case s == VALID:
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
if _, ok := err.(validator.InvalidResourceError); ok {
|
|
||||||
fmt.Printf("invalid resource: %s\n", err)
|
|
||||||
o.nInvalid++
|
|
||||||
} else {
|
|
||||||
fmt.Printf("failed validating resource in file %s: %s\n", filename, err)
|
|
||||||
o.nErrors++
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if !skipped{
|
|
||||||
fmt.Printf("file %s is valid\n", filename)
|
fmt.Printf("file %s is valid\n", filename)
|
||||||
o.nValid++
|
o.nValid++
|
||||||
|
case s == INVALID:
|
||||||
|
fmt.Printf("invalid resource: %s\n", err)
|
||||||
|
o.nInvalid++
|
||||||
|
case s == ERROR:
|
||||||
|
fmt.Printf("failed validating resource in file %s: %s\n", filename, err)
|
||||||
|
o.nErrors++
|
||||||
|
case s == SKIPPED:
|
||||||
|
fmt.Printf("skipping resource\n")
|
||||||
|
o.nSkipped++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -43,4 +36,3 @@ func (o *TextOutput) Flush() {
|
||||||
fmt.Printf("Run summary - Valid: %d, Invalid: %d, Errors: %d Skipped: %d\n", o.nValid, o.nInvalid, o.nErrors, o.nSkipped)
|
fmt.Printf("Run summary - Valid: %d, Invalid: %d, Errors: %d Skipped: %d\n", o.nValid, o.nInvalid, o.nErrors, o.nSkipped)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,12 @@ type downloadError struct {
|
||||||
err error
|
err error
|
||||||
isRetryable bool
|
isRetryable bool
|
||||||
}
|
}
|
||||||
func newDownloadError(err error, isRetryable bool) *downloadError{
|
|
||||||
|
func newDownloadError(err error, isRetryable bool) *downloadError {
|
||||||
return &downloadError{err, isRetryable}
|
return &downloadError{err, isRetryable}
|
||||||
}
|
}
|
||||||
func (e *downloadError) IsRetryable() bool { return e.isRetryable }
|
func (e *downloadError) IsRetryable() bool { return e.isRetryable }
|
||||||
func (e *downloadError) Error() string { return e.err.Error() }
|
func (e *downloadError) Error() string { return e.err.Error() }
|
||||||
|
|
||||||
func NewKubernetesRegistry(strict bool) *KubernetesRegistry {
|
func NewKubernetesRegistry(strict bool) *KubernetesRegistry {
|
||||||
return &KubernetesRegistry{
|
return &KubernetesRegistry{
|
||||||
|
|
@ -61,7 +62,7 @@ func (r KubernetesRegistry) DownloadSchema(resourceKind, resourceAPIVersion, k8s
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if resp.StatusCode == http.StatusNotFound{
|
if resp.StatusCode == http.StatusNotFound {
|
||||||
return nil, newDownloadError(fmt.Errorf("no schema found"), false)
|
return nil, newDownloadError(fmt.Errorf("no schema found"), false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,9 @@ package registry
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/xeipuuv/gojsonschema"
|
"github.com/xeipuuv/gojsonschema"
|
||||||
"sigs.k8s.io/yaml"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"sigs.k8s.io/yaml"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -13,7 +13,6 @@ type LocalSchemas struct {
|
||||||
schemas map[string]*gojsonschema.Schema
|
schemas map[string]*gojsonschema.Schema
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func NewLocalSchemas(schemaFiles []string) (*LocalSchemas, error) {
|
func NewLocalSchemas(schemaFiles []string) (*LocalSchemas, error) {
|
||||||
schemas := &LocalSchemas{
|
schemas := &LocalSchemas{
|
||||||
schemas: map[string]*gojsonschema.Schema{},
|
schemas: map[string]*gojsonschema.Schema{},
|
||||||
|
|
@ -30,7 +29,7 @@ func NewLocalSchemas(schemaFiles []string) (*LocalSchemas, error) {
|
||||||
return nil, fmt.Errorf("failed to read schema %s", schemaFile)
|
return nil, fmt.Errorf("failed to read schema %s", schemaFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
var parsedSchema struct{
|
var parsedSchema struct {
|
||||||
Spec struct {
|
Spec struct {
|
||||||
Names struct {
|
Names struct {
|
||||||
Kind string `json:"Kind"`
|
Kind string `json:"Kind"`
|
||||||
|
|
@ -42,7 +41,7 @@ func NewLocalSchemas(schemaFiles []string) (*LocalSchemas, error) {
|
||||||
return nil, fmt.Errorf("failed parsing schema %s", schemaFile)
|
return nil, fmt.Errorf("failed parsing schema %s", schemaFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.HasSuffix(schemaFile, ".yml") || strings.HasSuffix(schemaFile, ".yaml") {
|
if strings.HasSuffix(schemaFile, ".yml") || strings.HasSuffix(schemaFile, ".yaml") {
|
||||||
asJSON, err := yaml.YAMLToJSON(content)
|
asJSON, err := yaml.YAMLToJSON(content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error converting manifest %s to JSON: %s", schemaFile, err)
|
return nil, fmt.Errorf("error converting manifest %s to JSON: %s", schemaFile, err)
|
||||||
|
|
|
||||||
|
|
@ -12,4 +12,4 @@ type Registry interface {
|
||||||
|
|
||||||
type Retryable interface {
|
type Retryable interface {
|
||||||
IsRetryable() bool
|
IsRetryable() bool
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,4 +21,3 @@ func SignatureFromBytes(s []byte) (Signature, error) {
|
||||||
|
|
||||||
return Signature{Kind: resource.Kind, Version: resource.APIVersion, Namespace: resource.Metadata.Namespace}, err
|
return Signature{Kind: resource.Kind, Version: resource.APIVersion, Namespace: resource.Metadata.Namespace}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,14 +6,16 @@ import (
|
||||||
"sigs.k8s.io/yaml"
|
"sigs.k8s.io/yaml"
|
||||||
)
|
)
|
||||||
|
|
||||||
type InvalidResourceError struct { err string }
|
type InvalidResourceError struct{ err string }
|
||||||
func (r InvalidResourceError) Error() string{
|
|
||||||
|
func (r InvalidResourceError) Error() string {
|
||||||
return r.err
|
return r.err
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidFormat is a type for quickly forcing
|
// ValidFormat is a type for quickly forcing
|
||||||
// new formats on the gojsonschema loader
|
// new formats on the gojsonschema loader
|
||||||
type ValidFormat struct{}
|
type ValidFormat struct{}
|
||||||
|
|
||||||
func (f ValidFormat) IsFormat(input interface{}) bool {
|
func (f ValidFormat) IsFormat(input interface{}) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
@ -40,13 +42,12 @@ func Validate(rawResource []byte, schema *gojsonschema.Schema) error {
|
||||||
results, err := schema.Validate(resourceLoader)
|
results, err := schema.Validate(resourceLoader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// This error can only happen if the Object to validate is poorly formed. There's no hope of saving this one
|
// This error can only happen if the Object to validate is poorly formed. There's no hope of saving this one
|
||||||
return fmt.Errorf("problem validating schema. Check JSON formatting: %s", err)
|
return fmt.Errorf("problem validating schema. Check JSON formatting: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !results.Valid() {
|
if !results.Valid() {
|
||||||
return InvalidResourceError{err: fmt.Sprintf("resource does not conform to schema: %+v", results) }
|
return InvalidResourceError{err: fmt.Sprintf("resource does not conform to schema: %+v", results)}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue