feat: support set go template delims

This commit is contained in:
rick 2023-01-09 19:36:16 +08:00
parent 84afe70659
commit 74cb3b20fb
8 changed files with 47 additions and 13 deletions

View file

@ -102,6 +102,8 @@ Usage: ./bin/kubeconform [OPTION]... [FILE OR FOLDER]...
cache schemas downloaded via HTTP to this folder
-debug
print debug information
-delims string
the delims for go template parsing
-exit-on-error
immediately stop execution when the first error is encountered
-h show help information
@ -211,6 +213,7 @@ in each of them, in order, stopping as soon as a matching file is found.
* if the `-schema-location` value ends with `.json` - Kubeconform assumes the value is a **Go templated
string** that indicates how to search for JSON schemas.
* the `-schema-location` value of `default` is an alias for `https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/{{.NormalizedKubernetesVersion}}-standalone{{.StrictSuffix}}/{{.ResourceKind}}{{.KindSuffix}}.json`.
* the `-delims` could use an alternative delims when parsing the URL. Such as, when you pass `-delims=[[,,]]`, then it could parse `https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/[[.NormalizedKubernetesVersion]]-standalone[[.StrictSuffix]]/[[.ResourceKind]][[.KindSuffix]].json`
**The following command lines are equivalent:**
```bash

View file

@ -108,6 +108,7 @@ func realMain() int {
KubernetesVersion: cfg.KubernetesVersion,
Strict: cfg.Strict,
IgnoreMissingSchemas: cfg.IgnoreMissingSchemas,
Delims: cfg.Delims,
})
if err != nil {
fmt.Fprintln(os.Stderr, err)

View file

@ -24,6 +24,7 @@ type Config struct {
Verbose bool
IgnoreMissingSchemas bool
IgnoreFilenamePatterns []string
Delims string
Help bool
Version bool
}
@ -78,6 +79,7 @@ func FromFlags(progName string, args []string) (Config, string, error) {
flags.BoolVar(&c.Verbose, "verbose", false, "print results for all resources (ignored for tap and junit output)")
flags.BoolVar(&c.SkipTLS, "insecure-skip-tls-verify", false, "disable verification of the server's SSL certificate. This will make your HTTPS connections insecure")
flags.StringVar(&c.Cache, "cache", "", "cache schemas downloaded via HTTP to this folder")
flags.StringVar(&c.Delims, "delims", "", "the delims for go template parsing")
flags.BoolVar(&c.Help, "h", false, "show help information")
flags.BoolVar(&c.Version, "v", false, "show version information")
flags.Usage = func() {

View file

@ -24,9 +24,10 @@ type SchemaRegistry struct {
cache cache.Cache
strict bool
debug bool
delims string
}
func newHTTPRegistry(schemaPathTemplate string, cacheFolder string, strict bool, skipTLS bool, debug bool) (*SchemaRegistry, error) {
func newHTTPRegistry(schemaPathTemplate string, cacheFolder string, strict bool, skipTLS bool, debug bool, delims string) (*SchemaRegistry, error) {
reghttp := &http.Transport{
MaxIdleConns: 100,
IdleConnTimeout: 3 * time.Second,
@ -57,12 +58,13 @@ func newHTTPRegistry(schemaPathTemplate string, cacheFolder string, strict bool,
cache: filecache,
strict: strict,
debug: debug,
delims: delims,
}, nil
}
// DownloadSchema downloads the schema for a particular resource from an HTTP server
func (r SchemaRegistry) DownloadSchema(resourceKind, resourceAPIVersion, k8sVersion string) ([]byte, error) {
url, err := schemaPath(r.schemaPathTemplate, resourceKind, resourceAPIVersion, k8sVersion, r.strict)
url, err := schemaPath(r.schemaPathTemplate, resourceKind, resourceAPIVersion, k8sVersion, r.strict, r.delims)
if err != nil {
return nil, err
}

View file

@ -12,20 +12,22 @@ type LocalRegistry struct {
pathTemplate string
strict bool
debug bool
delims string
}
// NewLocalSchemas creates a new "registry", that will serve schemas from files, given a list of schema filenames
func newLocalRegistry(pathTemplate string, strict bool, debug bool) (*LocalRegistry, error) {
func newLocalRegistry(pathTemplate string, strict bool, debug bool, delims string) (*LocalRegistry, error) {
return &LocalRegistry{
pathTemplate,
strict,
debug,
delims,
}, nil
}
// DownloadSchema retrieves the schema from a file for the resource
func (r LocalRegistry) DownloadSchema(resourceKind, resourceAPIVersion, k8sVersion string) ([]byte, error) {
schemaFile, err := schemaPath(r.pathTemplate, resourceKind, resourceAPIVersion, k8sVersion, r.strict)
schemaFile, err := schemaPath(r.pathTemplate, resourceKind, resourceAPIVersion, k8sVersion, r.strict, r.delims)
if err != nil {
return []byte{}, nil
}

View file

@ -32,7 +32,7 @@ func newNotFoundError(err error) *NotFoundError {
func (e *NotFoundError) Error() string { return e.err.Error() }
func (e *NotFoundError) Retryable() bool { return false }
func schemaPath(tpl, resourceKind, resourceAPIVersion, k8sVersion string, strict bool) (string, error) {
func schemaPath(tpl, resourceKind, resourceAPIVersion, k8sVersion string, strict bool, delims string) (string, error) {
normalisedVersion := k8sVersion
if normalisedVersion != "master" {
normalisedVersion = "v" + normalisedVersion
@ -51,7 +51,15 @@ func schemaPath(tpl, resourceKind, resourceAPIVersion, k8sVersion string, strict
kindSuffix += "-" + strings.ToLower(groupParts[1])
}
tmpl, err := template.New("tpl").Parse(tpl)
tmpl := template.New("tpl")
// in case of some special cases
delimsPairs := strings.Split(delims, ",")
if len(delimsPairs) == 2 {
tmpl = tmpl.Delims(delimsPairs[0], delimsPairs[1])
}
tmpl, err := tmpl.Parse(tpl)
if err != nil {
return "", err
}
@ -81,7 +89,7 @@ func schemaPath(tpl, resourceKind, resourceAPIVersion, k8sVersion string, strict
return buf.String(), nil
}
func New(schemaLocation string, cache string, strict bool, skipTLS bool, debug bool) (Registry, error) {
func New(schemaLocation string, cache string, strict bool, skipTLS bool, debug bool, delims string) (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
@ -89,13 +97,13 @@ func New(schemaLocation string, cache string, strict bool, skipTLS bool, debug b
}
// try to compile the schemaLocation template to ensure it is valid
if _, err := schemaPath(schemaLocation, "Deployment", "v1", "master", true); err != nil {
if _, err := schemaPath(schemaLocation, "Deployment", "v1", "master", true, delims); err != nil {
return nil, fmt.Errorf("failed initialising schema location registry: %s", err)
}
if strings.HasPrefix(schemaLocation, "http") {
return newHTTPRegistry(schemaLocation, cache, strict, skipTLS, debug)
return newHTTPRegistry(schemaLocation, cache, strict, skipTLS, debug, delims)
}
return newLocalRegistry(schemaLocation, strict, debug)
return newLocalRegistry(schemaLocation, strict, debug, delims)
}

View file

@ -8,6 +8,7 @@ func TestSchemaPath(t *testing.T) {
for i, testCase := range []struct {
tpl, resourceKind, resourceAPIVersion, k8sVersion, expected string
strict bool
delims string
errExpected error
}{
{
@ -17,6 +18,7 @@ func TestSchemaPath(t *testing.T) {
"1.16.0",
"https://kubernetesjsonschema.dev/v1.16.0-standalone-strict/deployment-apps-v1.json",
true,
"",
nil,
},
{
@ -26,6 +28,7 @@ func TestSchemaPath(t *testing.T) {
"1.16.0",
"https://kubernetesjsonschema.dev/v1.16.0-standalone/deployment-apps-v1.json",
false,
"",
nil,
},
{
@ -35,6 +38,7 @@ func TestSchemaPath(t *testing.T) {
"1.18.0",
"https://kubernetesjsonschema.dev/v1.18.0-standalone/service-v1.json",
false,
"",
nil,
},
{
@ -44,10 +48,21 @@ func TestSchemaPath(t *testing.T) {
"master",
"https://kubernetesjsonschema.dev/master-standalone/service-v1.json",
false,
"",
nil,
},
{
"https://kubernetesjsonschema.dev/[[ .NormalizedKubernetesVersion ]]-standalone[[ .StrictSuffix ]]/[[ .ResourceKind ]][[ .KindSuffix ]].json",
"Service",
"v1",
"master",
"https://kubernetesjsonschema.dev/master-standalone/service-v1.json",
false,
"[[,]]",
nil,
},
} {
got, err := schemaPath(testCase.tpl, testCase.resourceKind, testCase.resourceAPIVersion, testCase.k8sVersion, testCase.strict)
got, err := schemaPath(testCase.tpl, testCase.resourceKind, testCase.resourceAPIVersion, testCase.k8sVersion, testCase.strict, testCase.delims)
if err != testCase.errExpected {
t.Errorf("%d - got error %s, expected %s", i+1, err, testCase.errExpected)
}

View file

@ -49,7 +49,8 @@ type Opts struct {
RejectKinds map[string]struct{} // List of resource Kinds to reject
KubernetesVersion string // Kubernetes Version - has to match one in https://github.com/instrumenta/kubernetes-json-schema
Strict bool // thros an error if resources contain undocumented fields
IgnoreMissingSchemas bool // skip a resource if no schema for that resource can be found
Delims string
IgnoreMissingSchemas bool // skip a resource if no schema for that resource can be found
}
// New returns a new Validator
@ -62,7 +63,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, opts.Debug)
reg, err := registry.New(schemaLocation, opts.Cache, opts.Strict, opts.SkipTLS, opts.Debug, opts.Delims)
if err != nil {
return nil, err
}