Skip to content

Commit

Permalink
Add debug information to help understand failures finding schemas (#133)
Browse files Browse the repository at this point in the history
* Add debug information to help understand failures finding schemas

* Add debug information to help understand failures finding schemas
  • Loading branch information
yannh committed Oct 16, 2022
1 parent 3cb76bc commit f68d6ec
Show file tree
Hide file tree
Showing 9 changed files with 67 additions and 24 deletions.
2 changes: 2 additions & 0 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ Usage: ./bin/kubeconform [OPTION]... [FILE OR FOLDER]...
cache schemas downloaded via HTTP to this folder
-cpu-prof string
debug - log CPU profiling to file
-debug
print debug information
-exit-on-error
immediately stop execution when the first error is encountered
-h show help information
Expand Down
5 changes: 3 additions & 2 deletions cmd/kubeconform/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,10 @@ func realMain() int {
fmt.Fprintln(os.Stderr, err)
return 1
}

v, err := validator.New(cfg.SchemaLocations, validator.Opts{
var v validator.Validator
v, err = validator.New(cfg.SchemaLocations, validator.Opts{
Cache: cfg.Cache,
Debug: cfg.Debug,
SkipTLS: cfg.SkipTLS,
SkipKinds: cfg.SkipKinds,
RejectKinds: cfg.RejectKinds,
Expand Down
2 changes: 2 additions & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
type Config struct {
Cache string
CPUProfileFile string
Debug bool
ExitOnError bool
Files []string
SchemaLocations []string
Expand Down Expand Up @@ -67,6 +68,7 @@ func FromFlags(progName string, args []string) (Config, string, error) {
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(&rejectKindsCSV, "reject", "", "comma-separated list of kinds to reject")
flags.BoolVar(&c.Debug, "debug", false, "print debug information")
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.Var(&ignoreFilenamePatterns, "ignore-filename-pattern", "regular expression specifying paths to ignore (can be specified multiple times)")
Expand Down
3 changes: 2 additions & 1 deletion pkg/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,10 @@ func TestFromFlags(t *testing.T) {
{
[]string{"-cache", "cache", "-ignore-missing-schemas", "-kubernetes-version", "1.16.0", "-n", "2", "-output", "json",
"-schema-location", "folder", "-schema-location", "anotherfolder", "-skip", "kinda,kindb", "-strict",
"-reject", "kindc,kindd", "-summary", "-verbose", "file1", "file2"},
"-reject", "kindc,kindd", "-summary", "-debug", "-verbose", "file1", "file2"},
Config{
Cache: "cache",
Debug: true,
Files: []string{"file1", "file2"},
IgnoreMissingSchemas: true,
KubernetesVersion: "1.16.0",
Expand Down
34 changes: 29 additions & 5 deletions pkg/registry/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ package registry

import (
"crypto/tls"
"errors"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"time"
Expand All @@ -21,9 +23,10 @@ type SchemaRegistry struct {
schemaPathTemplate string
cache cache.Cache
strict bool
debug bool
}

func newHTTPRegistry(schemaPathTemplate string, cacheFolder string, strict bool, skipTLS bool) (*SchemaRegistry, error) {
func newHTTPRegistry(schemaPathTemplate string, cacheFolder string, strict bool, skipTLS bool, debug bool) (*SchemaRegistry, error) {
reghttp := &http.Transport{
MaxIdleConns: 100,
IdleConnTimeout: 3 * time.Second,
Expand Down Expand Up @@ -53,6 +56,7 @@ func newHTTPRegistry(schemaPathTemplate string, cacheFolder string, strict bool,
schemaPathTemplate: schemaPathTemplate,
cache: filecache,
strict: strict,
debug: debug,
}, nil
}

Expand All @@ -71,21 +75,41 @@ func (r SchemaRegistry) DownloadSchema(resourceKind, resourceAPIVersion, k8sVers

resp, err := r.c.Get(url)
if err != nil {
return nil, fmt.Errorf("failed downloading schema at %s: %s", url, err)
msg := fmt.Sprintf("failed downloading schema at %s: %s", url, err)
if r.debug {
log.Println(msg)
}
return nil, errors.New(msg)
}
defer resp.Body.Close()

if resp.StatusCode == http.StatusNotFound {
return nil, newNotFoundError(fmt.Errorf("no schema found"))
msg := fmt.Sprintf("could not find schema at %s", url)
if r.debug {
log.Print(msg)
}
return nil, newNotFoundError(errors.New(msg))
}

if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("error while downloading schema at %s - received HTTP status %d", url, resp.StatusCode)
msg := fmt.Sprintf("error while downloading schema at %s - received HTTP status %d", url, resp.StatusCode)
if r.debug {
log.Print(msg)
}
return nil, fmt.Errorf(msg)
}

body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed downloading schema at %s: %s", url, err)
msg := fmt.Sprintf("failed parsing schema from %s: %s", url, err)
if r.debug {
log.Print(msg)
}
return nil, errors.New(msg)
}

if r.debug {
log.Printf("using schema found at %s", url)
}

if r.cache != nil {
Expand Down
2 changes: 1 addition & 1 deletion pkg/registry/http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func TestDownloadSchema(t *testing.T) {
"v1",
"1.18.0",
nil,
fmt.Errorf("no schema found"),
fmt.Errorf("could not find schema at http://kubernetesjson.dev"),
},
{
"getting 503",
Expand Down
26 changes: 23 additions & 3 deletions pkg/registry/local.go
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
package registry

import (
"errors"
"fmt"
"io/ioutil"
"log"
"os"
)

type LocalRegistry struct {
pathTemplate string
strict bool
debug bool
}

// NewLocalSchemas creates a new "registry", that will serve schemas from files, given a list of schema filenames
func newLocalRegistry(pathTemplate string, strict bool) (*LocalRegistry, error) {
func newLocalRegistry(pathTemplate string, strict bool, debug bool) (*LocalRegistry, error) {
return &LocalRegistry{
pathTemplate,
strict,
debug,
}, nil
}

Expand All @@ -28,16 +32,32 @@ func (r LocalRegistry) DownloadSchema(resourceKind, resourceAPIVersion, k8sVersi
f, err := os.Open(schemaFile)
if err != nil {
if os.IsNotExist(err) {
return nil, newNotFoundError(fmt.Errorf("no schema found"))
msg := fmt.Sprintf("could not open file %s", schemaFile)
if r.debug {
log.Print(msg)
}
return nil, newNotFoundError(errors.New(msg))
}
return nil, fmt.Errorf("failed to open schema %s", schemaFile)

msg := fmt.Sprintf("failed to open schema at %s: %s", schemaFile, err)
if r.debug {
log.Print(msg)
}
return nil, errors.New(msg)
}

defer f.Close()
content, err := ioutil.ReadAll(f)
if err != nil {
msg := fmt.Sprintf("failed to read schema at %s: %s", schemaFile, err)
if r.debug {
log.Print(msg)
}
return nil, err
}

if r.debug {
log.Printf("using schema found at %s", schemaFile)
}
return content, nil
}
6 changes: 3 additions & 3 deletions pkg/registry/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ func schemaPath(tpl, resourceKind, resourceAPIVersion, k8sVersion string, strict
return buf.String(), nil
}

func New(schemaLocation string, cache string, strict bool, skipTLS bool) (Registry, error) {
func New(schemaLocation string, cache string, strict bool, skipTLS bool, debug bool) (Registry, error) {
if schemaLocation == "default" {
schemaLocation = "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/{{ .NormalizedKubernetesVersion }}-standalone{{ .StrictSuffix }}/{{ .ResourceKind }}{{ .KindSuffix }}.json"
} else if !strings.HasSuffix(schemaLocation, "json") { // If we dont specify a full templated path, we assume the paths of our fork of kubernetes-json-schema
Expand All @@ -94,8 +94,8 @@ func New(schemaLocation string, cache string, strict bool, skipTLS bool) (Regist
}

if strings.HasPrefix(schemaLocation, "http") {
return newHTTPRegistry(schemaLocation, cache, strict, skipTLS)
return newHTTPRegistry(schemaLocation, cache, strict, skipTLS, debug)
}

return newLocalRegistry(schemaLocation, strict)
return newLocalRegistry(schemaLocation, strict, debug)
}
11 changes: 2 additions & 9 deletions pkg/validator/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ type Validator interface {
// Opts contains a set of options for the validator.
type Opts struct {
Cache string // Cache schemas downloaded via HTTP to this folder
Debug bool // Debug infos will be print here
SkipTLS bool // skip TLS validation when downloading from an HTTP Schema Registry
SkipKinds map[string]struct{} // List of resource Kinds to ignore
RejectKinds map[string]struct{} // List of resource Kinds to reject
Expand All @@ -61,7 +62,7 @@ func New(schemaLocations []string, opts Opts) (Validator, error) {

registries := []registry.Registry{}
for _, schemaLocation := range schemaLocations {
reg, err := registry.New(schemaLocation, opts.Cache, opts.Strict, opts.SkipTLS)
reg, err := registry.New(schemaLocation, opts.Cache, opts.Strict, opts.SkipTLS, opts.Debug)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -249,11 +250,3 @@ func downloadSchema(registries []registry.Registry, kind, version, k8sVersion st

return nil, nil // No schema found - we don't consider it an error, resource will be skipped
}

// From kubeval - let's see if absolutely necessary
// func init () {
// gojsonschema.FormatCheckers.Add("int64", ValidFormat{})
// gojsonschema.FormatCheckers.Add("byte", ValidFormat{})
// gojsonschema.FormatCheckers.Add("int32", ValidFormat{})
// gojsonschema.FormatCheckers.Add("int-or-string", ValidFormat{})
// }

0 comments on commit f68d6ec

Please sign in to comment.