From d17e6685892c4bdc3dc666953f98d5dc8dd9c79b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20P=C3=A9rez=20Schr=C3=B6der?= <2639770+nomonamo@users.noreply.github.com> Date: Tue, 12 Nov 2019 18:21:11 +0100 Subject: [PATCH] accessor for step error in templating values (#16) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pablo Pérez Schröder <2639770+nomonamo@users.noreply.github.com> --- README.md | 2 ++ engine/engine.go | 1 + engine/engine_test.go | 5 ++++- engine/step/condition.go | 6 +++--- engine/templates_tests/simple.yaml | 8 ++++++++ engine/values/values.go | 21 ++++++++++++++++++++- models/resolution/resolution.go | 1 + 7 files changed, 39 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 4a6017e9..8a6079ab 100644 --- a/README.md +++ b/README.md @@ -142,6 +142,8 @@ A user can be allowed to resolve a task in three ways: - `.resolver_input.[INPUT_NAME]`: the value of an input provided by the task's resolver - `.step.[STEP_NAME].output.foo`: field `foo` from the output of a named step - `.step.[STEP_NAME].metadata.HTTPStatus`: field `HTTPStatus` from the metadata of a named step +- `.step.[STEP_NAME].children`: the collection of results from a 'foreach' step +- `.step.[STEP_NAME].error`: error message from a failed step - `.config.[CONFIG_ITEM].bar`: field `bar` from a config item (configstore, see above) - `.iterator.foo`: field `foo` from the iterator in a loop (see `foreach` steps below) diff --git a/engine/engine.go b/engine/engine.go index 8c5dc501..825918a6 100644 --- a/engine/engine.go +++ b/engine/engine.go @@ -348,6 +348,7 @@ func resolve(dbp zesty.DBProvider, res *resolution.Resolution, t *task.Task, deb res.Values.SetOutput(s.Name, s.Output) res.Values.SetMetadata(s.Name, s.Metadata) res.Values.SetChildren(s.Name, s.Children) + res.Values.SetError(s.Name, s.Error) // call after-run step logic modifiedSteps := map[string]bool{ diff --git a/engine/engine_test.go b/engine/engine_test.go index f18577be..80502c9a 100644 --- a/engine/engine_test.go +++ b/engine/engine_test.go @@ -162,9 +162,12 @@ func TestSimpleTemplate(t *testing.T) { res, err := runTask("simple.yaml", input, nil) assert.Equal(t, nil, err) - assert.Equal(t, resolution.StateDone, res.State) + assert.Equal(t, resolution.StateError, res.State) assert.Equal(t, step.StateDone, res.Steps["stepOne"].State) assert.Equal(t, step.StateDone, res.Steps["stepTwo"].State) + assert.Equal(t, step.StateServerError, res.Steps["stepThree"].State) + + assert.Equal(t, "FAIL!", res.Values.GetError("stepThree")) } func TestClientError(t *testing.T) { diff --git a/engine/step/condition.go b/engine/step/condition.go index 34763def..d9a2e867 100644 --- a/engine/step/condition.go +++ b/engine/step/condition.go @@ -18,7 +18,7 @@ const ( LT = "LT" GE = "GE" LE = "LE" - REGEXP = "regexp" + REGEXP = "REGEXP" ) type ( @@ -51,7 +51,7 @@ func (a *Assert) Eval(v *values.Values, item interface{}, stepName string) error valStr := strings.Replace(string(val), "", "", -1) expStr := strings.Replace(string(expected), "", "", -1) - switch a.Operator { + switch strings.ToUpper(a.Operator) { // normalized operator, accept both lower case and upper case from template case EQ: if valStr != expStr { return ErrConditionNotMet(fmt.Sprintf("Condition not met: expected '%s', got '%s': %s", expStr, valStr, a.Message)) @@ -100,7 +100,7 @@ func (a *Assert) Eval(v *values.Values, item interface{}, stepName string) error // ie. the operator is among the accepted values listed above func (a *Assert) Valid() error { if a != nil { - switch a.Operator { + switch strings.ToUpper(a.Operator) { case EQ, NE, GT, LT, GE, LE: case REGEXP: if _, err := regexp.Compile(a.Expected); err != nil { diff --git a/engine/templates_tests/simple.yaml b/engine/templates_tests/simple.yaml index 61a77a48..113433e6 100644 --- a/engine/templates_tests/simple.yaml +++ b/engine/templates_tests/simple.yaml @@ -17,3 +17,11 @@ steps: configuration: output: foo: baz + stepThree: + description: third step, fails + dependencies: [stepTwo] + action: + type: echo + configuration: + error_message: FAIL! + \ No newline at end of file diff --git a/engine/values/values.go b/engine/values/values.go index 609e6466..8e2ad1af 100644 --- a/engine/values/values.go +++ b/engine/values/values.go @@ -9,8 +9,8 @@ import ( "github.com/Masterminds/sprig" "github.com/juju/errors" - "github.com/robertkrimen/otto" "github.com/ovh/utask" + "github.com/robertkrimen/otto" ) // keys to store/retrieve data from a Values struct @@ -26,6 +26,7 @@ const ( OutputKey = "output" MetadataKey = "metadata" ChildrenKey = "children" + ErrorKey = "error" ) // Values is a container for all the live data of a running task @@ -124,6 +125,21 @@ func (v *Values) UnsetChildren(stepName string) { v.unsetStepData(stepName, ChildrenKey) } +// GetError returns the error resulting from a failed step +func (v *Values) GetError(stepName string) interface{} { + return v.getStepData(stepName, ErrorKey) +} + +// SetChildren stores the error resulting from a failed step +func (v *Values) SetError(stepName string, value interface{}) { + v.setStepData(stepName, ErrorKey, value) +} + +// UnsetChildren empties the error from a failed step +func (v *Values) UnsetError(stepName string) { + v.unsetStepData(stepName, ErrorKey) +} + func (v *Values) getStepData(stepName, field string) interface{} { stepmap := v.m[StepKey].(map[string]interface{}) if stepmap[stepName] == nil { @@ -213,6 +229,9 @@ func (v *Values) Apply(templateStr string, item interface{}, stepName string) ([ v.SetChildren(utask.This, v.GetChildren(stepName)) defer v.UnsetChildren(utask.This) + + v.SetError(utask.This, v.GetError(stepName)) + defer v.UnsetError(utask.This) } err = tmpl.Execute(b, v.m) diff --git a/models/resolution/resolution.go b/models/resolution/resolution.go index d7eda1fe..c39e06fb 100644 --- a/models/resolution/resolution.go +++ b/models/resolution/resolution.go @@ -460,6 +460,7 @@ func (r *Resolution) setSteps(st map[string]*step.Step) { r.Values.SetOutput(name, s.Output) r.Values.SetMetadata(name, s.Metadata) r.Values.SetChildren(name, s.Children) + r.Values.SetError(s.Name, s.Error) } }