Skip to content

Commit db37fc8

Browse files
ozevrenistio-testing
authored andcommitted
Add labels to the test framework. (istio#12819)
* Add basic label support to the test framework. * Refactor test framework surface area to use fluent-style. * Apply labels to CircleCI tests & stable integration tests. * Add early exit support to avoid running setup functions when the label set can never match. * Add Citadel tests as presubmit tests. * Remove environments from label usage. * Fixup some of the label usages, and convert some of the test entry points. * Fixup label usage. * Redisable sidecar tests. * Accommodate PR feedback. * Accommodate CR feedback. * Add more CR fixup.
1 parent 5eedeef commit db37fc8

File tree

21 files changed

+852
-307
lines changed

21 files changed

+852
-307
lines changed

Diff for: .circleci/config.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -472,7 +472,7 @@ jobs:
472472
export PATH=$GOPATH/bin:$PATH
473473
make localTestEnv
474474
set -o pipefail
475-
make test.integration.local T=-v | tee -a /go/out/tests/build-log.txt
475+
make test.integration.local.presubmit T=-v | tee -a /go/out/tests/build-log.txt
476476
- <<: *recordZeroExitCodeIfTestPassed
477477
- <<: *recordNonzeroExitCodeIfTestFailed
478478
- <<: *markJobFinishesOnGCS
@@ -508,7 +508,7 @@ jobs:
508508
no_output_timeout: 20m
509509
name: make test.integration.kube
510510
command: |
511-
make test.integration.kube T="-v" | tee -a /go/out/tests/build-log.txt
511+
make test.integration.kube.presubmit T="-v" | tee -a /go/out/tests/build-log.txt
512512
- <<: *recordZeroExitCodeIfTestPassed
513513
- <<: *recordNonzeroExitCodeIfTestFailed
514514
- <<: *markJobFinishesOnGCS

Diff for: pkg/test/framework/context.go

-7
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,12 @@ import (
1818
"testing"
1919

2020
"istio.io/istio/pkg/test/framework/components/environment"
21-
2221
"istio.io/istio/pkg/test/framework/resource"
2322
)
2423

2524
// SuiteContext contains suite-level items used during runtime.
2625
type SuiteContext interface {
2726
resource.Context
28-
29-
// Skip indicates that all of the tests in this suite should be skipped.
30-
Skip(reason string)
31-
32-
// Skip indicates that all of the tests in this suite should be skipped.
33-
Skipf(reasonfmt string, args ...interface{})
3427
}
3528

3629
// TestContext is a test-level context that can be created as part of test executing tests.

Diff for: pkg/test/framework/core/flags.go

+13-2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import (
1919
"fmt"
2020
"os"
2121

22+
"istio.io/istio/pkg/test/framework/label"
23+
2224
"istio.io/istio/pkg/test/framework/components/environment"
2325
)
2426

@@ -28,15 +30,21 @@ var (
2830

2931
// SettingsFromCommandLine returns settings obtained from command-line flags. flag.Parse must be called before
3032
// calling this function.
31-
func SettingsFromCommandLine(testID string) *Settings {
33+
func SettingsFromCommandLine(testID string) (*Settings, error) {
3234
if !flag.Parsed() {
3335
panic("flag.Parse must be called before this function")
3436
}
3537

3638
s := settingsFromCommandLine.Clone()
3739
s.TestID = testID
3840

39-
return s
41+
f, err := label.ParseSelector(s.SelectorString)
42+
if err != nil {
43+
return nil, err
44+
}
45+
s.Selector = f
46+
47+
return s, nil
4048
}
4149

4250
// init registers the command-line flags that we can exposed for "go test".
@@ -52,4 +60,7 @@ func init() {
5260

5361
flag.BoolVar(&settingsFromCommandLine.CIMode, "istio.test.ci", settingsFromCommandLine.CIMode,
5462
"Enable CI Mode. Additional logging and state dumping will be enabled.")
63+
64+
flag.StringVar(&settingsFromCommandLine.SelectorString, "istio.test.select", settingsFromCommandLine.SelectorString,
65+
"Comma separatated list of labels for selecting tests to run (e.g. 'foo,+bar-baz').")
5566
}

Diff for: pkg/test/framework/core/settings.go

+14-6
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import (
1919
"path"
2020
"strings"
2121

22+
"istio.io/istio/pkg/test/framework/label"
23+
2224
"istio.io/istio/pkg/test/framework/components/environment"
2325

2426
"github.com/google/uuid"
@@ -48,6 +50,12 @@ type Settings struct {
4850
// Local working directory root for creating temporary directories / files in. If left empty,
4951
// os.TempDir() will be used.
5052
BaseDir string
53+
54+
// The label selector that the user has specified.
55+
SelectorString string
56+
57+
// The label selector, in parsed form.
58+
Selector label.Selector
5159
}
5260

5361
// RunDir is the name of the dir to output, for this particular run.
@@ -82,11 +90,11 @@ func DefaultSettings() *Settings {
8290
func (s *Settings) String() string {
8391
result := ""
8492

85-
result += fmt.Sprintf("Environment: %v\n", s.Environment)
86-
result += fmt.Sprintf("TestID: %s\n", s.TestID)
87-
result += fmt.Sprintf("RunID: %s\n", s.RunID.String())
88-
result += fmt.Sprintf("NoCleanup: %v\n", s.NoCleanup)
89-
result += fmt.Sprintf("BaseDir: %s\n", s.BaseDir)
90-
93+
result += fmt.Sprintf("Environment: %v\n", s.Environment)
94+
result += fmt.Sprintf("TestID: %s\n", s.TestID)
95+
result += fmt.Sprintf("RunID: %s\n", s.RunID.String())
96+
result += fmt.Sprintf("NoCleanup: %v\n", s.NoCleanup)
97+
result += fmt.Sprintf("BaseDir: %s\n", s.BaseDir)
98+
result += fmt.Sprintf("Selector: %v\n", s.Selector)
9199
return result
92100
}

Diff for: pkg/test/framework/label/filter.go

+115
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
// Copyright 2019 Istio Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package label
16+
17+
import (
18+
"fmt"
19+
"regexp"
20+
"strings"
21+
)
22+
23+
// Selector is a Set of label filter expressions that get applied together to decide whether tests should be selected
24+
// for execution or not.
25+
type Selector struct {
26+
// The constraints are and'ed together.
27+
present Set
28+
absent Set
29+
}
30+
31+
var _ fmt.Stringer = Selector{}
32+
33+
func NewSelector(present []Instance, absent []Instance) Selector {
34+
return Selector{
35+
present: NewSet(present...),
36+
absent: NewSet(absent...),
37+
}
38+
}
39+
40+
var userLabelRegex = regexp.MustCompile("^[a-zA-Z][a-zA-Z0-9_]*(\\.[a-zA-Z0-9_]+)*$")
41+
42+
func ParseSelector(s string) (Selector, error) {
43+
var present, absent []Instance
44+
45+
parts := strings.Split(s, ",")
46+
for _, p := range parts {
47+
if len(p) == 0 {
48+
continue
49+
}
50+
51+
var negative bool
52+
switch p[0] {
53+
case '-':
54+
negative = true
55+
p = p[1:]
56+
case '+':
57+
p = p[1:]
58+
}
59+
60+
if !userLabelRegex.Match([]byte(p)) {
61+
return Selector{}, fmt.Errorf("invalid label name: %q", p)
62+
}
63+
64+
l := Instance(p)
65+
if !all.contains(l) {
66+
return Selector{}, fmt.Errorf("unknown label name: %q", p)
67+
}
68+
69+
if negative {
70+
absent = append(absent, l)
71+
} else {
72+
present = append(present, l)
73+
}
74+
}
75+
76+
pSet := NewSet(present...)
77+
aSet := NewSet(absent...)
78+
79+
if pSet.containsAny(aSet) || aSet.containsAny(pSet) {
80+
return Selector{}, fmt.Errorf("conflicting selector specification: %q", s)
81+
}
82+
83+
return NewSelector(present, absent), nil
84+
}
85+
86+
// Selects returns true, if the given label set satisfies the Selector.
87+
func (f *Selector) Selects(inputs Set) bool {
88+
return !inputs.containsAny(f.absent) && inputs.containsAll(f.present)
89+
}
90+
91+
// Excludes returns false, if the given set of labels, even combined with new ones, could end up satisfying the Selector.
92+
// It returns false, if Matches would never return true, even if new labels are added to the input set.
93+
func (f *Selector) Excludes(inputs Set) bool {
94+
return inputs.containsAny(f.absent)
95+
}
96+
97+
func (f Selector) String() string {
98+
var result string
99+
100+
for _, p := range f.present.All() {
101+
if result != "" {
102+
result += ","
103+
}
104+
result += "+" + string(p)
105+
}
106+
107+
for _, p := range f.absent.All() {
108+
if result != "" {
109+
result += ","
110+
}
111+
result += "-" + string(p)
112+
}
113+
114+
return result
115+
}

Diff for: pkg/test/framework/label/instance.go

+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// Copyright 2019 Istio Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package label
16+
17+
import (
18+
"sort"
19+
"strings"
20+
)
21+
22+
// Instance is a label instance.
23+
type Instance string
24+
25+
// Set is a set of labels
26+
type Set map[Instance]struct{}
27+
28+
// NewSet returns a new label set.
29+
func NewSet(labels ...Instance) Set {
30+
s := make(map[Instance]struct{})
31+
for _, l := range labels {
32+
s[l] = struct{}{}
33+
}
34+
35+
return s
36+
}
37+
38+
// Add adds the given labels and returns a new, combined set
39+
func (l Set) Add(labels ...Instance) Set {
40+
c := l.Clone()
41+
for _, label := range labels {
42+
c[label] = struct{}{}
43+
}
44+
45+
return c
46+
}
47+
48+
// Merge returns a set that is merging of l and s.
49+
func (l Set) Merge(s Set) Set {
50+
c := l.Clone()
51+
for k, v := range s {
52+
c[k] = v
53+
}
54+
55+
return c
56+
}
57+
58+
// Clone this set of labels
59+
func (l Set) Clone() Set {
60+
s := make(map[Instance]struct{})
61+
for k, v := range l {
62+
s[k] = v
63+
}
64+
65+
return s
66+
}
67+
68+
// All returns all labels in this set.
69+
func (l Set) All() []Instance {
70+
r := make([]Instance, 0, len(l))
71+
for label := range l {
72+
r = append(r, label)
73+
}
74+
75+
sort.Slice(r, func(i, j int) bool {
76+
return strings.Compare(string(r[i]), string(r[j])) < 0
77+
})
78+
return r
79+
}
80+
81+
func (l Set) contains(label Instance) bool {
82+
_, found := l[label]
83+
return found
84+
}
85+
86+
func (l Set) containsAny(other Set) bool {
87+
for l2 := range other {
88+
if l.contains(l2) {
89+
return true
90+
}
91+
}
92+
93+
return false
94+
}
95+
96+
func (l Set) containsAll(other Set) bool {
97+
for l2 := range other {
98+
if l.contains(l2) {
99+
continue
100+
}
101+
return false
102+
}
103+
104+
return true
105+
}

Diff for: pkg/test/framework/setup.go renamed to pkg/test/framework/label/labels.go

+10-13
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,16 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
package framework
15+
package label
1616

17-
import (
18-
"istio.io/istio/pkg/test/framework/components/environment"
17+
const (
18+
// Presubmit indicates that the test should be run as part of a presubmit run.
19+
Presubmit Instance = "presubmit"
20+
21+
// Postsubmit indicates that the test should be run as part of a postsubmit run.
22+
Postsubmit Instance = "postsubmit"
1923
)
2024

21-
// RequireEnvironment will mark the suite as skipped if the expected environment is not found.
22-
func RequireEnvironment(n environment.Name) SetupFn { // nolint:interfacer
23-
return func(ctx SuiteContext) error {
24-
if ctx.Environment().EnvironmentName() != n {
25-
ctx.Skipf("RequireEnvironment (%s): Test suite does not support environment: %q",
26-
n.String(), ctx.Environment().EnvironmentName())
27-
}
28-
return nil
29-
}
30-
}
25+
var all = NewSet(
26+
Presubmit,
27+
Postsubmit)

0 commit comments

Comments
 (0)