Skip to content

Commit

Permalink
Implement a ruleChecker to flow applied
Browse files Browse the repository at this point in the history
  • Loading branch information
clarsonneur committed Jan 15, 2019
1 parent 986cb70 commit b836549
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 24 deletions.
2 changes: 1 addition & 1 deletion Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ pipeline {
stage('Tests') {
steps {
sh('''set +x ; source ./build-env.sh
go test forjj forjj/creds forjj/drivers forjj/flow forjj/forjfile forjj/git forjj/repo forjj/utils forjj/sources_info''')
go test forjj forjj/creds forjj/drivers forjj/flow forjj/forjfile forjj/git forjj/repo forjj/utils forjj/sources_info forjj/rules''')
}
}
stage('Deploy') {
Expand Down
78 changes: 55 additions & 23 deletions forjfile/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package forjfile
import (
"fmt"
"forjj/drivers"
"forjj/rules"
"forjj/sources_info"
"strings"

Expand Down Expand Up @@ -443,8 +444,28 @@ func (r *RepoStruct) set_forge(f *ForgeYaml) {
r.forge = f
}

// ruleCheck implements a simple rule checker.
// It checks the rule for a key and a value. The value is returned by a get function.
//
// a rule is a string formatted with following syntax and meaning:
// - '<key>:<value>' or '<key>=<value>' - True if key has value equal to <value>. '<key>:<value>' is kept for compatibility but is obsolete.
// - '<key>!=<value>' - True if key has a value NOT equal to <value>
// - '<key>=/<regexp>/' - True if key has value respecting <regexp>.
// - '<key>!=/<regexp>/' - True if key has a value NOT respecting <regexp>
func ruleCheck(get func(string) (string, bool), rule []string) (found bool, err error) {

return
}

// HasApps return a bool if rules are all true on at least one application.
// a rule is a string formatted as '<key>:<value>' or '<key>:*' for any value.
//
// Supported rules syntax are defined by rules module
// a rule is a string formatted with following syntax and meaning:
// - '<key>:<value>' or '<key>=<value>' - True if key has value equal to <value>. '<key>:<value>' is kept for compatibility but is obsolete.
// - '<key>!=<value>' - True if key has a value NOT equal to <value>
// - '<key>=/<regexp>/' - True if key has value respecting <regexp>.
// - '<key>!=/<regexp>/' - True if key has a value NOT respecting <regexp>
//
// a rule is true on an application if it has the key value set to <value>
//
// If the rule is not well formatted, an error is returned.
Expand All @@ -453,47 +474,58 @@ func (r *RepoStruct) set_forge(f *ForgeYaml) {
// If all rules are true, HasApps return true
//
// TODO: Write Unit test of HasApps
func (r *RepoStruct) HasApps(rules ...string) (found bool, err error) {
func (r *RepoStruct) HasApps(rulesList ...string) (found bool, err error) {
if r.apps == nil {
return
}

ruleChecker := rules.NewRuleChecker()

for appRelName, app := range r.apps {
found = true
for _, rule := range rules {
ruleToCheck := strings.Split(rule, ":")
if len(ruleToCheck) != 2 {
err = fmt.Errorf("rule '%s' is invalid. Format supported is '<key>:<value>'.", rule)
for _, rule := range rulesList {
var key string
if key, _, _, err = ruleChecker.Validate(rule); err != nil {
return
}
if ruleToCheck[0] == "appRelName" {
if appRelName == ruleToCheck[0] {
continue

var ruleOk bool
if key == "appRelName" {
ruleOk, err = ruleChecker.Check(func(string) (string, bool) {
return appRelName, true
})
if err != nil {
return
}
found = false
break
}
v, found2, _ := app.Get(ruleToCheck[0])
if ruleToCheck[1] == "*" {
if found2 {
if ruleOk {
continue
}
found = false
break
} else if found2 {
if v.GetString() != ruleToCheck[1] {
found = false
break
}
ruleOk, err = ruleChecker.Check(func(key string) (value string, isFound bool) {
v, found, _ := app.Get(key)
if !found {
return
}
} else {
found = false
return v.GetString(), found
})
if err != nil {
return
}
if ruleOk {
continue
}

found = false
break
}
if found {
gotrace.Trace("Found an application which meets '%s'", rules)
gotrace.Trace("Found an application which meets '%s'", rulesList)
return
}
}
gotrace.Trace("NO application found which meets '%s'", rules)
gotrace.Trace("NO application found which meets '%s'", rulesList)
return
}

Expand Down
89 changes: 89 additions & 0 deletions rules/rule.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package rules

import (
"regexp"
"fmt"
"errors"
)

// rules package defines a rule checker system.
// It checks the rule for a key and a value. The value is returned by a get function.
//
// a rule is a string formatted with following syntax and meaning:
// - '<key>:<value>' or '<key>=<value>' - True if key has value equal to <value>. '<key>:<value>' is kept for compatibility but is obsolete.
// - '<key>!=<value>' - True if key has a value NOT equal to <value>
// - '<key>=/<regexp>/' - True if key has value respecting <regexp>.
// - '<key>!=/<regexp>/' - True if key has a value NOT respecting <regexp>

// RuleChecker is the core Object to manage a rule check.
type RuleChecker struct {
ruleRE *regexp.Regexp
ruleToCheck []string
key, op, value string
}

// NewRuleChecker returns a RuleChecker object
func NewRuleChecker() (ret *RuleChecker) {
ret = new(RuleChecker)
ret.ruleRE, _ = regexp.Compile(`^(.*?)(:|!?=/?)(.*)(/?)$`)
return
}

// Validate check if the rule string is valid and can be used to check.
// This function must be called before Check()
func (r *RuleChecker)Validate(rule string) (key, op, value string, err error) {
if r == nil {
err = errors.New("Invalid RuleChecker object")
return
}
r.ruleToCheck = r.ruleRE.FindStringSubmatch(rule)
if r.ruleToCheck == nil {
err = fmt.Errorf("rule '%s' is invalid. Format supported is '<key>[!]=<value>', <key>[!]=/<regExp>/", rule)
return
}
r.key = r.ruleToCheck[1]
r.op = r.ruleToCheck[2]
r.value = r.ruleToCheck[3]
return r.key, r.op, r.value, nil
}

// Check check if the key respect the value/regexp and returns true if confirmed.
//
// The string value is given by the call to the `get` function
func (r *RuleChecker)Check(get func(string)(string, bool)) (matched bool, err error) {
if r == nil {
return false, errors.New("Invalid RuleChecker object")
}

value, found := get(r.key)

switch r.op {
case "=", ":":
if r.value == "*" && found {
return true, nil
}
return r.value == value, nil
case "!=":
if r.value == "*" && !found {
return true, nil
}
return r.value != value, nil
case "=/":
if r.ruleToCheck[4] != "/" {
return false, fmt.Errorf("Invalid regexp rule. Missing leading '/'")
}
matched, err = regexp.MatchString(r.value, value)
return
case "!=/":
if r.ruleToCheck[4] != "/" {
return false, fmt.Errorf("Invalid regexp rule. Missing leading '/'")
}
matched, err = regexp.MatchString(r.value, value)
if err != nil {
return
}
return !matched, nil
}

return
}

0 comments on commit b836549

Please sign in to comment.