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

Commit 497a95a

Browse files
committed
Add emergency stop button
1 parent 61f4581 commit 497a95a

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"
@@ -175,6 +176,8 @@ type SubmitQueue struct {
175176
health submitQueueHealth
176177
healthHistory []healthRecord
177178

179+
emergencyMergeStopFlag int32
180+
178181
adminPort int
179182
}
180183

@@ -194,10 +197,38 @@ func init() {
194197
}
195198

196199
// Name is the name usable in --pr-mungers
197-
func (sq SubmitQueue) Name() string { return "submit-queue" }
200+
func (sq *SubmitQueue) Name() string { return "submit-queue" }
198201

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

202233
func round(num float64) int {
203234
return int(num + math.Copysign(0.5, num))
@@ -353,6 +384,10 @@ func (sq *SubmitQueue) internalInitialize(config *github.Config, features *featu
353384
go http.ListenAndServe(config.Address, nil)
354385
}
355386

387+
admin.Mux.HandleFunc("/api/emergency/stop", sq.EmergencyStopHTTP)
388+
admin.Mux.HandleFunc("/api/emergency/resume", sq.EmergencyStopHTTP)
389+
admin.Mux.HandleFunc("/api/emergency/status", sq.EmergencyStopHTTP)
390+
356391
if sq.githubE2EPollTime == 0 {
357392
sq.githubE2EPollTime = githubE2EPollTime
358393
}
@@ -431,21 +466,26 @@ func (sq *SubmitQueue) updateHealth() {
431466
}
432467
// Make the current record
433468
stable, _ := sq.e2e.GCSBasedStable()
469+
emergencyStop := sq.emergencyMergeStop()
434470
newEntry := healthRecord{
435471
Time: time.Now(),
436-
Overall: stable,
472+
Overall: stable && !emergencyStop,
437473
Jobs: map[string]bool{},
438474
}
439475
for job, status := range sq.e2e.GetBuildStatus() {
440476
// Ignore flakes.
441477
newEntry.Jobs[job] = status.Status != "Not Stable"
442478
}
479+
if emergencyStop {
480+
// invent an "emergency stop" job that's failing.
481+
newEntry.Jobs["Emergency Stop"] = false
482+
}
443483
sq.healthHistory = append(sq.healthHistory, newEntry)
444484
// Now compute the health structure so we don't have to do it on page load
445485
sq.health.TotalLoops = len(sq.healthHistory)
446486
sq.health.NumStable = 0
447487
sq.health.NumStablePerJob = map[string]int{}
448-
sq.health.MergePossibleNow = stable
488+
sq.health.MergePossibleNow = stable && !emergencyStop
449489
for _, record := range sq.healthHistory {
450490
if record.Overall {
451491
sq.health.NumStable += 1
@@ -466,6 +506,9 @@ func (sq *SubmitQueue) e2eStable(aboutToMerge bool) bool {
466506
wentUnstable := false
467507

468508
stable, ignorableFlakes := sq.e2e.GCSBasedStable()
509+
if stable && sq.emergencyMergeStop() {
510+
stable = false
511+
}
469512

470513
weakStable := sq.e2e.GCSWeakStable()
471514
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)