From ebfd8780681372f7760151dd1e7612db6a0973b2 Mon Sep 17 00:00:00 2001 From: "konveyor-ci-bot[bot]" <159171263+konveyor-ci-bot[bot]@users.noreply.github.com> Date: Thu, 1 Aug 2024 13:00:28 -0500 Subject: [PATCH] :bug: Fix Targets (Rulesets) with repositories. (#118) (#119) https://issues.redhat.com/browse/MTA-3330 A custom target that references a repository will be created with a related `RuleSet`. A repository may be defined on the ruleSet. An analysis task defines _included_ labels which determine which rulesets are pulled (downloaded) from the hub. Since ruleSets that reference repositories do not have labels, the addon did not know to download it and clone the repository. Solution: The new rules.ruleSets will be used to pass a list of RuleSet (refs) selected by the user (by selecting targets). Each will be be unconditionally fetched by the addon. For each ruleset with a repository the addon will: - clone the repository - update .yaml files - replacing the `labels` element with `konveyor.io/include=always`. related: https://github.com/konveyor/tackle2-ui/pull/2034 --------- Signed-off-by: Jeff Ortel Signed-off-by: Cherry Picker Signed-off-by: Jeff Ortel Signed-off-by: Cherry Picker Co-authored-by: Jeff Ortel --- cmd/rules.go | 150 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 127 insertions(+), 23 deletions(-) diff --git a/cmd/rules.go b/cmd/rules.go index e7594ab..f08fe97 100644 --- a/cmd/rules.go +++ b/cmd/rules.go @@ -1,8 +1,10 @@ package main import ( + "io/fs" "os" "path" + "path/filepath" "regexp" "strconv" "strings" @@ -13,6 +15,12 @@ import ( "github.com/konveyor/tackle2-hub/api" "github.com/konveyor/tackle2-hub/nas" "github.com/rogpeppe/go-internal/semver" + "gopkg.in/yaml.v3" +) + +const ( + // KonveyorIO namespace for labels. + KonveyorIO = "konveyor.io" ) type History = map[uint]byte @@ -22,11 +30,13 @@ var LvRegex = regexp.MustCompile(`(\D+)(\d(?:[\d\.]*\d)?)([\+-])?$`) // Rules settings. type Rules struct { - Path string `json:"path"` - Repository *api.Repository `json:"repository"` - Identity *api.Ref `json:"identity"` - Labels Labels `json:"labels"` - rules []string + Path string `json:"path"` + Repository *api.Repository `json:"repository"` + Identity *api.Ref `json:"identity"` + Labels Labels `json:"labels"` + RuleSets []api.Ref `json:"ruleSets"` + repositories []string + rules []string } // Build assets. @@ -47,6 +57,10 @@ func (r *Rules) Build() (err error) { if err != nil { return } + err = r.Labels.injectAlways(r.repositories) + if err != nil { + return + } return } @@ -86,13 +100,19 @@ func (r *Rules) addFiles() (err error) { } for _, ent := range entries { if ent.Name() == parser.RULE_SET_GOLDEN_FILE_NAME { + r.repositories = append(r.repositories, ruleDir) r.append(ruleDir) return } } + n := 0 for _, ent := range entries { p := path.Join(ruleDir, ent.Name()) r.append(p) + n++ + } + if n > 0 { + r.repositories = append(r.repositories, ruleDir) } return } @@ -100,10 +120,22 @@ func (r *Rules) addFiles() (err error) { // addRuleSets adds rulesets and their dependencies. func (r *Rules) addRuleSets() (err error) { history := make(History) - ruleSets, err := r.Labels.RuleSets() + ruleSets := make([]api.RuleSet, 0) + for _, ref := range r.RuleSets { + var ruleSet *api.RuleSet + ruleSet, err = addon.RuleSet.Get(ref.ID) + if err != nil { + return + } + ruleSets = append( + ruleSets, + *ruleSet) + } + matched, err := r.Labels.RuleSets() if err != nil { return } + ruleSets = append(ruleSets, matched...) for _, ruleSet := range ruleSets { if _, found := history[ruleSet.ID]; found { continue @@ -223,6 +255,7 @@ func (r *Rules) addRuleSetRepository(ruleset *api.RuleSet) (err error) { return } ruleDir := path.Join(rootDir, ruleset.Repository.Path) + r.repositories = append(r.repositories, ruleDir) r.append(ruleDir) return } @@ -255,6 +288,7 @@ func (r *Rules) addRepository() (err error) { return } ruleDir := path.Join(rootDir, r.Repository.Path) + r.repositories = append(r.repositories, ruleDir) r.append(ruleDir) return } @@ -271,26 +305,14 @@ func (r *Rules) addSelector(options *command.Options) (err error) { // convert windup rules. func (r *Rules) convert() (err error) { - output := path.Join(RuleDir, "converted") - err = nas.MkDir(output, 0755) - if err != nil { - return - } cmd := command.New("/usr/bin/windup-shim") cmd.Options.Add("convert") - cmd.Options.Add("--outputdir", output) + cmd.Options.Add("--outputdir", RuleDir) cmd.Options.Add(RuleDir) err = cmd.Run() if err != nil { return } - converted, err := os.ReadDir(output) - if err != nil { - return - } - if len(converted) > 0 { - r.append(output) - } return } @@ -352,6 +374,88 @@ func (r *Labels) ruleSetMap() (mp RuleSetMap, err error) { return } +// injectAlways - Replaces the labels in every rule file +// with konveyor.io/include=always. +func (r *Labels) injectAlways(paths []string) (err error) { + read := func(m any, p string) (err error) { + f, err := os.Open(p) + if err != nil { + return + } + defer func() { + _ = f.Close() + }() + d := yaml.NewDecoder(f) + err = d.Decode(m) + return + } + write := func(m any, p string) (err error) { + f, err := os.Create(p) + if err != nil { + return + } + defer func() { + _ = f.Close() + }() + en := yaml.NewEncoder(f) + err = en.Encode(m) + return + } + inspect := func(p string, info fs.FileInfo, wErr error) (_ error) { + var err error + if wErr != nil || info.IsDir() { + addon.Log.Error(wErr, p) + return + } + switch strings.ToUpper(path.Ext(p)) { + case "", + ".YAML", + ".YML": + default: + return + } + key := "labels" + if path.Base(p) == parser.RULE_SET_GOLDEN_FILE_NAME { + ruleSet := make(map[any]any) + err = read(&ruleSet, p) + if err != nil { + return + } + ruleSet[key] = []string{"konveyor.io/include=always"} + err = write(&ruleSet, p) + if err != nil { + return + } + } else { + rules := make([]map[any]any, 0) + err = read(&rules, p) + if err != nil { + return + } + for _, rule := range rules { + rule[key] = []string{"konveyor.io/include=always"} + } + err = write(&rules, p) + if err != nil { + return + } + } + return + } + ruleSelector := RuleSelector{Included: r.Included} + selector := ruleSelector.String() + if selector == "" { + return + } + for _, ruleDir := range paths { + err = filepath.Walk(ruleDir, inspect) + if err != nil { + return + } + } + return +} + // RuleSetMap is a map of labels mapped to ruleSets with those labels. type RuleSetMap map[string][]api.RuleSet @@ -426,9 +530,9 @@ func (r Label) Match(other Label) (matched bool) { // Eq returns true when equal. func (r Label) Eq(other Label) (matched bool) { - matched = r.Namespace() != other.Namespace() || - r.Name() != other.Name() || - r.Value() != other.Value() + matched = r.Namespace() == other.Namespace() && + r.Name() == other.Name() && + r.Value() == other.Value() return } @@ -443,7 +547,7 @@ func (r *RuleSelector) String() (selector string) { var other, sources, targets []string for _, s := range r.unique(r.Included) { label := Label(s) - if label.Namespace() != "konveyor.io" { + if label.Namespace() != KonveyorIO { other = append(other, s) continue }