Skip to content
This repository was archived by the owner on Apr 17, 2019. It is now read-only.

Commit fe721a9

Browse files
authored
Merge pull request #1204 from lavalamp/ff5
Add emergency stop button
2 parents 3a4f8dc + 497a95a commit fe721a9

File tree

2 files changed

+77
-4
lines changed

2 files changed

+77
-4
lines changed

mungegithub/mungers/submit-queue.go

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"net/http"
2525
"sort"
2626
"strconv"
27+
"strings"
2728
"sync"
2829
"sync/atomic"
2930
"time"
@@ -181,6 +182,8 @@ type SubmitQueue struct {
181182
health submitQueueHealth
182183
healthHistory []healthRecord
183184

185+
emergencyMergeStopFlag int32
186+
184187
adminPort int
185188
}
186189

@@ -200,10 +203,38 @@ func init() {
200203
}
201204

202205
// Name is the name usable in --pr-mungers
203-
func (sq SubmitQueue) Name() string { return "submit-queue" }
206+
func (sq *SubmitQueue) Name() string { return "submit-queue" }
204207

205208
// RequiredFeatures is a slice of 'features' that must be provided
206-
func (sq SubmitQueue) RequiredFeatures() []string { return []string{} }
209+
func (sq *SubmitQueue) RequiredFeatures() []string { return []string{} }
210+
211+
func (sq *SubmitQueue) emergencyMergeStop() bool {
212+
return atomic.LoadInt32(&sq.emergencyMergeStopFlag) != 0
213+
}
214+
215+
func (sq *SubmitQueue) setEmergencyMergeStop(stopMerges bool) {
216+
if stopMerges {
217+
atomic.StoreInt32(&sq.emergencyMergeStopFlag, 1)
218+
} else {
219+
atomic.StoreInt32(&sq.emergencyMergeStopFlag, 0)
220+
}
221+
}
222+
223+
// EmergencyStopHTTP sets the emergency stop flag. It expects the path of
224+
// req.URL to contain either "emergency/stop", "emergency/resume", or "emergency/status".
225+
func (sq *SubmitQueue) EmergencyStopHTTP(res http.ResponseWriter, req *http.Request) {
226+
switch {
227+
case strings.Contains(req.URL.Path, "emergency/stop"):
228+
sq.setEmergencyMergeStop(true)
229+
case strings.Contains(req.URL.Path, "emergency/resume"):
230+
sq.setEmergencyMergeStop(false)
231+
case strings.Contains(req.URL.Path, "emergency/status"):
232+
default:
233+
http.NotFound(res, req)
234+
return
235+
}
236+
sq.serve(sq.marshal(struct{ EmergencyInProgress bool }{sq.emergencyMergeStop()}), res, req)
237+
}
207238

