diff --git a/cmd/analyzer/main.go b/cmd/analyzer/main.go index 663d87e0..f8e57da8 100644 --- a/cmd/analyzer/main.go +++ b/cmd/analyzer/main.go @@ -95,7 +95,7 @@ func main() { selectors := []engine.RuleSelector{} if labelSelector != "" { - selector, err := labels.NewLabelSelector[*engine.RuleMeta](labelSelector) + selector, err := labels.NewLabelSelector[*engine.RuleMeta](labelSelector, nil) if err != nil { log.Error(err, "failed to create label selector from expression", "selector", labelSelector) os.Exit(1) @@ -105,7 +105,7 @@ func main() { var dependencyLabelSelector *labels.LabelSelector[*konveyor.Dep] if depLabelSelector != "" { - dependencyLabelSelector, err = labels.NewLabelSelector[*konveyor.Dep](depLabelSelector) + dependencyLabelSelector, err = labels.NewLabelSelector[*konveyor.Dep](depLabelSelector, nil) if err != nil { log.Error(err, "failed to create label selector from expression", "selector", labelSelector) os.Exit(1) diff --git a/cmd/dep/main.go b/cmd/dep/main.go index 998e7588..f68c05f7 100644 --- a/cmd/dep/main.go +++ b/cmd/dep/main.go @@ -54,7 +54,7 @@ func main() { var labelSelector *labels.LabelSelector[*konveyor.Dep] if depLabelSelector != "" { - labelSelector, err = labels.NewLabelSelector[*konveyor.Dep](depLabelSelector) + labelSelector, err = labels.NewLabelSelector[*konveyor.Dep](depLabelSelector, nil) if err != nil { log.Error(err, "invalid label selector") os.Exit(1) diff --git a/demo-output.yaml b/demo-output.yaml index 1a626c5a..bb6bff68 100644 --- a/demo-output.yaml +++ b/demo-output.yaml @@ -346,6 +346,7 @@ file: file:///analyzer-lsp/examples/java/example/src/main/java/com/example/apps/App.java kind: Module name: io.fabric8.kubernetes.api.model.apiextensions.v1beta1.CustomResourceDefinition + package: com.example.apps - uri: file:///analyzer-lsp/examples/java/example/src/main/java/com/example/apps/App.java message: apiextensions/v1beta1/customresourcedefinitions is deprecated, apiextensions/v1/customresourcedefinitions should be used instead codeSnip: " 4 \n 5 public class App \n 6 {\n 7 \n 8 /**\n 9 * {@link CustomResourceDefinition}\n10 * @param args\n11 */\n12 public static void main( String[] args )\n13 {\n14 CustomResourceDefinition crd = new CustomResourceDefinition();\n15 System.out.println( crd );\n16 \n17 GenericClass element = new GenericClass(\"Hello world!\");\n18 element.get();\n19 }\n20 }\n" @@ -354,6 +355,7 @@ file: file:///analyzer-lsp/examples/java/example/src/main/java/com/example/apps/App.java kind: Method name: main + package: com.example.apps lang-ref-003: description: "" category: potential @@ -366,6 +368,7 @@ file: file:///analyzer-lsp/examples/java/example/src/main/java/com/example/apps/App.java kind: Method name: main + package: com.example.apps - uri: file:///analyzer-lsp/examples/java/example/src/main/java/com/example/apps/App.java message: java found apiextensions/v1/customresourcedefinitions found file:///analyzer-lsp/examples/java/example/src/main/java/com/example/apps/App.java:3 codeSnip: " 1 package com.example.apps;\n 2 \n 3 import io.fabric8.kubernetes.api.model.apiextensions.v1beta1.CustomResourceDefinition;\n 4 \n 5 public class App \n 6 {\n 7 \n 8 /**\n 9 * {@link CustomResourceDefinition}\n10 * @param args\n11 */\n12 public static void main( String[] args )\n13 {" @@ -374,6 +377,7 @@ file: file:///analyzer-lsp/examples/java/example/src/main/java/com/example/apps/App.java kind: Module name: io.fabric8.kubernetes.api.model.apiextensions.v1beta1.CustomResourceDefinition + package: com.example.apps lang-ref-004: description: "" category: potential @@ -387,6 +391,7 @@ file: file:///analyzer-lsp/examples/java/example/src/main/java/com/example/apps/App.java kind: Method name: main + package: com.example.apps multiple-actions-001: description: "" category: potential @@ -445,6 +450,7 @@ file: file:///analyzer-lsp/examples/java/example/src/main/java/com/example/apps/Bean.java kind: Class name: Singleton + package: com.example.apps - uri: file:///analyzer-lsp/examples/java/example/src/main/java/com/example/apps/Bean.java message: condition entries should evaluate out of order codeSnip: " 1 package com.example.apps;\n 2 \n 3 import javax.ejb.SessionBean;\n 4 import javax.ejb.Singleton;\n 5 \n 6 @Singleton\n 7 public abstract class Bean implements SessionBean {\n 8 }\n" @@ -453,6 +459,7 @@ file: file:///analyzer-lsp/examples/java/example/src/main/java/com/example/apps/Bean.java kind: Class name: Bean + package: com.example.apps singleton-sessionbean-00002: description: "" category: potential @@ -465,6 +472,7 @@ file: file:///analyzer-lsp/examples/java/example/src/main/java/com/example/apps/Bean.java kind: Class name: Singleton + package: com.example.apps - uri: file:///analyzer-lsp/examples/java/example/src/main/java/com/example/apps/Bean.java message: condition entries should evaluate in order codeSnip: " 1 package com.example.apps;\n 2 \n 3 import javax.ejb.SessionBean;\n 4 import javax.ejb.Singleton;\n 5 \n 6 @Singleton\n 7 public abstract class Bean implements SessionBean {\n 8 }\n" @@ -473,6 +481,7 @@ file: file:///analyzer-lsp/examples/java/example/src/main/java/com/example/apps/Bean.java kind: Class name: Bean + package: com.example.apps tech-tag-001: description: "" category: potential diff --git a/engine/engine.go b/engine/engine.go index 0ea6b4cc..a272f790 100644 --- a/engine/engine.go +++ b/engine/engine.go @@ -410,7 +410,7 @@ func (r *ruleEngine) createViolation(ctx context.Context, conditionResponse Cond var incidentSelector *labels.LabelSelector[internal.VariableLabelSelector] var err error if r.incidentSelector != "" { - incidentSelector, err = labels.NewLabelSelector[internal.VariableLabelSelector](r.incidentSelector) + incidentSelector, err = labels.NewLabelSelector[internal.VariableLabelSelector](r.incidentSelector, internal.MatchVariables) if err != nil { return konveyor.Violation{}, err } diff --git a/engine/internal/variable_labels.go b/engine/internal/variable_labels.go index 0e79d7cf..0e57060b 100644 --- a/engine/internal/variable_labels.go +++ b/engine/internal/variable_labels.go @@ -1,6 +1,9 @@ package internal -import "fmt" +import ( + "fmt" + "strings" +) type VariableLabelSelector map[string]interface{} @@ -16,3 +19,25 @@ func (v VariableLabelSelector) GetLabels() []string { } return s } + +func MatchVariables(elem string, items []string) bool { + // Adding the trailing . to make sure that com.example.apps matches but not com.example2.apps + var containsSearch bool + if strings.Contains(elem, ".") { + elem = fmt.Sprintf("%v.", elem) + containsSearch = true + } + for _, i := range items { + if containsSearch { + if strings.Contains(i, elem) { + return true + } + } else { + if i == elem { + return true + } + } + } + return false + +} diff --git a/engine/labels/labels.go b/engine/labels/labels.go index 44d5d37a..8fa43c7a 100644 --- a/engine/labels/labels.go +++ b/engine/labels/labels.go @@ -28,6 +28,7 @@ const ( type LabelSelector[T Labeled] struct { expr string language gval.Language + matchAny MatchAny } // Helper function to refactor key value label manipulation @@ -49,7 +50,7 @@ func (l *LabelSelector[T]) Matches(v T) (bool, error) { return false, nil } } - expr := getBooleanExpression(l.expr, ruleLabels) + expr := getBooleanExpression(l.expr, ruleLabels, l.matchAny) val, err := l.language.Evaluate(expr, nil) if err != nil { return false, err @@ -80,11 +81,13 @@ type Labeled interface { GetLabels() []string } +type MatchAny func(elem string, items []string) bool + // NewRuleSelector returns a new rule selector that works on rule labels // it enables using string expressions to form complex label queries // supports "&&", "||" and "!" operators, "(" ")" for grouping, operands // are string labels in key=val format, keys can be subdomain prefixed -func NewLabelSelector[T Labeled](expr string) (*LabelSelector[T], error) { +func NewLabelSelector[T Labeled](expr string, match MatchAny) (*LabelSelector[T], error) { language := gval.NewLanguage( gval.Ident(), gval.Parentheses(), @@ -103,13 +106,17 @@ func NewLabelSelector[T Labeled](expr string) (*LabelSelector[T], error) { gval.InfixBoolOperator("||", func(a, b bool) (interface{}, error) { return a || b, nil }), ) // we need this hack to force validation - _, err := gval.Evaluate(getBooleanExpression(expr, map[string][]string{}), nil) + _, err := gval.Evaluate(getBooleanExpression(expr, map[string][]string{}, matchesAny), nil) if err != nil { return nil, fmt.Errorf("invalid expression '%s'", expr) } + if match == nil { + match = matchesAny + } return &LabelSelector[T]{ language: language, expr: expr, + matchAny: match, }, nil } @@ -207,7 +214,7 @@ func getLabelsFromExpression(expr string) (map[string][]string, error) { // does not understand labels as operands. "konveyor.io/k1=v1 && v2" will look // something like "true && false" as a boolean expression depending on passed labels // we wouldn't need this if gval supported writing custom operands -func getBooleanExpression(expr string, compareLabels map[string][]string) string { +func getBooleanExpression(expr string, compareLabels map[string][]string, matchAny MatchAny) string { exprLabels, err := getLabelsFromExpression(expr) if err != nil { return expr @@ -221,7 +228,7 @@ func getBooleanExpression(expr string, compareLabels map[string][]string) string } if labelVals, ok := compareLabels[exprLabelKey]; !ok { replaceMap[toReplace] = "false" - } else if exprLabelVal != "" && !matchesAny(exprLabelVal, labelVals) { + } else if exprLabelVal != "" && !matchAny(exprLabelVal, labelVals) { replaceMap[toReplace] = "false" } else { replaceMap[toReplace] = "true" @@ -271,7 +278,7 @@ func labelValueMatches(matchWith string, candidate string) bool { mMatch := versionRegex.FindStringSubmatch(matchWith) cMatch := versionRegex.FindStringSubmatch(candidate) if len(mMatch) != 3 { - return matchWith == candidate + return candidate == matchWith } mName, mVersion, mVersionRangeSymbol := versionRegex.ReplaceAllString(matchWith, ""), mMatch[1], mMatch[2] diff --git a/engine/labels/labels_test.go b/engine/labels/labels_test.go index 3a5281b2..222f9aae 100644 --- a/engine/labels/labels_test.go +++ b/engine/labels/labels_test.go @@ -117,7 +117,7 @@ func Test_getBooleanExpression(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := getBooleanExpression(tt.expr, tt.compareLabels); got != tt.want { + if got := getBooleanExpression(tt.expr, tt.compareLabels, matchesAny); got != tt.want { t.Errorf("getBooleanExpression() = %v, want %v", got, tt.want) } }) @@ -280,7 +280,7 @@ func TestNewRuleSelector(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - _, err := NewLabelSelector[internal.VariableLabelSelector](tt.expr) + _, err := NewLabelSelector[internal.VariableLabelSelector](tt.expr, nil) if (err != nil) != tt.wantErr { t.Errorf("NewRuleSelector() error = %v, wantErr %v", err, tt.wantErr) return @@ -389,7 +389,7 @@ func Test_ruleSelector_Matches(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - s, _ := NewLabelSelector[Labeled](tt.expr) + s, _ := NewLabelSelector[Labeled](tt.expr, nil) if got, _ := s.Matches(ruleMeta{Labels: tt.ruleLabels}); got != tt.want { t.Errorf("ruleSelector.Matches() = %v, want %v", got, tt.want) } diff --git a/provider/provider_test.go b/provider/provider_test.go index 6b6eddcf..2a2045e1 100644 --- a/provider/provider_test.go +++ b/provider/provider_test.go @@ -226,7 +226,7 @@ func Test_matchDepLabelSelector(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - labelSelector, err := labels.NewLabelSelector[*Dep](tt.labelSelector) + labelSelector, err := labels.NewLabelSelector[*Dep](tt.labelSelector, nil) if err != nil { t.Errorf("invalid label selector %s", tt.labelSelector) return