Skip to content
This repository has been archived by the owner on Oct 19, 2024. It is now read-only.

Commit

Permalink
feat: improve oncePer evaluate (#228)
Browse files Browse the repository at this point in the history
Signed-off-by: Ryota Sakamoto <[email protected]>
  • Loading branch information
ryota-sakamoto authored Apr 3, 2021
1 parent c4c3cf1 commit 1bb7e92
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 14 deletions.
16 changes: 16 additions & 0 deletions docs/triggers.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,22 @@ data:
send: [app-sync-succeeded]
```

### oncePer

The `oncePer` filed is supported like as follows.

```yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
annotations:
example.com/version: v0.1
```

```yaml
oncePer: app.metadata.annotations["example.com/version"]
```

## Default Triggers

You can use `defaultTriggers` field instead of specifying individual triggers to the annotations.
Expand Down
36 changes: 22 additions & 14 deletions pkg/triggers/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@ import (
"crypto/sha1"
"encoding/base64"
"fmt"
"strings"

"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"

"github.com/antonmedv/expr"
"github.com/antonmedv/expr/vm"
Expand Down Expand Up @@ -34,18 +31,31 @@ type Service interface {

type service struct {
compiledConditions map[string]*vm.Program
compiledOncePer map[string]*vm.Program
triggers map[string][]Condition
}

func NewService(triggers map[string][]Condition) (*service, error) {
svc := service{map[string]*vm.Program{}, triggers}
svc := service{
compiledConditions: map[string]*vm.Program{},
compiledOncePer: map[string]*vm.Program{},
triggers: triggers,
}
for _, t := range triggers {
for _, condition := range t {
prog, err := expr.Compile(condition.When)
if err != nil {
return nil, err
}
svc.compiledConditions[condition.When] = prog

if condition.OncePer != "" {
prog, err := expr.Compile(condition.OncePer)
if err != nil {
return nil, err
}
svc.compiledOncePer[condition.OncePer] = prog
}
}
}
return &svc, nil
Expand All @@ -64,26 +74,24 @@ func (svc *service) Run(triggerName string, vars map[string]interface{}) ([]Cond
}
var res []ConditionResult
for i, condition := range t {
prog, ok := svc.compiledConditions[condition.When]
if !ok {
return nil, fmt.Errorf("trigger configiration has changed after initialization")
}
conditionResult := ConditionResult{
Templates: condition.Send,
Key: fmt.Sprintf("[%d].%s", i, hash(condition.When)),
}
// ignore execution error and treat and false result
val, err := expr.Run(prog, vars)
if err == nil {

if prog, ok := svc.compiledConditions[condition.When]; !ok {
return nil, fmt.Errorf("trigger configiration has changed after initialization")
} else if val, err := expr.Run(prog, vars); err == nil { // ignore execution error and treat and false result
boolRes, ok := val.(bool)
conditionResult.Triggered = ok && boolRes
}

if condition.OncePer != "" {
if oncePer, ok, err := unstructured.NestedFieldNoCopy(vars, strings.Split(condition.OncePer, ".")...); err == nil && ok {
conditionResult.OncePer = fmt.Sprintf("%v", oncePer)
if prog, ok := svc.compiledOncePer[condition.OncePer]; ok {
if val, err := expr.Run(prog, vars); err == nil { // ignore execution error and treat and false result
conditionResult.OncePer = fmt.Sprintf("%v", val)
}
}

res = append(res, conditionResult)
}

Expand Down
58 changes: 58 additions & 0 deletions pkg/triggers/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,61 @@ func TestRun_OncePerSet(t *testing.T) {
}}, res)
})
}

func TestRun_OncePer_Evaluate(t *testing.T) {
vars := map[string]interface{}{
"var1": "abc",
"revision": "123",
"app": map[string]interface{}{
"metadata": map[string]interface{}{
"annotations": map[string]interface{}{
"example.com/version": "v0.1",
},
},
},
}

tests := []struct {
OncePer string
Result string
}{
{
OncePer: "revision",
Result: "123",
},
{
OncePer: `app.metadata.annotations["example.com/version"]`,
Result: "v0.1",
},
}

for _, tt := range tests {
svc, err := NewService(map[string][]Condition{
"my-trigger": {{
When: "var1 == 'abc'",
Send: []string{"my-template"},
OncePer: tt.OncePer,
}},
})

if !assert.NoError(t, err) {
t.FailNow()
return
}

conditionKey := fmt.Sprintf("[0].%s", hash("var1 == 'abc'"))

res, err := svc.Run("my-trigger", vars)
if !assert.NoError(t, err) {
t.FailNow()
return
}

assert.Equal(t, []ConditionResult{{
Key: conditionKey,
Triggered: true,
Templates: []string{"my-template"},
OncePer: tt.Result,
}}, res)
}
}

0 comments on commit 1bb7e92

Please sign in to comment.