Skip to content

Commit

Permalink
Merge pull request #1667 from lcarva/EC-666
Browse files Browse the repository at this point in the history
Support multiple terms
  • Loading branch information
lcarva authored Jun 5, 2024
2 parents 5d49916 + 891397d commit 2b77bfe
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 10 deletions.
4 changes: 4 additions & 0 deletions docs/modules/ROOT/pages/configuration.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ xref:ec-policies:ROOT:release_policy.adoc#test_package[Test package] emits resul
test case. A particular test case can be ignored without ignoring the remaining
ones.

It is also possible that a policy rule emits a list of terms. In such cases, the matcher only needs
to match one of those terms. For example, if a policy rule emits multiple terms (`a`, `b`, and `c`),
then a matcher that includes `b` will successfully match it.

A "rule name:term"::

This is similar to "package name:term", but allows finer granularity by only
Expand Down
39 changes: 30 additions & 9 deletions internal/evaluator/conftest_evaluator.go
Original file line number Diff line number Diff line change
Expand Up @@ -826,7 +826,7 @@ func score(name string) int {
// makeMatchers returns the possible matching strings for the result.
func makeMatchers(result Result) []string {
code := ExtractStringFromMetadata(result, metadataCode)
term := ExtractStringFromMetadata(result, metadataTerm)
terms := extractStringsFromMetadata(result, metadataTerm)
parts := strings.Split(code, ".")
var pkg string
if len(parts) >= 2 {
Expand All @@ -840,12 +840,18 @@ func makeMatchers(result Result) []string {
matchers = append(matchers, pkg, fmt.Sprintf("%s.*", pkg), fmt.Sprintf("%s.%s", pkg, rule))
}

// A term can be applied to any of the package matchers above.
if term != "" {
for i, l := 0, len(matchers); i < l; i++ {
matchers = append(matchers, fmt.Sprintf("%s:%s", matchers[i], term))
// A term can be applied to any of the package matchers above. But we don't want to apply a term
// matcher to a matcher that already includes a term.
var termMatchers []string
for _, term := range terms {
if len(term) == 0 {
continue
}
for _, matcher := range matchers {
termMatchers = append(termMatchers, fmt.Sprintf("%s:%s", matcher, term))
}
}
matchers = append(matchers, termMatchers...)

matchers = append(matchers, "*")

Expand All @@ -871,14 +877,29 @@ func extractCollections(result Result) []string {

// ExtractStringFromMetadata returns the string value from the result metadata at the given key.
func ExtractStringFromMetadata(result Result, key string) string {
if maybeValue, exists := result.Metadata[key]; exists {
if value, ok := maybeValue.(string); ok {
return value
}
values := extractStringsFromMetadata(result, key)
if len(values) > 0 {
return values[0]
}
return ""
}

func extractStringsFromMetadata(result Result, key string) []string {
if value, ok := result.Metadata[key].(string); ok && len(value) > 0 {
return []string{value}
}
if anyValues, ok := result.Metadata[key].([]any); ok {
var values []string
for _, anyValue := range anyValues {
if value, ok := anyValue.(string); ok && len(value) > 0 {
values = append(values, value)
}
}
return values
}
return []string{}
}

func withCapabilities(ctx context.Context, capabilities string) context.Context {
return context.WithValue(ctx, capabilitiesKey, capabilities)
}
Expand Down
79 changes: 78 additions & 1 deletion internal/evaluator/conftest_evaluator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,42 @@ func TestConftestEvaluatorIncludeExclude(t *testing.T) {
},
},
},
{
name: "exclude by package name with multiple terms",
results: []Outcome{
{
Failures: []Result{
{Metadata: map[string]any{"code": "breakfast.spam", "term": []any{"eggs", "sgge"}}},
{Metadata: map[string]any{"code": "breakfast.spam", "term": []any{"bacon", "nocab"}}},
{Metadata: map[string]any{"code": "breakfast.sausage"}},
{Metadata: map[string]any{"code": "not_breakfast.spam", "term": []any{"eggs", "sgge"}}},
},
Warnings: []Result{
{Metadata: map[string]any{"code": "breakfast.ham", "term": []any{"eggs", "sgge"}}},
{Metadata: map[string]any{"code": "breakfast.ham", "term": []any{"bacon", "nocab"}}},
{Metadata: map[string]any{"code": "breakfast.hash"}},
{Metadata: map[string]any{"code": "not_breakfast.ham", "term": []any{"eggs", "sgge"}}},
},
},
},
config: &ecc.EnterpriseContractPolicyConfiguration{Exclude: []string{"breakfast:eggs"}},
want: []Outcome{
{
Failures: []Result{
{Metadata: map[string]any{"code": "breakfast.spam", "term": []any{"bacon", "nocab"}}},
{Metadata: map[string]any{"code": "breakfast.sausage"}},
{Metadata: map[string]any{"code": "not_breakfast.spam", "term": []any{"eggs", "sgge"}}},
},
Warnings: []Result{
{Metadata: map[string]any{"code": "breakfast.ham", "term": []any{"bacon", "nocab"}}},
{Metadata: map[string]any{"code": "breakfast.hash"}},
{Metadata: map[string]any{"code": "not_breakfast.ham", "term": []any{"eggs", "sgge"}}},
},
Skipped: []Result{},
Exceptions: []Result{},
},
},
},
{
name: "exclude by package name with wildcard and term",
results: []Outcome{
Expand Down Expand Up @@ -723,6 +759,38 @@ func TestConftestEvaluatorIncludeExclude(t *testing.T) {
},
},
},
{
name: "include by package with multiple terms",
results: []Outcome{
{
Failures: []Result{
{Metadata: map[string]any{"code": "breakfast.spam", "term": []any{"eggs", "sgge"}}},
{Metadata: map[string]any{"code": "breakfast.spam", "term": []any{"bacon", "nocab"}}},
{Metadata: map[string]any{"code": "breakfast.sausage"}},
{Metadata: map[string]any{"code": "not_breakfast.spam", "term": []any{"eggs", "sgge"}}},
},
Warnings: []Result{
{Metadata: map[string]any{"code": "breakfast.ham", "term": []any{"eggs", "sgge"}}},
{Metadata: map[string]any{"code": "breakfast.ham", "term": []any{"bacon", "nocab"}}},
{Metadata: map[string]any{"code": "breakfast.hash"}},
{Metadata: map[string]any{"code": "not_breakfast.ham", "term": []any{"eggs", "sgge"}}},
},
},
},
config: &ecc.EnterpriseContractPolicyConfiguration{Include: []string{"breakfast:eggs"}},
want: []Outcome{
{
Failures: []Result{
{Metadata: map[string]any{"code": "breakfast.spam", "term": []any{"eggs", "sgge"}}},
},
Warnings: []Result{
{Metadata: map[string]any{"code": "breakfast.ham", "term": []any{"eggs", "sgge"}}},
},
Skipped: []Result{},
Exceptions: []Result{},
},
},
},
{
name: "include by package with wildcard and term",
results: []Outcome{
Expand Down Expand Up @@ -1147,7 +1215,7 @@ func TestMakeMatchers(t *testing.T) {
cases := []struct {
name string
code string
term string
term any
want []string
}{
{
Expand All @@ -1157,6 +1225,15 @@ func TestMakeMatchers(t *testing.T) {
"breakfast.spam:eggs", "*",
},
},
{
name: "valid with multiple terms", code: "breakfast.spam", term: []any{"eggs", "ham"},
want: []string{
"breakfast", "breakfast.*", "breakfast.spam",
"breakfast:eggs", "breakfast.*:eggs", "breakfast.spam:eggs",
"breakfast:ham", "breakfast.*:ham", "breakfast.spam:ham",
"*",
},
},
{
name: "valid without term", code: "breakfast.spam",
want: []string{"breakfast", "breakfast.*", "breakfast.spam", "*"},
Expand Down

0 comments on commit 2b77bfe

Please sign in to comment.