diff --git a/api/internal/plugins/execplugin/execplugin.go b/api/internal/plugins/execplugin/execplugin.go index d780121e6..a7df41981 100644 --- a/api/internal/plugins/execplugin/execplugin.go +++ b/api/internal/plugins/execplugin/execplugin.go @@ -140,6 +140,11 @@ func (p *ExecPlugin) Transform(rm resmap.ResMap) error { return p.updateResMapValues(output, rm) } +func (p *ExecPlugin) Validate(rm resmap.ResMap) error { + // Validate works exactly same with Transformer + return p.Transform(rm) +} + // invokePlugin writes plugin config to a temp file, then // passes the full temp file path as the first arg to a process // running the plugin binary. Process output is returned. diff --git a/api/internal/plugins/loader/loader.go b/api/internal/plugins/loader/loader.go index 09dc3a167..1d1fe8e80 100644 --- a/api/internal/plugins/loader/loader.go +++ b/api/internal/plugins/loader/loader.go @@ -87,6 +87,32 @@ func (l *Loader) LoadTransformer( return t, nil } +func (l *Loader) LoadValidators( + ldr ifc.Loader, v ifc.Validator, rm resmap.ResMap) ([]resmap.Validator, error) { + var result []resmap.Validator + for _, res := range rm.Resources() { + t, err := l.LoadValidator(ldr, v, res) + if err != nil { + return nil, err + } + result = append(result, t) + } + return result, nil +} + +func (l *Loader) LoadValidator( + ldr ifc.Loader, v ifc.Validator, res *resource.Resource) (resmap.Validator, error) { + c, err := l.loadAndConfigurePlugin(ldr, v, res) + if err != nil { + return nil, err + } + t, ok := c.(resmap.Validator) + if !ok { + return nil, fmt.Errorf("plugin %s not a validator", res.OrgId()) + } + return t, nil +} + func relativePluginPath(id resid.ResId) string { return filepath.Join( id.Group, diff --git a/api/internal/target/kusttarget.go b/api/internal/target/kusttarget.go index 4cade158d..06af972bc 100644 --- a/api/internal/target/kusttarget.go +++ b/api/internal/target/kusttarget.go @@ -209,6 +209,10 @@ func (kt *KustTarget) accumulateTarget(ra *accumulator.ResAccumulator) ( if err != nil { return nil, err } + err = kt.runValidators(ra) + if err != nil { + return nil, err + } err = ra.MergeVars(kt.kustomization.Vars) if err != nil { return nil, errors.Wrapf( @@ -278,6 +282,29 @@ func (kt *KustTarget) configureExternalTransformers() ([]resmap.Transformer, err return kt.pLdr.LoadTransformers(kt.ldr, kt.validator, ra.ResMap()) } +func (kt *KustTarget) runValidators(ra *accumulator.ResAccumulator) error { + validators, err := kt.configureExternalValidators() + if err != nil { + return err + } + for _, v := range validators { + err := v.Validate(ra.ResMap()) + if err != nil { + return err + } + } + return nil +} + +func (kt *KustTarget) configureExternalValidators() ([]resmap.Validator, error) { + ra := accumulator.MakeEmptyAccumulator() + ra, err := kt.accumulateResources(ra, kt.kustomization.Validators) + if err != nil { + return nil, err + } + return kt.pLdr.LoadValidators(kt.ldr, kt.validator, ra.ResMap()) +} + // accumulateResources fills the given resourceAccumulator // with resources read from the given list of paths. func (kt *KustTarget) accumulateResources( diff --git a/api/resmap/resmap.go b/api/resmap/resmap.go index 91e87df66..db971b462 100644 --- a/api/resmap/resmap.go +++ b/api/resmap/resmap.go @@ -30,6 +30,12 @@ type Generator interface { Generate() (ResMap, error) } +// A Validator checkes the ResMap and return an error +// if it's not valid. +type Validator interface { + Validate(m ResMap) error +} + // Something that's configurable accepts an // instance of PluginHelpers and a raw config // object (YAML in []byte form). @@ -73,6 +79,11 @@ type TransformerPlugin interface { Configurable } +type ValidatorPlugin interface { + Validator + Configurable +} + // ResMap is an interface describing operations on the // core kustomize data structure, a list of Resources. // diff --git a/api/types/kustomization.go b/api/types/kustomization.go index 4a2c42d36..e7a1f9dac 100644 --- a/api/types/kustomization.go +++ b/api/types/kustomization.go @@ -124,6 +124,9 @@ type Kustomization struct { // Transformers is a list of files containing transformers Transformers []string `json:"transformers,omitempty" yaml:"transformers,omitempty"` + // Validators is a list of files containing validators + Validators []string `json:"validators,omitempty" yaml:"validators,omitempty"` + // Inventory appends an object that contains the record // of all other objects, which can be used in apply, prune and delete Inventory *Inventory `json:"inventory,omitempty" yaml:"inventory,omitempty"`