Skip to content

Commit

Permalink
Merge pull request #4248 from natasha41575/resourceListResults
Browse files Browse the repository at this point in the history
[breaking] update results field of ResourceList to implement function spec v1
  • Loading branch information
k8s-ci-robot committed Oct 29, 2021
2 parents 71b978d + 3ea8b79 commit 4fd77b3
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 110 deletions.
35 changes: 15 additions & 20 deletions kyaml/fn/framework/command/example_test.go
Expand Up @@ -296,7 +296,7 @@ functionConfig:
func ExampleBuild_validate() {
fn := func(rl *framework.ResourceList) error {
// validation results
var validationResults []framework.ResultItem
var validationResults framework.Results

// validate that each Deployment resource has spec.replicas set
for i := range rl.Items {
Expand All @@ -319,28 +319,25 @@ func ExampleBuild_validate() {
if r != nil {
continue
}
validationResults = append(validationResults, framework.ResultItem{
validationResults = append(validationResults, &framework.Result{
Severity: framework.Error,
Message: "field is required",
ResourceRef: yaml.ResourceIdentifier{
TypeMeta: meta.TypeMeta,
NameMeta: meta.ObjectMeta.NameMeta,
},
Field: framework.Field{
Path: "spec.replicas",
SuggestedValue: "1",
Path: "spec.replicas",
ProposedValue: "1",
},
})
}

if len(validationResults) > 0 {
rl.Result = &framework.Result{
Name: "replicas-validator",
Items: validationResults,
}
rl.Results = validationResults
}

return rl.Result
return rl.Results
}

cmd := command.Build(framework.ResourceListProcessorFunc(fn), command.StandaloneDisabled, true)
Expand Down Expand Up @@ -370,15 +367,13 @@ items:
// metadata:
// name: foo
// results:
// name: replicas-validator
// items:
// - message: field is required
// severity: error
// resourceRef:
// apiVersion: apps/v1
// kind: Deployment
// name: foo
// field:
// path: spec.replicas
// suggestedValue: "1"
// - message: field is required
// severity: error
// resourceRef:
// apiVersion: apps/v1
// kind: Deployment
// name: foo
// field:
// path: spec.replicas
// proposedValue: "1"
}
39 changes: 20 additions & 19 deletions kyaml/fn/framework/framework.go
Expand Up @@ -17,6 +17,22 @@ import (
// This framework facilitates building functions that receive and emit ResourceLists,
// as required by the specification.
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.
//
// e.g. given the input:
Expand All @@ -33,25 +49,10 @@ type ResourceList struct {
// foo: var
FunctionConfig *yaml.RNode `yaml:"functionConfig" json:"functionConfig"`

// 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.
Items []*yaml.RNode `yaml:"items" json:"items"`

// Result is ResourceList.result output value.
// Results is ResourceList.results output value.
// Validating functions can optionally use this field to communicate structured
// 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
Expand Down Expand Up @@ -119,8 +120,8 @@ func Execute(p ResourceListProcessor, rlSource *kio.ByteReadWriter) error {

// Write the results
// Set the ResourceList.results for validating functions
if rl.Result != nil && len(rl.Result.Items) > 0 {
b, err := yaml.Marshal(rl.Result)
if len(rl.Results) > 0 {
b, err := yaml.Marshal(rl.Results)
if err != nil {
return errors.Wrap(err)
}
Expand Down
80 changes: 39 additions & 41 deletions kyaml/fn/framework/framework_test.go
Expand Up @@ -16,33 +16,31 @@ import (

func TestExecute_Result(t *testing.T) {
p := framework.ResourceListProcessorFunc(func(rl *framework.ResourceList) error {
err := &framework.Result{
Name: "Incompatible config",
Items: []framework.ResultItem{
{
Message: "bad value for replicas",
Severity: framework.Error,
ResourceRef: yaml.ResourceIdentifier{
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,
},
err := &framework.Results{
{
Message: "bad value for replicas",
Severity: framework.Error,
ResourceRef: yaml.ResourceIdentifier{
TypeMeta: yaml.TypeMeta{APIVersion: "v1", Kind: "Deployment"},
NameMeta: yaml.NameMeta{Name: "tester", Namespace: "default"},
},
{
Message: "some error",
Severity: framework.Error,
Field: framework.Field{
Path: ".spec.Replicas",
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
})
out := new(bytes.Buffer)
Expand All @@ -62,7 +60,7 @@ items:
assert.EqualError(t, err, `[error] v1/Deployment/default/tester .spec.Replicas: bad value for replicas
[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
kind: ResourceList
items:
Expand All @@ -74,21 +72,21 @@ items:
spec:
replicas: 0
results:
name: Incompatible config
items:
- message: bad value for replicas
severity: error
resourceRef:
apiVersion: v1
kind: Deployment
name: tester
namespace: default
field:
path: .spec.Replicas
currentValue: "0"
suggestedValue: "3"
file:
path: /path/to/deployment.yaml
- message: some error
severity: error`, strings.TrimSpace(out.String()))
- message: bad value for replicas
severity: error
resourceRef:
apiVersion: v1
kind: Deployment
name: tester
namespace: default
field:
path: .spec.Replicas
currentValue: "0"
proposedValue: "3"
file:
path: /path/to/deployment.yaml
- message: some error
severity: error
tags:
foo: bar`, strings.TrimSpace(out.String()))
}
59 changes: 29 additions & 30 deletions kyaml/fn/framework/result.go
Expand Up @@ -23,25 +23,30 @@ const (
)

// ResultItem defines a validation result
type ResultItem struct {
// Message is a human readable message
Message string `yaml:"message,omitempty"`
type Result struct {
// Message is a human readable message. This field is required.
Message string `yaml:"message,omitempty" json:"message,omitempty"`

// 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 yaml.ResourceIdentifier `yaml:"resourceRef,omitempty"`
// ResourceRef is a reference to a resource.
// 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 Field `yaml:"field,omitempty"`
Field Field `yaml:"field,omitempty" json:"field,omitempty"`

// 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
func (i ResultItem) String() string {
func (i Result) String() string {
identifier := i.ResourceRef
var idStringList []string
if identifier.APIVersion != "" {
Expand All @@ -65,47 +70,41 @@ func (i ResultItem) String() string {

// File references a file containing a resource
type File struct {
// Path is relative path to the file containing the resource
Path string `yaml:"path,omitempty"`
// Path is relative path to the file containing the resource.
// This field is required.
Path string `yaml:"path,omitempty" json:"path,omitempty"`

// Index is the index into the file containing the resource
// (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
type Field struct {
// Path is the field path
Path string `yaml:"path,omitempty"`
// Path is the field path. This field is required.
Path string `yaml:"path,omitempty" json:"path,omitempty"`

// 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
SuggestedValue string `yaml:"suggestedValue,omitempty"`
// ProposedValue is the proposed value of the field to fix an issue.
ProposedValue interface{} `yaml:"proposedValue,omitempty" json:"proposedValue,omitempty"`
}

// Result defines a function result which will be set on the emitted ResourceList
type Result struct {
// Name is the name of the function creating the result
Name string `yaml:"name,omitempty"`

// Items are the individual results
Items []ResultItem `yaml:"items,omitempty"`
}
type Results []*Result

// Error enables a Result to be returned as an error
func (e Result) Error() string {
// Error enables Results to be returned as an error
func (e Results) Error() string {
var msgs []string
for _, i := range e.Items {
for _, i := range e {
msgs = append(msgs, i.String())
}
return strings.Join(msgs, "\n\n")
}

// ExitCode provides the exit code based on the result's severity
func (e Result) ExitCode() int {
for _, i := range e.Items {
func (e Results) ExitCode() int {
for _, i := range e {
if i.Severity == Error {
return 1
}
Expand Down

0 comments on commit 4fd77b3

Please sign in to comment.