208239
func round(num float64) int {
209240
return int(num + math.Copysign(0.5, num))
@@ -361,6 +392,10 @@ func (sq *SubmitQueue) internalInitialize(config *github.Config, features *featu
361392
go http.ListenAndServe(config.Address, nil)
362393
}
363394

395+
admin.Mux.HandleFunc("/api/emergency/stop", sq.EmergencyStopHTTP)
396+
admin.Mux.HandleFunc("/api/emergency/resume", sq.EmergencyStopHTTP)
397+
admin.Mux.HandleFunc("/api/emergency/status", sq.EmergencyStopHTTP)
398+
364399
if sq.githubE2EPollTime == 0 {
365400
sq.githubE2EPollTime = githubE2EPollTime
366401
}
@@ -440,21 +475,26 @@ func (sq *SubmitQueue) updateHealth() {
440475
}
441476
// Make the current record
442477
stable, _ := sq.e2e.GCSBasedStable()
478+
emergencyStop := sq.emergencyMergeStop()
443479
newEntry := healthRecord{
444480
Time: time.Now(),
445-
Overall: stable,
481+
Overall: stable && !emergencyStop,
446482
Jobs: map[string]bool{},
447483
}
448484
for job, status := range sq.e2e.GetBuildStatus() {
449485
// Ignore flakes.
450486
newEntry.Jobs[job] = status.Status != "Not Stable"
451487
}
488+
if emergencyStop {
489+
// invent an "emergency stop" job that's failing.
490+
newEntry.Jobs["Emergency Stop"] = false
491+
}
452492
sq.healthHistory = append(sq.healthHistory, newEntry)
453493
// Now compute the health structure so we don't have to do it on page load
454494
sq.health.TotalLoops = len(sq.healthHistory)
455495
sq.health.NumStable = 0
456496
sq.health.NumStablePerJob = map[string]int{}
457-
sq.health.MergePossibleNow = stable
497+
sq.health.MergePossibleNow = stable && !emergencyStop
458498
for _, record := range sq.healthHistory {
459499
if record.Overall {
460500
sq.health.NumStable += 1
@@ -475,6 +515,9 @@ func (sq *SubmitQueue) e2eStable(aboutToMerge bool) bool {
475515
wentUnstable := false
476516

477517
stable, ignorableFlakes := sq.e2e.GCSBasedStable()
518+
if stable && sq.emergencyMergeStop() {
519+
stable = false
520+
}
478521

479522
weakStable := sq.e2e.GCSWeakStable()
480523
if !weakStable {

mungegithub/mungers/submit-queue_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,9 @@ func TestSubmitQueue(t *testing.T) {
446446
reason string
447447
state string // what the github status context should be for the PR HEAD
448448

449+
emergencyMergeStop bool
450+
isMerged bool
451+
449452
imHeadSHA string
450453
imBaseSHA string
451454
masterCommit *github.RepositoryCommit
@@ -466,6 +469,25 @@ func TestSubmitQueue(t *testing.T) {
466469
retest2Pass: true,
467470
reason: merged,
468471
state: "success",
472+
isMerged: true,
473+
},
474+
// Entire thing was run and good, but emergency merge stop in progress
475+
{
476+
name: "Test1+emergencyStop",
477+
pr: ValidPR(),
478+
issue: LGTMIssue(),
479+
events: NewLGTMEvents(),
480+
commits: Commits(), // Modified at time.Unix(7), 8, and 9
481+
ciStatus: SuccessStatus(),
482+
lastBuildNumber: LastBuildNumber(),
483+
gcsResult: SuccessGCS(),
484+
weakResults: map[int]utils.FinishedFile{LastBuildNumber(): SuccessGCS()},
485+
retest1Pass: true,
486+
retest2Pass: true,
487+
emergencyMergeStop: true,
488+
isMerged: false,
489+
reason: e2eFailure,
490+
state: "success",
469491
},
470492
// Should pass without running tests because we had a previous run.
471493
// TODO: Add a proper test to make sure we don't shuffle queue when we can just merge a PR
@@ -483,6 +505,7 @@ func TestSubmitQueue(t *testing.T) {
483505
retest2Pass: true,
484506
reason: merged,
485507
state: "success",
508+
isMerged: true,
486509
retestsAvoided: 1,
487510
imHeadSHA: "mysha", // Set by ValidPR
488511
imBaseSHA: "mastersha",
@@ -520,6 +543,7 @@ func TestSubmitQueue(t *testing.T) {
520543
retest2Pass: false,
521544
reason: merged,
522545
state: "success",
546+
isMerged: true,
523547
},
524548
// Fail because PR can't automatically merge
525549
{
@@ -853,6 +877,7 @@ func TestSubmitQueue(t *testing.T) {
853877
config.PendingWaitTime = &d
854878

855879
stateSet := ""
880+
wasMerged := false
856881

857882
numTestChecks := 0
858883
path := "/foo/latest-build.txt"
@@ -963,6 +988,7 @@ func TestSubmitQueue(t *testing.T) {
963988
}
964989
w.Write(data)
965990
test.pr.Merged = boolPtr(true)
991+
wasMerged = true
966992
})
967993
path = fmt.Sprintf("/repos/o/r/statuses/%s", *test.pr.Head.SHA)
968994
mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
@@ -987,6 +1013,7 @@ func TestSubmitQueue(t *testing.T) {
9871013
})
9881014

9891015
sq := getTestSQ(true, config, server)
1016+
sq.setEmergencyMergeStop(test.emergencyMergeStop)
9901017

9911018
obj := github_util.TestObject(config, test.issue, test.pr, test.commits, test.events)
9921019
if test.imBaseSHA != "" && test.imHeadSHA != "" {
@@ -1037,6 +1064,9 @@ func TestSubmitQueue(t *testing.T) {
10371064
if test.state != "" && test.state != stateSet {
10381065
t.Errorf("%d:%q state set to %q but expected %q", testNum, test.name, stateSet, test.state)
10391066
}
1067+
if test.isMerged != wasMerged {
1068+
t.Errorf("%d:%q PR merged = %v but wanted %v", testNum, test.name, wasMerged, test.isMerged)
1069+
}
10401070
if e, a := test.retestsAvoided, int(sq.retestsAvoided); e != a {
10411071
t.Errorf("%d:%q expected %v tests avoided but got %v", testNum, test.name, e, a)
10421072
}

0 commit comments

Comments
 (0)