forked from kubernetes/kubernetes
/
submit-queue.go
162 lines (146 loc) · 5.99 KB
/
submit-queue.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
160
161
162
/*
Copyright 2015 The Kubernetes Authors All rights reserved.
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 main
// A simple binary for merging PR that match a criteria
// Usage:
// submit-queue -token=<github-access-token> -user-whitelist=<file> --jenkins-host=http://some.host [-min-pr-number=<number>] [-dry-run] [-once]
//
// Details:
/*
Usage of ./submit-queue:
-alsologtostderr=false: log to standard error as well as files
-dry-run=false: If true, don't actually merge anything
-jenkins-job="kubernetes-e2e-gce,kubernetes-e2e-gke-ci,kubernetes-build": Comma separated list of jobs in Jenkins to use for stability testing
-log_backtrace_at=:0: when logging hits line file:N, emit a stack trace
-log_dir="": If non-empty, write log files in this directory
-logtostderr=false: log to standard error instead of files
-min-pr-number=0: The minimum PR to start with [default: 0]
-once=false: If true, only merge one PR, don't run forever
-stderrthreshold=0: logs at or above this threshold go to stderr
-token="": The OAuth Token to use for requests.
-user-whitelist="": Path to a whitelist file that contains users to auto-merge. Required.
-v=0: log level for V logs
-vmodule=: comma-separated list of pattern=N settings for file-filtered logging
*/
import (
"bufio"
"errors"
"flag"
"os"
"strings"
"k8s.io/kubernetes/contrib/submit-queue/github"
"k8s.io/kubernetes/contrib/submit-queue/jenkins"
"github.com/golang/glog"
github_api "github.com/google/go-github/github"
)
var (
token = flag.String("token", "", "The OAuth Token to use for requests.")
minPRNumber = flag.Int("min-pr-number", 0, "The minimum PR to start with [default: 0]")
dryrun = flag.Bool("dry-run", false, "If true, don't actually merge anything")
oneOff = flag.Bool("once", false, "If true, only merge one PR, don't run forever")
jobs = flag.String("jenkins-jobs", "kubernetes-e2e-gce,kubernetes-e2e-gke-ci,kubernetes-build", "Comma separated list of jobs in Jenkins to use for stability testing")
jenkinsHost = flag.String("jenkins-host", "", "The URL for the jenkins job to watch")
userWhitelist = flag.String("user-whitelist", "", "Path to a whitelist file that contains users to auto-merge. Required.")
requiredContexts = flag.String("required-contexts", "cla/google,Shippable,continuous-integration/travis-ci/pr,Jenkins GCE e2e", "Comma separate list of status contexts required for a PR to be considered ok to merge")
whitelistOverride = flag.String("whitelist-override-label", "ok-to-merge", "Github label, if present on a PR it will be merged even if the author isn't in the whitelist")
)
const (
org = "GoogleCloudPlatform"
project = "kubernetes"
)
// This is called on a potentially mergeable PR
func runE2ETests(client *github_api.Client, pr *github_api.PullRequest, issue *github_api.Issue) error {
// Test if the build is stable in Jenkins
jenkinsClient := &jenkins.JenkinsClient{Host: *jenkinsHost}
builds := strings.Split(*jobs, ",")
for _, build := range builds {
stable, err := jenkinsClient.IsBuildStable(build)
glog.V(2).Infof("Checking build stability for %s", build)
if err != nil {
return err
}
if !stable {
glog.Errorf("Build %s isn't stable, skipping!", build)
return errors.New("Unstable build")
}
}
glog.V(2).Infof("Build is stable.")
// Ask for a fresh build
glog.V(4).Infof("Asking PR builder to build %d", *pr.Number)
body := "@k8s-bot test this [testing build queue, sorry for the noise]"
if _, _, err := client.Issues.CreateComment(org, project, *pr.Number, &github_api.IssueComment{Body: &body}); err != nil {
return err
}
// Wait for the build to start
err := github.WaitForPending(client, org, project, *pr.Number)
// Wait for the status to go back to 'success'
ok, err := github.ValidateStatus(client, org, project, *pr.Number, []string{}, true)
if err != nil {
return err
}
if !ok {
glog.Infof("Status after build is not 'success', skipping PR %d", *pr.Number)
return nil
}
if !*dryrun {
glog.Infof("Merging PR: %d", *pr.Number)
mergeBody := "Automatic merge from SubmitQueue"
if _, _, err := client.Issues.CreateComment(org, project, *pr.Number, &github_api.IssueComment{Body: &mergeBody}); err != nil {
glog.Warningf("Failed to create merge comment: %v", err)
return err
}
_, _, err := client.PullRequests.Merge(org, project, *pr.Number, "Auto commit by PR queue bot")
return err
}
glog.Infof("Skipping actual merge because --dry-run is set")
return nil
}
func loadWhitelist(file string) ([]string, error) {
fp, err := os.Open(file)
if err != nil {
return nil, err
}
defer fp.Close()
scanner := bufio.NewScanner(fp)
result := []string{}
for scanner.Scan() {
result = append(result, scanner.Text())
}
return result, scanner.Err()
}
func main() {
flag.Parse()
if len(*userWhitelist) == 0 {
glog.Fatalf("--user-whitelist is required.")
}
if len(*jenkinsHost) == 0 {
glog.Fatalf("--jenkins-host is required.")
}
client := github.MakeClient(*token)
users, err := loadWhitelist(*userWhitelist)
if err != nil {
glog.Fatalf("error loading user whitelist: %v", err)
}
requiredContexts := strings.Split(*requiredContexts, ",")
config := &github.FilterConfig{
MinPRNumber: *minPRNumber,
UserWhitelist: users,
RequiredStatusContexts: requiredContexts,
WhitelistOverride: *whitelistOverride,
}
for !*oneOff {
if err := github.ForEachCandidatePRDo(client, org, project, runE2ETests, *oneOff, config); err != nil {
glog.Fatalf("Error getting candidate PRs: %v", err)
}
}
}