-
Notifications
You must be signed in to change notification settings - Fork 114
/
filter.go
159 lines (138 loc) · 6.04 KB
/
filter.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
/*
Copyright 2019 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package jobutil
import (
"regexp"
"github.com/sirupsen/logrus"
"github.com/jenkins-x/lighthouse/pkg/config/job"
"k8s.io/apimachinery/pkg/util/sets"
)
// TestAllRe provides the regex for `/test all`
var TestAllRe = regexp.MustCompile(`(?m)^/(?:lh-)?test all,?($|\s.*)`)
// RetestRe provides the regex for `/retest`
var RetestRe = regexp.MustCompile(`(?m)^/(?:lh-)?retest\s*$`)
// OkToTestRe provies the regex for `/ok-to-test`
var OkToTestRe = regexp.MustCompile(`(?m)^/(?:lh-)?ok-to-test\s*$`)
// Filter digests a presubmit config to determine if:
// - we the presubmit matched the filter
// - we know that the presubmit is forced to run
// - what the default behavior should be if the presubmit
// runs conditionally and does not match trigger conditions
type Filter func(p job.Presubmit) (shouldRun bool, forcedToRun bool, defaultBehavior bool)
// CommandFilter builds a filter for `/test foo`
func CommandFilter(body string) Filter {
return func(p job.Presubmit) (bool, bool, bool) {
return p.TriggerMatches(body), p.TriggerMatches(body), true
}
}
// TestAllFilter builds a filter for the automatic behavior of `/test all`.
// Pipelines that explicitly match `/test all` in their trigger regex will be
// handled by a commandFilter for the comment in question.
func TestAllFilter() Filter {
return func(p job.Presubmit) (bool, bool, bool) {
return !p.NeedsExplicitTrigger(), false, false
}
}
// AggregateFilter builds a filter that evaluates the child filters in order
// and returns the first match
func AggregateFilter(filters []Filter) Filter {
return func(presubmit job.Presubmit) (bool, bool, bool) {
for _, filter := range filters {
if shouldRun, forced, defaults := filter(presubmit); shouldRun {
return shouldRun, forced, defaults
}
}
return false, false, false
}
}
// FilterPresubmits determines which presubmits should run and which should be skipped
// by evaluating the user-provided filter.
func FilterPresubmits(filter Filter, changes job.ChangedFilesProvider, branch string, presubmits []job.Presubmit, logger *logrus.Entry) ([]job.Presubmit, []job.Presubmit, error) {
var toTrigger []job.Presubmit
var namesToTrigger []string
var toSkipSuperset []job.Presubmit
for _, presubmit := range presubmits {
matches, forced, defaults := filter(presubmit)
if !matches {
logger.WithField("presubmit", presubmit).Trace("doesn't match command filter")
continue
}
shouldRun, err := presubmit.ShouldRun(branch, changes, forced, defaults)
if err != nil {
return nil, nil, err
}
if shouldRun {
toTrigger = append(toTrigger, presubmit)
namesToTrigger = append(namesToTrigger, presubmit.Name)
} else {
toSkipSuperset = append(toSkipSuperset, presubmit)
}
}
toSkip := determineSkippedPresubmits(toTrigger, toSkipSuperset, logger)
var namesToSkip []string
for _, presubmit := range toSkip {
namesToSkip = append(namesToSkip, presubmit.Name)
}
logger.WithFields(logrus.Fields{"to-trigger": namesToTrigger, "to-skip": namesToSkip}).Debugf("Filtered %d jobs, found %d to trigger and %d to skip.", len(presubmits), len(toTrigger), len(toSkipSuperset))
return toTrigger, toSkip, nil
}
// determineSkippedPresubmits identifies the largest set of contexts we can actually
// post skipped contexts for, given a set of presubmits we're triggering. We don't
// want to skip a job that posts a context that will be written to by a job we just
// identified for triggering or the skipped context will override the triggered one
func determineSkippedPresubmits(toTrigger, toSkipSuperset []job.Presubmit, logger *logrus.Entry) []job.Presubmit {
triggeredContexts := sets.NewString()
for _, presubmit := range toTrigger {
triggeredContexts.Insert(presubmit.Context)
}
var toSkip []job.Presubmit
for _, presubmit := range toSkipSuperset {
if triggeredContexts.Has(presubmit.Context) {
logger.WithFields(logrus.Fields{"context": presubmit.Context, "job": presubmit.Name}).Debug("Not skipping job as context will be created by a triggered job.")
continue
}
toSkip = append(toSkip, presubmit)
}
return toSkip
}
// RetestFilter builds a filter for `/retest`
func RetestFilter(failedContexts, allContexts sets.String) Filter {
return func(p job.Presubmit) (bool, bool, bool) {
return failedContexts.Has(p.Context) || (!p.NeedsExplicitTrigger() && !allContexts.Has(p.Context)), false, true
}
}
type contextGetter func() (sets.String, sets.String, error)
// PresubmitFilter creates a filter for presubmits
func PresubmitFilter(honorOkToTest bool, contextGetter contextGetter, body string, logger *logrus.Entry) (Filter, error) {
// the filters determine if we should check whether a job should run, whether
// it should run regardless of whether its triggering conditions match, and
// what the default behavior should be for that check. Multiple filters
// can match a single presubmit, so it is important to order them correctly
// as they have precedence -- filters that override the false default should
// match before others. We order filters by amount of specificity.
var filters []Filter
filters = append(filters, CommandFilter(body))
if RetestRe.MatchString(body) {
logger.Debug("Using retest filter.")
failedContexts, allContexts, err := contextGetter()
if err != nil {
return nil, err
}
filters = append(filters, RetestFilter(failedContexts, allContexts))
}
if (honorOkToTest && OkToTestRe.MatchString(body)) || TestAllRe.MatchString(body) {
logger.Debug("Using test-all filter.")
filters = append(filters, TestAllFilter())
}
return AggregateFilter(filters), nil
}