Skip to content

Commit

Permalink
Add /test logic to rerun button for presubmits
Browse files Browse the repository at this point in the history
  • Loading branch information
mirandachrist committed Aug 7, 2019
1 parent 012cf5f commit a0da7b0
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 31 deletions.
4 changes: 3 additions & 1 deletion prow/cmd/deck/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ go_test(
"//prow/client/clientset/versioned/fake:go_default_library",
"//prow/config:go_default_library",
"//prow/flagutil:go_default_library",
"//prow/github:go_default_library",
"//prow/github/fakegithub:go_default_library",
"//prow/githuboauth:go_default_library",
"//prow/pluginhelp:go_default_library",
"//prow/spyglass/lenses/buildlog:go_default_library",
Expand Down Expand Up @@ -118,6 +118,8 @@ go_library(
"//prow/metrics:go_default_library",
"//prow/pjutil:go_default_library",
"//prow/pluginhelp:go_default_library",
"//prow/plugins:go_default_library",
"//prow/plugins/trigger:go_default_library",
"//prow/pod-utils/downwardapi:go_default_library",
"//prow/pod-utils/gcs:go_default_library",
"//prow/prstatus:go_default_library",
Expand Down
40 changes: 34 additions & 6 deletions prow/cmd/deck/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import (
"fmt"
"html/template"
"io/ioutil"
"k8s.io/test-infra/prow/plugins"
"k8s.io/test-infra/prow/plugins/trigger"
"net/http"
"net/url"
"os"
Expand Down Expand Up @@ -99,6 +101,7 @@ type options struct {
rerunCreatesJob bool
allowInsecure bool
dryRun bool
pluginConfig string
}

func (o *options) Validate() error {
Expand Down Expand Up @@ -162,6 +165,7 @@ func gatherOptions(fs *flag.FlagSet, args ...string) options {
fs.BoolVar(&o.rerunCreatesJob, "rerun-creates-job", false, "Change the re-run option in Deck to actually create the job. **WARNING:** Only use this with non-public deck instances, otherwise strangers can DOS your Prow instance")
fs.BoolVar(&o.allowInsecure, "allow-insecure", false, "Allows insecure requests for CSRF and GitHub oauth.")
fs.BoolVar(&o.dryRun, "dry-run", false, "Whether or not to make mutating API calls to GitHub.")
fs.StringVar(&o.pluginConfig, "plugin-config", "", "Path to plugin config file, probably /etc/plugins/plugins.yaml")
o.kubernetes.AddFlags(fs)
o.github.AddFlagsWithoutDefaultGitHubTokenPath(fs)
fs.Parse(args)
Expand Down Expand Up @@ -499,8 +503,9 @@ func prodOnlyMain(cfg config.Getter, o options, mux *http.ServeMux) *http.ServeM
mux.Handle("/tide-history.js", gziphandler.GzipHandler(handleTideHistory(ta)))
}

var pcfg *plugins.Configuration
// We use the GH client to resolve GH teams when determining who is permitted to rerun a job.
var githubClient prowgithub.Client
var githubClient prowgithub.RerunClient
// Enable Git OAuth feature if oauthURL is provided.
var goa *githuboauth.Agent
if o.oauthURL != "" {
Expand Down Expand Up @@ -551,6 +556,13 @@ func prodOnlyMain(cfg config.Getter, o options, mux *http.ServeMux) *http.ServeM
if err != nil {
logrus.WithError(err).Fatal("Error getting GitHub client.")
}
if o.pluginConfig != "" {
pluginAgent := plugins.ConfigAgent{}
if err := pluginAgent.Load(o.pluginConfig); err != nil {
logrus.WithError(err).Fatal("Error loading Prow plugin config.")
}
pcfg = pluginAgent.Config()
}
}

repoSet := make(map[string]bool)
Expand Down Expand Up @@ -584,7 +596,7 @@ func prodOnlyMain(cfg config.Getter, o options, mux *http.ServeMux) *http.ServeM
mux.Handle("/github-login/redirect", goa.HandleRedirect(oauthClient, githuboauth.NewGitHubClientGetter(), secure))
}

mux.Handle("/rerun", gziphandler.GzipHandler(handleRerun(prowJobClient, o.rerunCreatesJob, cfgGetter, goa, githuboauth.NewGitHubClientGetter(), githubClient)))
mux.Handle("/rerun", gziphandler.GzipHandler(handleRerun(prowJobClient, o.rerunCreatesJob, cfgGetter, goa, githuboauth.NewGitHubClientGetter(), githubClient, pcfg)))

// optionally inject http->https redirect handler when behind loadbalancer
if o.redirectHTTPTo != "" {
Expand Down Expand Up @@ -1256,7 +1268,7 @@ func handleProwJob(prowJobClient prowv1.ProwJobInterface) http.HandlerFunc {
}

// canTriggerJob determines whether the given user can trigger any job.
func canTriggerJob(user string, pj prowapi.ProwJob, cfg *prowapi.RerunAuthConfig, cli prowgithub.RerunClient) (bool, error) {
func canTriggerJob(user string, pj prowapi.ProwJob, cfg *prowapi.RerunAuthConfig, cli prowgithub.RerunClient, pcfg *plugins.Configuration) (bool, error) {
auth, err := cfg.IsAuthorized(user, cli)
if auth {
return true, nil
Expand All @@ -1274,8 +1286,24 @@ func canTriggerJob(user string, pj prowapi.ProwJob, cfg *prowapi.RerunAuthConfig
}
if cli == nil {
logrus.Warning("No GitHub token was provided, so we cannot retrieve GitHub teams")
return false, nil
}

// If the job is a presubmit and has an associated PR, do the same checks as for /test
if pj.Spec.Type != prowapi.PresubmitJob || pj.Spec.Refs == nil || len(pj.Spec.Refs.Pulls) == 0 {
return false, nil
}
pull := pj.Spec.Refs.Pulls[0]
org := pj.Spec.Refs.Org
repo := pj.Spec.Refs.Repo
if pcfg == nil {
// If no trigger is provided, create one with the strictest relevant permissions
defaultTrigger := plugins.Trigger{OnlyOrgMembers: true}
_, allowed, err := trigger.TrustedPullRequest(cli, defaultTrigger, pull.Author, org, repo, pull.Number, nil)
return allowed, err
}
return false, nil
_, allowed, err := trigger.TrustedPullRequest(cli, pcfg.TriggerFor(org, repo), pull.Author, org, repo, pull.Number, nil)
return allowed, err
}

func marshalJob(w http.ResponseWriter, pj prowapi.ProwJob, l *logrus.Entry) {
Expand All @@ -1294,7 +1322,7 @@ func marshalJob(w http.ResponseWriter, pj prowapi.ProwJob, l *logrus.Entry) {
// handleRerun triggers a rerun of the given job if that features is enabled, it receives a
// POST request, and the user has the necessary permissions. Otherwise, it writes the config
// for a new job but does not trigger it.
func handleRerun(prowJobClient prowv1.ProwJobInterface, createProwJob bool, cfg authCfgGetter, goa *githuboauth.Agent, ghc githuboauth.GitHubClientGetter, cli prowgithub.RerunClient) http.HandlerFunc {
func handleRerun(prowJobClient prowv1.ProwJobInterface, createProwJob bool, cfg authCfgGetter, goa *githuboauth.Agent, ghc githuboauth.GitHubClientGetter, cli prowgithub.RerunClient, pcfg *plugins.Configuration) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
name := r.URL.Query().Get("prowjob")
l := logrus.WithField("prowjob", name)
Expand Down Expand Up @@ -1339,7 +1367,7 @@ func handleRerun(prowJobClient prowv1.ProwJobInterface, createProwJob bool, cfg
http.Error(w, "Error retrieving GitHub login", http.StatusUnauthorized)
return
}
allowed, err = canTriggerJob(login, newPJ, authConfig, cli)
allowed, err = canTriggerJob(login, newPJ, authConfig, cli, pcfg)
if err != nil {
http.Error(w, fmt.Sprintf("Error checking if user can trigger job: %v", err), http.StatusInternalServerError)
l.WithError(err).Errorf("Error checking if user can trigger job")
Expand Down
44 changes: 20 additions & 24 deletions prow/cmd/deck/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"golang.org/x/oauth2"
"io"
"io/ioutil"
"k8s.io/test-infra/prow/github/fakegithub"
"k8s.io/test-infra/prow/githuboauth"
"net/http"
"net/http/httptest"
Expand All @@ -48,7 +49,6 @@ import (
"k8s.io/test-infra/prow/client/clientset/versioned/fake"
"k8s.io/test-infra/prow/config"
"k8s.io/test-infra/prow/flagutil"
prowgithub "k8s.io/test-infra/prow/github"
"k8s.io/test-infra/prow/pluginhelp"
_ "k8s.io/test-infra/prow/spyglass/lenses/buildlog"
_ "k8s.io/test-infra/prow/spyglass/lenses/junit"
Expand Down Expand Up @@ -272,23 +272,6 @@ func (getter mockGitHubConfigGetter) GetUser(login string) (*github.User, error)
return &github.User{Login: &getter.githubLogin}, nil
}

type mockRerunClient struct {
members []string
}

func (cli mockRerunClient) TeamHasMember(teamID int, login string) (bool, error) {
for _, member := range cli.members {
if login == member {
return true, nil
}
}
return false, nil
}

func (cli mockRerunClient) GetTeamBySlug(slug string, org string) (*prowgithub.Team, error) {
return nil, nil
}

// TestRerun just checks that the result can be unmarshaled properly, has an
// updated status, and has equal spec.
func TestRerun(t *testing.T) {
Expand Down Expand Up @@ -363,8 +346,18 @@ func TestRerun(t *testing.T) {
httpMethod: http.MethodPost,
},
{
name: "User on permitted GitHub team",
login: "teammember",
name: "User on permitted team",
login: "sig-lead",
authorized: []string{},
allowAnyone: false,
rerunCreatesJob: true,
shouldCreateProwJob: true,
httpCode: http.StatusOK,
httpMethod: http.MethodPost,
},
{
name: "Org member permitted for presubmits",
login: "org-member",
authorized: []string{},
allowAnyone: false,
rerunCreatesJob: true,
Expand All @@ -388,13 +381,16 @@ func TestRerun(t *testing.T) {
Org: "org",
Repo: "repo",
Pulls: []prowapi.Pull{
{Number: 1},
{
Number: 1,
Author: tc.login,
},
},
},
RerunAuthConfig: prowapi.RerunAuthConfig{
AllowAnyone: false,
GitHubUsers: []string{"authorized", "alsoauthorized"},
GitHubTeamIDs: []int{1},
GitHubTeamIDs: []int{42},
},
},
Status: prowapi.ProwJobStatus{
Expand Down Expand Up @@ -432,8 +428,8 @@ func TestRerun(t *testing.T) {
}
goa := githuboauth.NewAgent(mockConfig, &logrus.Entry{})
ghc := mockGitHubConfigGetter{githubLogin: tc.login}
rc := mockRerunClient{members: []string{"teammember"}}
handler := handleRerun(fakeProwJobClient.ProwV1().ProwJobs("prowjobs"), tc.rerunCreatesJob, configGetter, goa, ghc, rc)
rc := &fakegithub.FakeClient{OrgMembers: map[string][]string{"org": {"org-member"}}}
handler := handleRerun(fakeProwJobClient.ProwV1().ProwJobs("prowjobs"), tc.rerunCreatesJob, configGetter, goa, ghc, rc, nil)
handler.ServeHTTP(rr, req)
if rr.Code != tc.httpCode {
t.Fatalf("Bad error code: %d", rr.Code)
Expand Down
14 changes: 14 additions & 0 deletions prow/github/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,20 @@ type MilestoneClient interface {
type RerunClient interface {
TeamHasMember(teamID int, memberLogin string) (bool, error)
GetTeamBySlug(slug string, org string) (*Team, error)
AddLabel(org, repo string, number int, label string) error
BotName() (string, error)
IsCollaborator(org, repo, user string) (bool, error)
IsMember(org, user string) (bool, error)
GetPullRequest(org, repo string, number int) (*PullRequest, error)
GetRef(org, repo, ref string) (string, error)
CreateComment(owner, repo string, number int, comment string) error
ListIssueComments(owner, repo string, issue int) ([]IssueComment, error)
CreateStatus(owner, repo, ref string, status Status) error
GetCombinedStatus(org, repo, ref string) (*CombinedStatus, error)
GetPullRequestChanges(org, repo string, number int) ([]PullRequestChange, error)
RemoveLabel(org, repo string, number int, label string) error
DeleteStaleComments(org, repo string, number int, comments []IssueComment, isStale func(IssueComment) bool) error
GetIssueLabels(org, repo string, number int) ([]Label, error)
}

// Client interface for GitHub API
Expand Down
10 changes: 10 additions & 0 deletions prow/github/fakegithub/fakegithub.go
Original file line number Diff line number Diff line change
Expand Up @@ -636,3 +636,13 @@ func (f *FakeClient) TeamHasMember(teamID int, memberLogin string) (bool, error)
}
return false, nil
}

func (f *FakeClient) GetTeamBySlug(slug string, org string) (*github.Team, error) {
teams, _ := f.ListTeams(org)
for _, team := range teams {
if team.Name == slug {
return &team, nil
}
}
return &github.Team{}, nil
}
1 change: 1 addition & 0 deletions prow/plugins/trigger/pull-request.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ I understand the commands that are listed [here](https://go.k8s.io/bot-commands?

// TrustedPullRequest returns whether or not the given PR should be tested.
// It first checks if the author is in the org, then looks for "ok-to-test" label.
// If already known, GitHub labels should be provided to save tokens. Otherwise, it fetches them.
func TrustedPullRequest(ghc githubClient, trigger plugins.Trigger, author, org, repo string, num int, l []github.Label) ([]github.Label, bool, error) {
// First check if the author is a member of the org.
if orgMember, err := TrustedUser(ghc, trigger.OnlyOrgMembers, trigger.TrustedOrg, author, org, repo); err != nil {
Expand Down

0 comments on commit a0da7b0

Please sign in to comment.