mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-14 10:30:59 +00:00
update results field of ResourceList to implement function spec v1
This commit is contained in:
@@ -296,7 +296,7 @@ functionConfig:
|
|||||||
func ExampleBuild_validate() {
|
func ExampleBuild_validate() {
|
||||||
fn := func(rl *framework.ResourceList) error {
|
fn := func(rl *framework.ResourceList) error {
|
||||||
// validation results
|
// validation results
|
||||||
var validationResults []framework.ResultItem
|
var validationResults framework.Results
|
||||||
|
|
||||||
// validate that each Deployment resource has spec.replicas set
|
// validate that each Deployment resource has spec.replicas set
|
||||||
for i := range rl.Items {
|
for i := range rl.Items {
|
||||||
@@ -319,7 +319,7 @@ func ExampleBuild_validate() {
|
|||||||
if r != nil {
|
if r != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
validationResults = append(validationResults, framework.ResultItem{
|
validationResults = append(validationResults, &framework.Result{
|
||||||
Severity: framework.Error,
|
Severity: framework.Error,
|
||||||
Message: "field is required",
|
Message: "field is required",
|
||||||
ResourceRef: yaml.ResourceIdentifier{
|
ResourceRef: yaml.ResourceIdentifier{
|
||||||
@@ -327,20 +327,17 @@ func ExampleBuild_validate() {
|
|||||||
NameMeta: meta.ObjectMeta.NameMeta,
|
NameMeta: meta.ObjectMeta.NameMeta,
|
||||||
},
|
},
|
||||||
Field: framework.Field{
|
Field: framework.Field{
|
||||||
Path: "spec.replicas",
|
Path: "spec.replicas",
|
||||||
SuggestedValue: "1",
|
ProposedValue: "1",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(validationResults) > 0 {
|
if len(validationResults) > 0 {
|
||||||
rl.Result = &framework.Result{
|
rl.Results = validationResults
|
||||||
Name: "replicas-validator",
|
|
||||||
Items: validationResults,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return rl.Result
|
return rl.Results
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := command.Build(framework.ResourceListProcessorFunc(fn), command.StandaloneDisabled, true)
|
cmd := command.Build(framework.ResourceListProcessorFunc(fn), command.StandaloneDisabled, true)
|
||||||
@@ -370,15 +367,13 @@ items:
|
|||||||
// metadata:
|
// metadata:
|
||||||
// name: foo
|
// name: foo
|
||||||
// results:
|
// results:
|
||||||
// name: replicas-validator
|
// - message: field is required
|
||||||
// items:
|
// severity: error
|
||||||
// - message: field is required
|
// resourceRef:
|
||||||
// severity: error
|
// apiVersion: apps/v1
|
||||||
// resourceRef:
|
// kind: Deployment
|
||||||
// apiVersion: apps/v1
|
// name: foo
|
||||||
// kind: Deployment
|
// field:
|
||||||
// name: foo
|
// path: spec.replicas
|
||||||
// field:
|
// proposedValue: "1"
|
||||||
// path: spec.replicas
|
|
||||||
// suggestedValue: "1"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,22 @@ import (
|
|||||||
// This framework facilitates building functions that receive and emit ResourceLists,
|
// This framework facilitates building functions that receive and emit ResourceLists,
|
||||||
// as required by the specification.
|
// as required by the specification.
|
||||||
type ResourceList struct {
|
type ResourceList struct {
|
||||||
|
// Items is the ResourceList.items input and output value.
|
||||||
|
//
|
||||||
|
// e.g. given the function input:
|
||||||
|
//
|
||||||
|
// kind: ResourceList
|
||||||
|
// items:
|
||||||
|
// - kind: Deployment
|
||||||
|
// ...
|
||||||
|
// - kind: Service
|
||||||
|
// ...
|
||||||
|
//
|
||||||
|
// Items will be a slice containing the Deployment and Service resources
|
||||||
|
// Mutating functions will alter this field during processing.
|
||||||
|
// This field is required.
|
||||||
|
Items []*yaml.RNode `yaml:"items" json:"items"`
|
||||||
|
|
||||||
// FunctionConfig is the ResourceList.functionConfig input value.
|
// FunctionConfig is the ResourceList.functionConfig input value.
|
||||||
//
|
//
|
||||||
// e.g. given the input:
|
// e.g. given the input:
|
||||||
@@ -33,25 +49,10 @@ type ResourceList struct {
|
|||||||
// foo: var
|
// foo: var
|
||||||
FunctionConfig *yaml.RNode `yaml:"functionConfig" json:"functionConfig"`
|
FunctionConfig *yaml.RNode `yaml:"functionConfig" json:"functionConfig"`
|
||||||
|
|
||||||
// Items is the ResourceList.items input and output value.
|
// Results is ResourceList.results output value.
|
||||||
//
|
|
||||||
// e.g. given the function input:
|
|
||||||
//
|
|
||||||
// kind: ResourceList
|
|
||||||
// items:
|
|
||||||
// - kind: Deployment
|
|
||||||
// ...
|
|
||||||
// - kind: Service
|
|
||||||
// ...
|
|
||||||
//
|
|
||||||
// Items will be a slice containing the Deployment and Service resources
|
|
||||||
// Mutating functions will alter this field during processing.
|
|
||||||
Items []*yaml.RNode `yaml:"items" json:"items"`
|
|
||||||
|
|
||||||
// Result is ResourceList.result output value.
|
|
||||||
// Validating functions can optionally use this field to communicate structured
|
// Validating functions can optionally use this field to communicate structured
|
||||||
// validation error data to downstream functions.
|
// validation error data to downstream functions.
|
||||||
Result *Result `yaml:"results" json:"results"`
|
Results Results `yaml:"results" json:"results"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResourceListProcessor is implemented by configuration functions built with this framework
|
// ResourceListProcessor is implemented by configuration functions built with this framework
|
||||||
@@ -119,8 +120,8 @@ func Execute(p ResourceListProcessor, rlSource *kio.ByteReadWriter) error {
|
|||||||
|
|
||||||
// Write the results
|
// Write the results
|
||||||
// Set the ResourceList.results for validating functions
|
// Set the ResourceList.results for validating functions
|
||||||
if rl.Result != nil && len(rl.Result.Items) > 0 {
|
if len(rl.Results) > 0 {
|
||||||
b, err := yaml.Marshal(rl.Result)
|
b, err := yaml.Marshal(rl.Results)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err)
|
return errors.Wrap(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,33 +16,31 @@ import (
|
|||||||
|
|
||||||
func TestExecute_Result(t *testing.T) {
|
func TestExecute_Result(t *testing.T) {
|
||||||
p := framework.ResourceListProcessorFunc(func(rl *framework.ResourceList) error {
|
p := framework.ResourceListProcessorFunc(func(rl *framework.ResourceList) error {
|
||||||
err := &framework.Result{
|
err := &framework.Results{
|
||||||
Name: "Incompatible config",
|
{
|
||||||
Items: []framework.ResultItem{
|
Message: "bad value for replicas",
|
||||||
{
|
Severity: framework.Error,
|
||||||
Message: "bad value for replicas",
|
ResourceRef: yaml.ResourceIdentifier{
|
||||||
Severity: framework.Error,
|
TypeMeta: yaml.TypeMeta{APIVersion: "v1", Kind: "Deployment"},
|
||||||
ResourceRef: yaml.ResourceIdentifier{
|
NameMeta: yaml.NameMeta{Name: "tester", Namespace: "default"},
|
||||||
TypeMeta: yaml.TypeMeta{APIVersion: "v1", Kind: "Deployment"},
|
|
||||||
NameMeta: yaml.NameMeta{Name: "tester", Namespace: "default"},
|
|
||||||
},
|
|
||||||
Field: framework.Field{
|
|
||||||
Path: ".spec.Replicas",
|
|
||||||
CurrentValue: "0",
|
|
||||||
SuggestedValue: "3",
|
|
||||||
},
|
|
||||||
File: framework.File{
|
|
||||||
Path: "/path/to/deployment.yaml",
|
|
||||||
Index: 0,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
Field: framework.Field{
|
||||||
Message: "some error",
|
Path: ".spec.Replicas",
|
||||||
Severity: framework.Error,
|
CurrentValue: "0",
|
||||||
|
ProposedValue: "3",
|
||||||
|
},
|
||||||
|
File: framework.File{
|
||||||
|
Path: "/path/to/deployment.yaml",
|
||||||
|
Index: 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Message: "some error",
|
||||||
|
Severity: framework.Error,
|
||||||
|
Tags: map[string]string{"foo": "bar"},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
rl.Result = err
|
rl.Results = *err
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
out := new(bytes.Buffer)
|
out := new(bytes.Buffer)
|
||||||
@@ -62,7 +60,7 @@ items:
|
|||||||
assert.EqualError(t, err, `[error] v1/Deployment/default/tester .spec.Replicas: bad value for replicas
|
assert.EqualError(t, err, `[error] v1/Deployment/default/tester .spec.Replicas: bad value for replicas
|
||||||
|
|
||||||
[error] : some error`)
|
[error] : some error`)
|
||||||
assert.Equal(t, 1, err.(*framework.Result).ExitCode())
|
assert.Equal(t, 1, err.(*framework.Results).ExitCode())
|
||||||
assert.Equal(t, `apiVersion: config.kubernetes.io/v1
|
assert.Equal(t, `apiVersion: config.kubernetes.io/v1
|
||||||
kind: ResourceList
|
kind: ResourceList
|
||||||
items:
|
items:
|
||||||
@@ -74,21 +72,21 @@ items:
|
|||||||
spec:
|
spec:
|
||||||
replicas: 0
|
replicas: 0
|
||||||
results:
|
results:
|
||||||
name: Incompatible config
|
- message: bad value for replicas
|
||||||
items:
|
severity: error
|
||||||
- message: bad value for replicas
|
resourceRef:
|
||||||
severity: error
|
apiVersion: v1
|
||||||
resourceRef:
|
kind: Deployment
|
||||||
apiVersion: v1
|
name: tester
|
||||||
kind: Deployment
|
namespace: default
|
||||||
name: tester
|
field:
|
||||||
namespace: default
|
path: .spec.Replicas
|
||||||
field:
|
currentValue: "0"
|
||||||
path: .spec.Replicas
|
proposedValue: "3"
|
||||||
currentValue: "0"
|
file:
|
||||||
suggestedValue: "3"
|
path: /path/to/deployment.yaml
|
||||||
file:
|
- message: some error
|
||||||
path: /path/to/deployment.yaml
|
severity: error
|
||||||
- message: some error
|
tags:
|
||||||
severity: error`, strings.TrimSpace(out.String()))
|
foo: bar`, strings.TrimSpace(out.String()))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,25 +23,30 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// ResultItem defines a validation result
|
// ResultItem defines a validation result
|
||||||
type ResultItem struct {
|
type Result struct {
|
||||||
// Message is a human readable message
|
// Message is a human readable message. This field is required.
|
||||||
Message string `yaml:"message,omitempty"`
|
Message string `yaml:"message,omitempty" json:"message,omitempty"`
|
||||||
|
|
||||||
// Severity is the severity of this result
|
// Severity is the severity of this result
|
||||||
Severity Severity `yaml:"severity,omitempty"`
|
Severity Severity `yaml:"severity,omitempty" json:"severity,omitempty"`
|
||||||
|
|
||||||
// ResourceRef is a reference to a resource
|
// ResourceRef is a reference to a resource.
|
||||||
ResourceRef yaml.ResourceIdentifier `yaml:"resourceRef,omitempty"`
|
// Required fields: apiVersion, kind, name.
|
||||||
|
ResourceRef yaml.ResourceIdentifier `yaml:"resourceRef,omitempty" json:"resourceRef,omitempty"`
|
||||||
|
|
||||||
// Field is a reference to the field in a resource this result refers to
|
// Field is a reference to the field in a resource this result refers to
|
||||||
Field Field `yaml:"field,omitempty"`
|
Field Field `yaml:"field,omitempty" json:"field,omitempty"`
|
||||||
|
|
||||||
// File references a file containing the resource this result refers to
|
// File references a file containing the resource this result refers to
|
||||||
File File `yaml:"file,omitempty"`
|
File File `yaml:"file,omitempty" json:"file,omitempty"`
|
||||||
|
|
||||||
|
// Tags is an unstructured key value map stored with a result that may be set
|
||||||
|
// by external tools to store and retrieve arbitrary metadata
|
||||||
|
Tags map[string]string `yaml:"tags,omitempty" json:"tags,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// String provides a human-readable message for the result item
|
// String provides a human-readable message for the result item
|
||||||
func (i ResultItem) String() string {
|
func (i Result) String() string {
|
||||||
identifier := i.ResourceRef
|
identifier := i.ResourceRef
|
||||||
var idStringList []string
|
var idStringList []string
|
||||||
if identifier.APIVersion != "" {
|
if identifier.APIVersion != "" {
|
||||||
@@ -65,47 +70,41 @@ func (i ResultItem) String() string {
|
|||||||
|
|
||||||
// File references a file containing a resource
|
// File references a file containing a resource
|
||||||
type File struct {
|
type File struct {
|
||||||
// Path is relative path to the file containing the resource
|
// Path is relative path to the file containing the resource.
|
||||||
Path string `yaml:"path,omitempty"`
|
// This field is required.
|
||||||
|
Path string `yaml:"path,omitempty" json:"path,omitempty"`
|
||||||
|
|
||||||
// Index is the index into the file containing the resource
|
// Index is the index into the file containing the resource
|
||||||
// (i.e. if there are multiple resources in a single file)
|
// (i.e. if there are multiple resources in a single file)
|
||||||
Index int `yaml:"index,omitempty"`
|
Index int `yaml:"index,omitempty" json:"index,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Field references a field in a resource
|
// Field references a field in a resource
|
||||||
type Field struct {
|
type Field struct {
|
||||||
// Path is the field path
|
// Path is the field path. This field is required.
|
||||||
Path string `yaml:"path,omitempty"`
|
Path string `yaml:"path,omitempty" json:"path,omitempty"`
|
||||||
|
|
||||||
// CurrentValue is the current field value
|
// CurrentValue is the current field value
|
||||||
CurrentValue string `yaml:"currentValue,omitempty"`
|
CurrentValue interface{} `yaml:"currentValue,omitempty" json:"currentValue,omitempty"`
|
||||||
|
|
||||||
// SuggestedValue is the suggested field value
|
// ProposedValue is the proposed value of the field to fix an issue.
|
||||||
SuggestedValue string `yaml:"suggestedValue,omitempty"`
|
ProposedValue interface{} `yaml:"proposedValue,omitempty" json:"proposedValue,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Result defines a function result which will be set on the emitted ResourceList
|
type Results []*Result
|
||||||
type Result struct {
|
|
||||||
// Name is the name of the function creating the result
|
|
||||||
Name string `yaml:"name,omitempty"`
|
|
||||||
|
|
||||||
// Items are the individual results
|
// Error enables Results to be returned as an error
|
||||||
Items []ResultItem `yaml:"items,omitempty"`
|
func (e Results) Error() string {
|
||||||
}
|
|
||||||
|
|
||||||
// Error enables a Result to be returned as an error
|
|
||||||
func (e Result) Error() string {
|
|
||||||
var msgs []string
|
var msgs []string
|
||||||
for _, i := range e.Items {
|
for _, i := range e {
|
||||||
msgs = append(msgs, i.String())
|
msgs = append(msgs, i.String())
|
||||||
}
|
}
|
||||||
return strings.Join(msgs, "\n\n")
|
return strings.Join(msgs, "\n\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExitCode provides the exit code based on the result's severity
|
// ExitCode provides the exit code based on the result's severity
|
||||||
func (e Result) ExitCode() int {
|
func (e Results) ExitCode() int {
|
||||||
for _, i := range e.Items {
|
for _, i := range e {
|
||||||
if i.Severity == Error {
|
if i.Severity == Error {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user