mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-05-17 18:25:26 +00:00
135 lines
3.3 KiB
Go
135 lines
3.3 KiB
Go
// Copyright 2019 The Kubernetes Authors.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
// Package main implements a validator function run by `kustomize fn run`
|
|
package main
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/instrumenta/kubeval/kubeval"
|
|
"sigs.k8s.io/kustomize/kyaml/kio"
|
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
|
)
|
|
|
|
func main() {
|
|
rw := &kio.ByteReadWriter{
|
|
Reader: os.Stdin,
|
|
Writer: os.Stdout,
|
|
OmitReaderAnnotations: true,
|
|
KeepReaderAnnotations: true,
|
|
}
|
|
p := kio.Pipeline{
|
|
Inputs: []kio.Reader{rw}, // read the inputs into a slice
|
|
Filters: []kio.Filter{kubevalFilter{rw: rw}},
|
|
Outputs: []kio.Writer{rw}, // copy the inputs to the output
|
|
}
|
|
if err := p.Execute(); err != nil {
|
|
fmt.Fprintf(os.Stderr, "%v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
// kubevalFilter implements kio.Filter
|
|
type kubevalFilter struct {
|
|
rw *kio.ByteReadWriter
|
|
}
|
|
|
|
// define the input API schema as a struct
|
|
type API struct {
|
|
Spec struct {
|
|
// Strict disallows additional properties not in schema if set.
|
|
Strict bool `yaml:"strict"`
|
|
|
|
// IgnoreMissingSchemas skips validation for resource
|
|
// definitions without a schema.
|
|
IgnoreMissingSchemas bool `yaml:"ignoreMissingSchemas"`
|
|
|
|
// KubernetesVersion is the version of Kubernetes to validate
|
|
// against (default "master").
|
|
KubernetesVersion string `yaml:"kubernetesVersion"`
|
|
|
|
// SchemaLocation is the base URL used to download schemas.
|
|
SchemaLocation string `yaml:"schemaLocation"`
|
|
} `yaml:"spec"`
|
|
}
|
|
|
|
// Filter checks each resource for validity, otherwise returning an error.
|
|
func (f kubevalFilter) Filter(in []*yaml.RNode) ([]*yaml.RNode, error) {
|
|
api := f.parseAPI()
|
|
config := kubeval.NewDefaultConfig()
|
|
config.Strict = api.Spec.Strict
|
|
config.IgnoreMissingSchemas = api.Spec.IgnoreMissingSchemas
|
|
config.KubernetesVersion = api.Spec.KubernetesVersion
|
|
config.SchemaLocation = api.Spec.SchemaLocation
|
|
|
|
// validate each Resource
|
|
for _, r := range in {
|
|
if err := validate(r.MustString(), config); err != nil {
|
|
meta, merr := r.GetMeta()
|
|
if merr != nil {
|
|
return nil, merr
|
|
}
|
|
fmt.Fprintf(
|
|
os.Stderr,
|
|
"Resource invalid: (Kind: %s, Name: %s)\n",
|
|
meta.Kind, meta.Name,
|
|
)
|
|
return nil, err
|
|
}
|
|
}
|
|
return in, nil
|
|
}
|
|
|
|
func validate(r string, config *kubeval.Config) error {
|
|
results, err := kubeval.Validate([]byte(r), config)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return checkResults(results)
|
|
}
|
|
|
|
func checkResults(results []kubeval.ValidationResult) error {
|
|
if len(results) == 0 {
|
|
return nil
|
|
}
|
|
|
|
errs := []string{}
|
|
for _, r := range results {
|
|
for _, e := range r.Errors {
|
|
// Workaround a bug where the
|
|
// "config.kubernetes.io/index" annotation value is a
|
|
// number (invalid), and is still set on the Resource
|
|
// regardless of OmitReaderAnnotations.
|
|
// TODO: Remove this once the above issues are
|
|
// resolved.
|
|
if e.String() == "metadata.annotations: Invalid type. Expected: [string,null], given: integer" {
|
|
continue
|
|
}
|
|
errs = append(errs, e.String())
|
|
}
|
|
}
|
|
|
|
if len(errs) > 0 {
|
|
return errors.New(strings.Join(errs, "\n"))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// parseAPI parses the functionConfig into an API struct.
|
|
func (f *kubevalFilter) parseAPI() API {
|
|
// parse the input function config -- TODO: simplify this
|
|
var api API
|
|
if err := yaml.Unmarshal([]byte(f.rw.FunctionConfig.MustString()), &api); err != nil {
|
|
fmt.Fprintf(os.Stderr, "%v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
return api
|
|
}
|