Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide error for invalid bindings #1049

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 46 additions & 4 deletions pkg/apis/triggers/v1alpha1/trigger_binding_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,29 +18,71 @@ package v1alpha1

import (
"context"
"fmt"
"strings"

"k8s.io/apimachinery/pkg/util/sets"
"knative.dev/pkg/apis"
)

// Validate TriggerBinding.
func (tb *TriggerBinding) Validate(ctx context.Context) *apis.FieldError {
return tb.Spec.Validate(ctx)
return tb.Spec.Validate(ctx).ViaField("spec")
}

// Validate TriggerBindingSpec.
func (s *TriggerBindingSpec) Validate(ctx context.Context) *apis.FieldError {
return validateParams(s.Params)
return validateParams(s.Params).ViaField("params")
}

func validateParams(params []Param) *apis.FieldError {
// Ensure there aren't multiple params with the same name.
seen := sets.NewString()
for _, param := range params {
for i, param := range params {
if seen.Has(param.Name) {
return apis.ErrMultipleOneOf("spec.params")
return apis.ErrMultipleOneOf(fmt.Sprintf("[%d].name", i))
}
seen.Insert(param.Name)
errs := validateParamValue(param.Value).ViaField(fmt.Sprintf("[%d]", i))
if errs != nil {
return errs
}
}
return nil
}

func validateParamValue(in string) *apis.FieldError {
if !strings.Contains(in, "$(") {
return nil
}
// Splits string on $( to find potential Tekton expressions
maybeExpressions := strings.Split(in, "$(")
terminated := true
for _, e := range maybeExpressions[1:] { // Split always returns at least one element
// Iterate until we find the first unbalanced )
numOpenBrackets := 0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: is it possible to reuse this bit from the jsonpath.go? Maybe this bit could be extracted to its own function? If that's too much for this PR, I'm happy to merge this as is.

if !terminated {
return apis.ErrInvalidValue(in, "value")
}
terminated = false
for _, ch := range e {
switch ch {
case '(':
numOpenBrackets++
case ')':
numOpenBrackets--
if numOpenBrackets < 0 {
terminated = true
}
default:
continue
}
if numOpenBrackets < 0 {
terminated = true
break
}
}
}
return nil

}
43 changes: 39 additions & 4 deletions pkg/apis/triggers/v1alpha1/trigger_binding_validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"context"
"testing"

"github.com/google/go-cmp/cmp"
"github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1"
bldr "github.com/tektoncd/triggers/test/builder"
)
Expand All @@ -37,7 +38,8 @@ func Test_TriggerBindingValidate(t *testing.T) {
bldr.TriggerBindingSpec(
bldr.TriggerBindingParam("param1", "$(body.input1)"),
bldr.TriggerBindingParam("param2", "$(body.input2)"),
bldr.TriggerBindingParam("param3", "$(body.input3)"),
bldr.TriggerBindingParam("param3", "$(body.(input3))"),
bldr.TriggerBindingParam("param4", "static-input"),
)),
}, {
name: "multiple params case sensitive",
Expand All @@ -47,6 +49,12 @@ func Test_TriggerBindingValidate(t *testing.T) {
bldr.TriggerBindingParam("PARAM1", "$(body.input2)"),
bldr.TriggerBindingParam("Param1", "$(body.input3)"),
)),
}, {
name: "multiple expressions in one body",
tb: bldr.TriggerBinding("name", "namespace",
bldr.TriggerBindingSpec(
bldr.TriggerBindingParam("param1", "$(body.input1)-$(body.input2)"),
)),
}}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand All @@ -59,8 +67,9 @@ func Test_TriggerBindingValidate(t *testing.T) {

func Test_TriggerBindingValidate_error(t *testing.T) {
tests := []struct {
name string
tb *v1alpha1.TriggerBinding
name string
tb *v1alpha1.TriggerBinding
errMsg string
}{{
name: "duplicate params",
tb: bldr.TriggerBinding("name", "namespace",
Expand All @@ -69,12 +78,38 @@ func Test_TriggerBindingValidate_error(t *testing.T) {
bldr.TriggerBindingParam("param1", "$(body.param1)"),
bldr.TriggerBindingParam("param3", "$(body.param1)"),
)),
errMsg: "expected exactly one, got both: spec.params[1].name",
}, {
name: "invalid parameter",
tb: bldr.TriggerBinding("name", "namespace",
bldr.TriggerBindingSpec(
bldr.TriggerBindingParam("param1", "$($(body.param1))"),
)),
errMsg: "invalid value: $($(body.param1)): spec.params[0].value",
}, {
name: "invalid parameter further nested",
tb: bldr.TriggerBinding("name", "namespace",
bldr.TriggerBindingSpec(
bldr.TriggerBindingParam("param1", "$(body.test-$(body.param1))"),
)),
errMsg: "invalid value: $(body.test-$(body.param1)): spec.params[0].value",
}, {
name: "invalid parameter triple nested",
tb: bldr.TriggerBinding("name", "namespace",
bldr.TriggerBindingSpec(
bldr.TriggerBindingParam("param1", "$($($(body.param1)))"),
)),
errMsg: "invalid value: $($($(body.param1))): spec.params[0].value",
}}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := tt.tb.Validate(context.Background()); err == nil {
err := tt.tb.Validate(context.Background())
if err == nil {
t.Errorf("TriggerBinding.Validate() expected error for TriggerBinding: %v", tt.tb)
}
if diff := cmp.Diff(tt.errMsg, err.Error()); diff != "" {
t.Errorf("-want +got: %s", diff)
}
})
}
}