Skip to content

Commit 3d7f26d

Browse files
committed
chore(e2e): naively parallelize CI jobs by chunking alphabetically
Signed-off-by: Eric Stroczynski <ericstroczynski@gmail.com>
1 parent 4f5c16c commit 3d7f26d

File tree

7 files changed

+131
-19
lines changed

7 files changed

+131
-19
lines changed

.github/workflows/e2e-tests.yml

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,20 @@ on:
66
pull_request:
77
jobs:
88
e2e-tests:
9+
strategy:
10+
matrix:
11+
parallel-id: [0, 1, 2, 3]
912
runs-on: ubuntu-latest
1013
steps:
1114
- uses: actions/checkout@v2
1215
- uses: actions/setup-go@v2
1316
with:
1417
go-version: '~1.16'
15-
- run: make e2e-local E2E_NODES=2 ARTIFACTS_DIR=./artifacts/
16-
- name: Archive Test Artifacts # test results, failed or not, are always uploaded.
17-
if: ${{ always() }}
18-
uses: actions/upload-artifact@v2
19-
with:
20-
name: e2e-test-output-${{(github.event.pull_request.head.sha||github.sha)}}-${{ github.run_id }}
21-
path: ${{ github.workspace }}/bin/artifacts/*
18+
- run: make e2e-local E2E_TEST_CHUNK=${{ matrix.parallel-id }} E2E_TEST_NUM_CHUNKS=${{ strategy.job-total }} E2E_NODES=2 ARTIFACTS_DIR=./artifacts-${{ matrix.parallel-id }}/
19+
archive-artifacts: # test results, failed or not, are always uploaded.
20+
needs: e2e-tests
21+
if: ${{ always() }}
22+
uses: actions/upload-artifact@v2
23+
with:
24+
name: e2e-test-output-${{ (github.event.pull_request.head.sha || github.sha) }}-${{ github.run_id }}
25+
path: ${{ github.workspace }}/bin/artifacts-*

Makefile

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,15 @@ setup-bare: clean e2e.namespace
126126
E2E_NODES ?= 1
127127
E2E_FLAKE_ATTEMPTS ?= 1
128128
E2E_TIMEOUT ?= 90m
129-
E2E_OPTS ?= $(if $(E2E_SEED),-seed '$(E2E_SEED)') $(if $(TEST),-focus '$(TEST)') -flakeAttempts $(E2E_FLAKE_ATTEMPTS) -nodes $(E2E_NODES) -timeout $(E2E_TIMEOUT) -v -randomizeSuites -race -trace -progress
129+
E2E_COND_OPTS := $(if $(E2E_SEED),-seed '$(E2E_SEED)')
130+
E2E_TEST_CHUNK ?= all
131+
E2E_TEST_NUM_CHUNKS ?= 4
132+
ifeq (all,$(E2E_TEST_CHUNK))
133+
E2E_COND_OPTS := $(E2E_COND_OPTS) $(if $(TEST),-focus '$(TEST)')
134+
else
135+
E2E_COND_OPTS := $(E2E_COND_OPTS) -focus "$(shell go run ./test/e2e/split/... -chunks $(E2E_TEST_NUM_CHUNKS) -print-chunk $(E2E_TEST_CHUNK) ./test/e2e)"
136+
endif
137+
E2E_OPTS ?= $(E2E_COND_OPTS) -flakeAttempts $(E2E_FLAKE_ATTEMPTS) -nodes $(E2E_NODES) -timeout $(E2E_TIMEOUT) -v -randomizeSuites -race -trace -progress
130138
E2E_INSTALL_NS ?= operator-lifecycle-manager
131139
E2E_TEST_NS ?= operators
132140

test/e2e/installplan_e2e_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4185,7 +4185,7 @@ func newCSV(name, namespace, replaces string, version semver.Version, owned []ap
41854185
},
41864186
Spec: operatorsv1alpha1.ClusterServiceVersionSpec{
41874187
Replaces: replaces,
4188-
Version: opver.OperatorVersion{version},
4188+
Version: opver.OperatorVersion{Version: version},
41894189
MinKubeVersion: "0.0.0",
41904190
InstallModes: []operatorsv1alpha1.InstallMode{
41914191
{

test/e2e/packagemanifest_e2e_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ var _ = Describe("Package Manifest API lists available Operators from Catalog So
100100

101101
csvAlpha = *csv.DeepCopy()
102102
csvAlpha.SetName(packageAlpha)
103-
csvAlpha.Spec.Version = opver.OperatorVersion{semver.MustParse("0.1.1")}
103+
csvAlpha.Spec.Version = opver.OperatorVersion{Version: semver.MustParse("0.1.1")}
104104
csvAlpha.Spec.Replaces = csv.GetName()
105105
csvAlpha.Spec.Icon = []v1alpha1.Icon{
106106
{

test/e2e/split/main.go

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package main
2+
3+
import (
4+
"flag"
5+
"fmt"
6+
"io/ioutil"
7+
"log"
8+
"math"
9+
"os"
10+
"path/filepath"
11+
"regexp"
12+
"sort"
13+
"strings"
14+
)
15+
16+
var topDescribeRE = regexp.MustCompile(`var _ = Describe\("(.+)", func\(.*`)
17+
18+
func main() {
19+
var numChunks, printChunk int
20+
flag.IntVar(&numChunks, "chunks", 1, "Number of chunks to create focus regexps for")
21+
flag.IntVar(&printChunk, "print-chunk", 0, "Chunk to print a regexp for")
22+
flag.Parse()
23+
24+
if printChunk >= numChunks {
25+
log.Fatalf("the chunk to print (%d) must be a smaller number than the number of chunks (%d)", printChunk, numChunks)
26+
}
27+
28+
dir := flag.Arg(0)
29+
30+
// Clean dir.
31+
var err error
32+
if dir, err = filepath.Abs(dir); err != nil {
33+
log.Fatal(err)
34+
}
35+
wd, err := os.Getwd()
36+
if err != nil {
37+
log.Fatal(err)
38+
}
39+
if dir, err = filepath.Rel(wd, dir); err != nil {
40+
log.Fatal(err)
41+
}
42+
43+
// Find all Ginkgo specs in dir's test files.
44+
// These can be grouped independently.
45+
describeTable := make(map[string]struct{})
46+
matches, err := filepath.Glob(filepath.Join(dir, "*_test.go"))
47+
if err != nil {
48+
log.Fatal(err)
49+
}
50+
for _, match := range matches {
51+
b, err := ioutil.ReadFile(match)
52+
if err != nil {
53+
log.Fatal(err)
54+
}
55+
specNames := topDescribeRE.FindAllSubmatch(b, -1)
56+
if len(specNames) == 0 {
57+
log.Printf("%s: found no top level describes, skipping", match)
58+
continue
59+
}
60+
for _, possibleNames := range specNames {
61+
if len(possibleNames) != 2 {
62+
log.Printf("%s: expected to find 2 submatch, found %d:", match, len(possibleNames))
63+
for _, name := range possibleNames {
64+
log.Printf("\t%s\n", string(name))
65+
}
66+
continue
67+
}
68+
describe := strings.TrimSpace(string(possibleNames[1]))
69+
describeTable[describe] = struct{}{}
70+
}
71+
}
72+
73+
describes := make([]string, len(describeTable))
74+
i := 0
75+
for describeKey := range describeTable {
76+
describes[i] = describeKey
77+
i++
78+
}
79+
sort.Strings(describes)
80+
81+
chunks := make([][]string, numChunks)
82+
interval := int(math.Ceil(float64(len(describes)) / float64(numChunks)))
83+
currIdx := 0
84+
for chunkIdx := 0; chunkIdx < numChunks; chunkIdx++ {
85+
nextIdx := int(math.Min(float64(currIdx+interval), float64(len(describes))))
86+
chunks[chunkIdx] = describes[currIdx:nextIdx]
87+
currIdx = nextIdx
88+
}
89+
90+
sb := strings.Builder{}
91+
sb.WriteString("(")
92+
sb.WriteString(chunks[printChunk][0])
93+
for _, test := range chunks[printChunk][1:] {
94+
sb.WriteString("|")
95+
sb.WriteString(test)
96+
}
97+
sb.WriteString(").*")
98+
99+
fmt.Println(sb.String())
100+
}

test/e2e/subscription_e2e_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2353,7 +2353,7 @@ var (
23532353
},
23542354
Spec: operatorsv1alpha1.ClusterServiceVersionSpec{
23552355
Replaces: "",
2356-
Version: version.OperatorVersion{semver.MustParse("0.1.0")},
2356+
Version: version.OperatorVersion{Version: semver.MustParse("0.1.0")},
23572357
MinKubeVersion: "0.0.0",
23582358
InstallModes: []operatorsv1alpha1.InstallMode{
23592359
{
@@ -2383,7 +2383,7 @@ var (
23832383
},
23842384
Spec: operatorsv1alpha1.ClusterServiceVersionSpec{
23852385
Replaces: outdated,
2386-
Version: version.OperatorVersion{semver.MustParse("0.2.0")},
2386+
Version: version.OperatorVersion{Version: semver.MustParse("0.2.0")},
23872387
MinKubeVersion: "0.0.0",
23882388
InstallModes: []operatorsv1alpha1.InstallMode{
23892389
{
@@ -2413,7 +2413,7 @@ var (
24132413
},
24142414
Spec: operatorsv1alpha1.ClusterServiceVersionSpec{
24152415
Replaces: stable,
2416-
Version: version.OperatorVersion{semver.MustParse("0.1.1")},
2416+
Version: version.OperatorVersion{Version: semver.MustParse("0.1.1")},
24172417
InstallModes: []operatorsv1alpha1.InstallMode{
24182418
{
24192419
Type: operatorsv1alpha1.InstallModeTypeOwnNamespace,
@@ -2442,7 +2442,7 @@ var (
24422442
},
24432443
Spec: operatorsv1alpha1.ClusterServiceVersionSpec{
24442444
Replaces: beta,
2445-
Version: version.OperatorVersion{semver.MustParse("0.3.0")},
2445+
Version: version.OperatorVersion{Version: semver.MustParse("0.3.0")},
24462446
InstallModes: []operatorsv1alpha1.InstallMode{
24472447
{
24482448
Type: operatorsv1alpha1.InstallModeTypeOwnNamespace,

test/e2e/webhook_e2e_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ var _ = Describe("CSVs with a Webhook", func() {
259259
AdmissionReviewVersions: []string{"v1beta1", "v1"},
260260
SideEffects: &sideEffect,
261261
Rules: []admissionregistrationv1.RuleWithOperations{
262-
admissionregistrationv1.RuleWithOperations{
262+
{
263263
Operations: []admissionregistrationv1.OperationType{},
264264
Rule: admissionregistrationv1.Rule{
265265
APIGroups: []string{"*"},
@@ -290,7 +290,7 @@ var _ = Describe("CSVs with a Webhook", func() {
290290
AdmissionReviewVersions: []string{"v1beta1", "v1"},
291291
SideEffects: &sideEffect,
292292
Rules: []admissionregistrationv1.RuleWithOperations{
293-
admissionregistrationv1.RuleWithOperations{
293+
{
294294
Operations: []admissionregistrationv1.OperationType{},
295295
Rule: admissionregistrationv1.Rule{
296296
APIGroups: []string{"operators.coreos.com"},
@@ -321,7 +321,7 @@ var _ = Describe("CSVs with a Webhook", func() {
321321
AdmissionReviewVersions: []string{"v1beta1", "v1"},
322322
SideEffects: &sideEffect,
323323
Rules: []admissionregistrationv1.RuleWithOperations{
324-
admissionregistrationv1.RuleWithOperations{
324+
{
325325
Operations: []admissionregistrationv1.OperationType{},
326326
Rule: admissionregistrationv1.Rule{
327327
APIGroups: []string{"admissionregistration.k8s.io"},
@@ -352,7 +352,7 @@ var _ = Describe("CSVs with a Webhook", func() {
352352
AdmissionReviewVersions: []string{"v1beta1", "v1"},
353353
SideEffects: &sideEffect,
354354
Rules: []admissionregistrationv1.RuleWithOperations{
355-
admissionregistrationv1.RuleWithOperations{
355+
{
356356
Operations: []admissionregistrationv1.OperationType{
357357
admissionregistrationv1.OperationAll,
358358
},
@@ -384,7 +384,7 @@ var _ = Describe("CSVs with a Webhook", func() {
384384
AdmissionReviewVersions: []string{"v1beta1", "v1"},
385385
SideEffects: &sideEffect,
386386
Rules: []admissionregistrationv1.RuleWithOperations{
387-
admissionregistrationv1.RuleWithOperations{
387+
{
388388
Operations: []admissionregistrationv1.OperationType{
389389
admissionregistrationv1.OperationAll,
390390
},

0 commit comments

Comments
 (0)