diff --git a/kubeval/config.go b/kubeval/config.go index a9240a8..b7635dc 100644 --- a/kubeval/config.go +++ b/kubeval/config.go @@ -63,10 +63,17 @@ type Config struct { // reporting results to the user. OutputFormat string + // LoggingLevel configures the logging level for whether we should log output when using the human layout + LoggingLevel string + // Quiet indicates whether non-results output should be emitted to the applications // log. Quiet bool + // IgnoreHelmSource if enabled we don't replace the file name used in errors reported with + // the contents of the `# Source` comment file name + IgnoreHelmSource bool + // InsecureSkipTLSVerify controls whether to skip TLS certificate validation // when retrieving schema content over HTTPS InsecureSkipTLSVerify bool @@ -95,7 +102,9 @@ func AddKubevalFlags(cmd *cobra.Command, config *Config) *cobra.Command { cmd.Flags().StringSliceVar(&config.AdditionalSchemaLocations, "additional-schema-locations", []string{}, "Comma-seperated list of secondary base URLs used to download schemas") cmd.Flags().StringVarP(&config.KubernetesVersion, "kubernetes-version", "v", "master", "Version of Kubernetes to validate against") cmd.Flags().StringVarP(&config.OutputFormat, "output", "o", "", fmt.Sprintf("The format of the output of this script. Options are: %v", validOutputs())) + cmd.Flags().StringVarP(&config.LoggingLevel, "log-level", "", "", fmt.Sprintf("The logging level for whether to log all levels or just warnings. Options are: %v", validLevels())) cmd.Flags().BoolVar(&config.Quiet, "quiet", false, "Silences any output aside from the direct results") + cmd.Flags().BoolVar(&config.IgnoreHelmSource, "ignore-helm-source", false, "If enabled we preserve the file name for errors rather than replace it with the contents of the '# Source' comment in the generated helm resource") cmd.Flags().BoolVar(&config.InsecureSkipTLSVerify, "insecure-skip-tls-verify", false, "If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure") return cmd diff --git a/kubeval/kubeval.go b/kubeval/kubeval.go index 10e0eef..bcb3f19 100644 --- a/kubeval/kubeval.go +++ b/kubeval/kubeval.go @@ -4,6 +4,7 @@ import ( "bytes" "fmt" "os" + "path/filepath" "regexp" "strings" @@ -322,10 +323,27 @@ func ValidateWithCache(input []byte, schemaCache map[string]*gojsonschema.Schema seenResourcesSet := make(map[[4]string]bool) // set of [API version, kind, namespace, name] + if config.IgnoreHelmSource { + // lets try use a relative file name to the current dir + dir, err := os.Getwd() + if err != nil { + return nil, err + } + rel, err := filepath.Rel(dir, config.FileName) + if err == nil && rel != "" { + names := strings.Split(rel, string(os.PathSeparator)) + if names[0] != ".." { + config.FileName = rel + } + } + } + for _, element := range bits { if len(element) > 0 { - if found := helmSourcePattern.FindStringSubmatch(string(element)); found != nil { - config.FileName = found[1] + if !config.IgnoreHelmSource { + if found := helmSourcePattern.FindStringSubmatch(string(element)); found != nil { + config.FileName = found[1] + } } result, body, err := validateResource(element, schemaCache, config) diff --git a/kubeval/output.go b/kubeval/output.go index f652cb1..7277fee 100644 --- a/kubeval/output.go +++ b/kubeval/output.go @@ -26,6 +26,10 @@ const ( outputSTD = "stdout" outputJSON = "json" outputTAP = "tap" + + levelINFO = "info" + levelSUCCESS = "success" + levelWARN = "warn" ) func validOutputs() []string { @@ -36,26 +40,35 @@ func validOutputs() []string { } } -func GetOutputManager(outFmt string) outputManager { +func validLevels() []string { + return []string{ + levelINFO, + levelSUCCESS, + levelWARN, + } +} + +func GetOutputManager(outFmt, loggingLevel string) outputManager { switch outFmt { case outputSTD: - return newSTDOutputManager() + return newSTDOutputManager(loggingLevel) case outputJSON: return newDefaultJSONOutputManager() case outputTAP: return newDefaultTAPOutputManager() default: - return newSTDOutputManager() + return newSTDOutputManager(loggingLevel) } } // STDOutputManager reports `kubeval` results to stdout. type STDOutputManager struct { + loggingLevel string } // newSTDOutputManager instantiates a new instance of STDOutputManager. -func newSTDOutputManager() *STDOutputManager { - return &STDOutputManager{} +func newSTDOutputManager(loggingLevel string) *STDOutputManager { + return &STDOutputManager{loggingLevel: loggingLevel} } func (s *STDOutputManager) Put(result ValidationResult) error { @@ -64,11 +77,17 @@ func (s *STDOutputManager) Put(result ValidationResult) error { kLog.Warn(result.FileName, "contains an invalid", result.Kind, fmt.Sprintf("(%s)", result.QualifiedName()), "-", desc.String()) } } else if result.Kind == "" { - kLog.Success(result.FileName, "contains an empty YAML document") + if s.loggingLevel != levelWARN { + kLog.Success(result.FileName, "contains an empty YAML document") + } } else if !result.ValidatedAgainstSchema { - kLog.Warn(result.FileName, "containing a", result.Kind, fmt.Sprintf("(%s)", result.QualifiedName()), "was not validated against a schema") + if s.loggingLevel != levelWARN && s.loggingLevel != levelSUCCESS { + kLog.Info(result.FileName, "containing a", result.Kind, fmt.Sprintf("(%s)", result.QualifiedName()), "was not validated against a schema") + } } else { - kLog.Success(result.FileName, "contains a valid", result.Kind, fmt.Sprintf("(%s)", result.QualifiedName())) + if s.loggingLevel != levelWARN { + kLog.Success(result.FileName, "contains a valid", result.Kind, fmt.Sprintf("(%s)", result.QualifiedName())) + } } return nil diff --git a/log/log.go b/log/log.go index 680cda6..291fff8 100644 --- a/log/log.go +++ b/log/log.go @@ -13,6 +13,11 @@ func Success(message ...string) { fmt.Printf("%s - %v\n", green("PASS"), strings.Join(message, " ")) } +func Info(message ...string) { + blue := color.New(color.FgBlue).SprintFunc() + fmt.Printf("%s - %v\n", blue("PASS"), strings.Join(message, " ")) +} + func Warn(message ...string) { yellow := color.New(color.FgYellow).SprintFunc() fmt.Printf("%s - %v\n", yellow("WARN"), strings.Join(message, " ")) diff --git a/main.go b/main.go index cd24086..2a94d6a 100644 --- a/main.go +++ b/main.go @@ -24,10 +24,10 @@ import ( ) var ( - version = "dev" - commit = "none" - date = "unknown" - directories = []string{} + version = "dev" + commit = "none" + date = "unknown" + directories = []string{} ignoredPathPatterns = []string{} // forceColor tells kubeval to use colored output even if @@ -59,7 +59,7 @@ var RootCmd = &cobra.Command{ success := true windowsStdinIssue := false - outputManager := kubeval.GetOutputManager(config.OutputFormat) + outputManager := kubeval.GetOutputManager(config.OutputFormat, config.LoggingLevel) stat, err := os.Stdin.Stat() if err != nil { @@ -242,7 +242,7 @@ func init() { RootCmd.Flags().StringSliceVarP(&directories, "directories", "d", []string{}, "A comma-separated list of directories to recursively search for YAML documents") RootCmd.Flags().StringSliceVarP(&ignoredPathPatterns, "ignored-path-patterns", "i", []string{}, "A comma-separated list of regular expressions specifying paths to ignore") RootCmd.Flags().StringSliceVarP(&ignoredPathPatterns, "ignored-filename-patterns", "", []string{}, "An alias for ignored-path-patterns") - + viper.SetEnvPrefix("KUBEVAL") viper.AutomaticEnv() viper.BindPFlag("schema_location", RootCmd.Flags().Lookup("schema-location"))