github.com/zppinho/prow@v0.0.0-20240510014325-1738badeb017/pkg/hook/events.go (about)

     1  /*
     2  Copyright 2016 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package hook
    18  
    19  import (
    20  	"fmt"
    21  	"runtime/debug"
    22  	"strconv"
    23  	"time"
    24  
    25  	"github.com/prometheus/client_golang/prometheus"
    26  	"github.com/sirupsen/logrus"
    27  
    28  	"sigs.k8s.io/prow/pkg/github"
    29  	"sigs.k8s.io/prow/pkg/plugins"
    30  )
    31  
    32  const FailedCommentCoerceFmt = "Could not coerce %s event to a GenericCommentEvent. Unknown 'action': %q."
    33  
    34  const eventTypeField = "event-type"
    35  
    36  var (
    37  	nonCommentIssueActions = map[github.IssueEventAction]bool{
    38  		github.IssueActionAssigned:     true,
    39  		github.IssueActionUnassigned:   true,
    40  		github.IssueActionLabeled:      true,
    41  		github.IssueActionUnlabeled:    true,
    42  		github.IssueActionMilestoned:   true,
    43  		github.IssueActionDemilestoned: true,
    44  		github.IssueActionClosed:       true,
    45  		github.IssueActionReopened:     true,
    46  		github.IssueActionPinned:       true,
    47  		github.IssueActionUnpinned:     true,
    48  		github.IssueActionTransferred:  true,
    49  		github.IssueActionDeleted:      true,
    50  		github.IssueActionLocked:       true,
    51  		github.IssueActionUnlocked:     true,
    52  	}
    53  	nonCommentPullRequestActions = map[github.PullRequestEventAction]bool{
    54  		github.PullRequestActionAssigned:             true,
    55  		github.PullRequestActionUnassigned:           true,
    56  		github.PullRequestActionReviewRequested:      true,
    57  		github.PullRequestActionReviewRequestRemoved: true,
    58  		github.PullRequestActionLabeled:              true,
    59  		github.PullRequestActionUnlabeled:            true,
    60  		github.PullRequestActionClosed:               true,
    61  		github.PullRequestActionReopened:             true,
    62  		github.PullRequestActionSynchronize:          true,
    63  		github.PullRequestActionReadyForReview:       true,
    64  		github.PullRequestActionConvertedToDraft:     true,
    65  		github.PullRequestActionLocked:               true,
    66  		github.PullRequestActionUnlocked:             true,
    67  		github.PullRequestActionAutoMergeEnabled:     true,
    68  		github.PullRequestActionAutoMergeDisabled:    true,
    69  	}
    70  )
    71  
    72  func (s *Server) handleReviewEvent(l *logrus.Entry, re github.ReviewEvent) {
    73  	defer s.wg.Done()
    74  	l = l.WithFields(logrus.Fields{
    75  		github.OrgLogField:  re.Repo.Owner.Login,
    76  		github.RepoLogField: re.Repo.Name,
    77  		github.PrLogField:   re.PullRequest.Number,
    78  		"review":            re.Review.ID,
    79  		"reviewer":          re.Review.User.Login,
    80  		"url":               re.Review.HTMLURL,
    81  	})
    82  	l.Infof("Review %s.", re.Action)
    83  	for p, h := range s.Plugins.ReviewEventHandlers(re.PullRequest.Base.Repo.Owner.Login, re.PullRequest.Base.Repo.Name) {
    84  		s.wg.Add(1)
    85  		go func(p string, h plugins.ReviewEventHandler) {
    86  			defer s.wg.Done()
    87  			agent := plugins.NewAgent(s.ConfigAgent, s.Plugins, s.ClientAgent, re.Repo.Owner.Login, s.Metrics.Metrics, l, p)
    88  			agent.InitializeCommentPruner(
    89  				re.Repo.Owner.Login,
    90  				re.Repo.Name,
    91  				re.PullRequest.Number,
    92  			)
    93  			start := time.Now()
    94  			err := errorOnPanic(func() error { return h(agent, re) })
    95  			labels := prometheus.Labels{"event_type": l.Data[eventTypeField].(string), "action": string(re.Action), "plugin": p, "took_action": strconv.FormatBool(agent.TookAction())}
    96  			if err != nil {
    97  				agent.Logger.WithError(err).Error("Error handling ReviewEvent.")
    98  				s.Metrics.PluginHandleErrors.With(labels).Inc()
    99  			}
   100  			s.Metrics.PluginHandleDuration.With(labels).Observe(time.Since(start).Seconds())
   101  		}(p, h)
   102  	}
   103  	action := github.GeneralizeCommentAction(string(re.Action))
   104  	if action == "" {
   105  		l.Errorf(FailedCommentCoerceFmt, "pull_request_review", string(re.Action))
   106  		return
   107  	}
   108  
   109  	gce, err := github.GeneralizeComment(re)
   110  	if err != nil {
   111  		l.Errorln(err)
   112  		return
   113  	}
   114  
   115  	s.handleGenericComment(l, gce)
   116  }
   117  
   118  func (s *Server) handleReviewCommentEvent(l *logrus.Entry, rce github.ReviewCommentEvent) {
   119  	defer s.wg.Done()
   120  	l = l.WithFields(logrus.Fields{
   121  		github.OrgLogField:  rce.Repo.Owner.Login,
   122  		github.RepoLogField: rce.Repo.Name,
   123  		github.PrLogField:   rce.PullRequest.Number,
   124  		"review":            rce.Comment.ReviewID,
   125  		"commenter":         rce.Comment.User.Login,
   126  		"url":               rce.Comment.HTMLURL,
   127  	})
   128  	l.Infof("Review comment %s.", rce.Action)
   129  	for p, h := range s.Plugins.ReviewCommentEventHandlers(rce.PullRequest.Base.Repo.Owner.Login, rce.PullRequest.Base.Repo.Name) {
   130  		s.wg.Add(1)
   131  		go func(p string, h plugins.ReviewCommentEventHandler) {
   132  			defer s.wg.Done()
   133  			agent := plugins.NewAgent(s.ConfigAgent, s.Plugins, s.ClientAgent, rce.Repo.Owner.Login, s.Metrics.Metrics, l, p)
   134  			agent.InitializeCommentPruner(
   135  				rce.Repo.Owner.Login,
   136  				rce.Repo.Name,
   137  				rce.PullRequest.Number,
   138  			)
   139  			start := time.Now()
   140  			err := errorOnPanic(func() error { return h(agent, rce) })
   141  			labels := prometheus.Labels{"event_type": l.Data[eventTypeField].(string), "action": string(rce.Action), "plugin": p, "took_action": strconv.FormatBool(agent.TookAction())}
   142  			if err != nil {
   143  				agent.Logger.WithError(err).Error("Error handling ReviewCommentEvent.")
   144  				s.Metrics.PluginHandleErrors.With(labels).Inc()
   145  			}
   146  			s.Metrics.PluginHandleDuration.With(labels).Observe(time.Since(start).Seconds())
   147  		}(p, h)
   148  	}
   149  	action := github.GeneralizeCommentAction(string(rce.Action))
   150  	if action == "" {
   151  		l.Errorf(FailedCommentCoerceFmt, "pull_request_review_comment", string(rce.Action))
   152  		return
   153  	}
   154  
   155  	gce, err := github.GeneralizeComment(rce)
   156  	if err != nil {
   157  		l.Errorln(err)
   158  		return
   159  	}
   160  
   161  	s.handleGenericComment(l, gce)
   162  }
   163  
   164  func (s *Server) handlePullRequestEvent(l *logrus.Entry, pr github.PullRequestEvent) {
   165  	defer s.wg.Done()
   166  	l = l.WithFields(logrus.Fields{
   167  		github.OrgLogField:  pr.Repo.Owner.Login,
   168  		github.RepoLogField: pr.Repo.Name,
   169  		github.PrLogField:   pr.Number,
   170  		"author":            pr.PullRequest.User.Login,
   171  		"url":               pr.PullRequest.HTMLURL,
   172  	})
   173  	l.Infof("Pull request %s.", pr.Action)
   174  	for p, h := range s.Plugins.PullRequestHandlers(pr.PullRequest.Base.Repo.Owner.Login, pr.PullRequest.Base.Repo.Name) {
   175  		s.wg.Add(1)
   176  		go func(p string, h plugins.PullRequestHandler) {
   177  			defer s.wg.Done()
   178  			agent := plugins.NewAgent(s.ConfigAgent, s.Plugins, s.ClientAgent, pr.Repo.Owner.Login, s.Metrics.Metrics, l, p)
   179  			agent.InitializeCommentPruner(
   180  				pr.Repo.Owner.Login,
   181  				pr.Repo.Name,
   182  				pr.PullRequest.Number,
   183  			)
   184  			start := time.Now()
   185  			err := errorOnPanic(func() error { return h(agent, pr) })
   186  			labels := prometheus.Labels{"event_type": l.Data[eventTypeField].(string), "action": string(pr.Action), "plugin": p, "took_action": strconv.FormatBool(agent.TookAction())}
   187  			if err != nil {
   188  				agent.Logger.WithError(err).Error("Error handling PullRequestEvent.")
   189  				s.Metrics.PluginHandleErrors.With(labels).Inc()
   190  			}
   191  			s.Metrics.PluginHandleDuration.With(labels).Observe(time.Since(start).Seconds())
   192  		}(p, h)
   193  	}
   194  	action := github.GeneralizeCommentAction(string(pr.Action))
   195  	if action == "" {
   196  		if !nonCommentPullRequestActions[pr.Action] {
   197  			l.Infof(FailedCommentCoerceFmt, "pull_request", string(pr.Action))
   198  		}
   199  		return
   200  	}
   201  
   202  	gce, err := github.GeneralizeComment(pr)
   203  	if err != nil {
   204  		l.Errorln(err)
   205  		return
   206  	}
   207  
   208  	s.handleGenericComment(l, gce)
   209  }
   210  
   211  func (s *Server) handlePushEvent(l *logrus.Entry, pe github.PushEvent) {
   212  	defer s.wg.Done()
   213  	l = l.WithFields(logrus.Fields{
   214  		github.OrgLogField:  pe.Repo.Owner.Name,
   215  		github.RepoLogField: pe.Repo.Name,
   216  		"ref":               pe.Ref,
   217  		"head":              pe.After,
   218  	})
   219  	l.Info("Push event.")
   220  	for p, h := range s.Plugins.PushEventHandlers(pe.Repo.Owner.Name, pe.Repo.Name) {
   221  		s.wg.Add(1)
   222  		go func(p string, h plugins.PushEventHandler) {
   223  			defer s.wg.Done()
   224  			agent := plugins.NewAgent(s.ConfigAgent, s.Plugins, s.ClientAgent, pe.Repo.Owner.Login, s.Metrics.Metrics, l, p)
   225  			start := time.Now()
   226  			err := errorOnPanic(func() error { return h(agent, pe) })
   227  			labels := prometheus.Labels{"event_type": l.Data[eventTypeField].(string), "action": "none", "plugin": p, "took_action": strconv.FormatBool(agent.TookAction())}
   228  			if err != nil {
   229  				agent.Logger.WithError(err).Error("Error handling PushEvent.")
   230  				s.Metrics.PluginHandleErrors.With(labels).Inc()
   231  			}
   232  			s.Metrics.PluginHandleDuration.With(labels).Observe(time.Since(start).Seconds())
   233  		}(p, h)
   234  	}
   235  }
   236  
   237  func (s *Server) handleIssueEvent(l *logrus.Entry, i github.IssueEvent) {
   238  	defer s.wg.Done()
   239  	l = l.WithFields(logrus.Fields{
   240  		github.OrgLogField:  i.Repo.Owner.Login,
   241  		github.RepoLogField: i.Repo.Name,
   242  		github.PrLogField:   i.Issue.Number,
   243  		"author":            i.Issue.User.Login,
   244  		"url":               i.Issue.HTMLURL,
   245  	})
   246  	l.Infof("Issue %s.", i.Action)
   247  	for p, h := range s.Plugins.IssueHandlers(i.Repo.Owner.Login, i.Repo.Name) {
   248  		s.wg.Add(1)
   249  		go func(p string, h plugins.IssueHandler) {
   250  			defer s.wg.Done()
   251  			agent := plugins.NewAgent(s.ConfigAgent, s.Plugins, s.ClientAgent, i.Repo.Owner.Login, s.Metrics.Metrics, l, p)
   252  			agent.InitializeCommentPruner(
   253  				i.Repo.Owner.Login,
   254  				i.Repo.Name,
   255  				i.Issue.Number,
   256  			)
   257  			start := time.Now()
   258  			err := errorOnPanic(func() error { return h(agent, i) })
   259  			labels := prometheus.Labels{"event_type": l.Data[eventTypeField].(string), "action": string(i.Action), "plugin": p, "took_action": strconv.FormatBool(agent.TookAction())}
   260  			if err != nil {
   261  				agent.Logger.WithError(err).Error("Error handling IssueEvent.")
   262  				s.Metrics.PluginHandleErrors.With(labels).Inc()
   263  			}
   264  			s.Metrics.PluginHandleDuration.With(labels).Observe(time.Since(start).Seconds())
   265  		}(p, h)
   266  	}
   267  	action := github.GeneralizeCommentAction(string(i.Action))
   268  	if action == "" {
   269  		if !nonCommentIssueActions[i.Action] {
   270  			l.Errorf(FailedCommentCoerceFmt, "issues", string(i.Action))
   271  		}
   272  		return
   273  	}
   274  
   275  	gce, err := github.GeneralizeComment(i)
   276  	if err != nil {
   277  		l.Errorln(err)
   278  		return
   279  	}
   280  
   281  	s.handleGenericComment(l, gce)
   282  }
   283  
   284  func (s *Server) handleIssueCommentEvent(l *logrus.Entry, ic github.IssueCommentEvent) {
   285  	defer s.wg.Done()
   286  	l = l.WithFields(logrus.Fields{
   287  		github.OrgLogField:  ic.Repo.Owner.Login,
   288  		github.RepoLogField: ic.Repo.Name,
   289  		github.PrLogField:   ic.Issue.Number,
   290  		"author":            ic.Comment.User.Login,
   291  		"url":               ic.Comment.HTMLURL,
   292  	})
   293  	l.Infof("Issue comment %s.", ic.Action)
   294  	for p, h := range s.Plugins.IssueCommentHandlers(ic.Repo.Owner.Login, ic.Repo.Name) {
   295  		s.wg.Add(1)
   296  		go func(p string, h plugins.IssueCommentHandler) {
   297  			defer s.wg.Done()
   298  			agent := plugins.NewAgent(s.ConfigAgent, s.Plugins, s.ClientAgent, ic.Repo.Owner.Login, s.Metrics.Metrics, l, p)
   299  			agent.InitializeCommentPruner(
   300  				ic.Repo.Owner.Login,
   301  				ic.Repo.Name,
   302  				ic.Issue.Number,
   303  			)
   304  			start := time.Now()
   305  			err := errorOnPanic(func() error { return h(agent, ic) })
   306  			labels := prometheus.Labels{"event_type": l.Data[eventTypeField].(string), "action": string(ic.Action), "plugin": p, "took_action": strconv.FormatBool(agent.TookAction())}
   307  			if err != nil {
   308  				agent.Logger.WithError(err).Error("Error handling IssueCommentEvent.")
   309  				s.Metrics.PluginHandleErrors.With(labels).Inc()
   310  			}
   311  			s.Metrics.PluginHandleDuration.With(labels).Observe(time.Since(start).Seconds())
   312  		}(p, h)
   313  	}
   314  	action := github.GeneralizeCommentAction(string(ic.Action))
   315  	if action == "" {
   316  		l.Errorf(FailedCommentCoerceFmt, "issue_comment", string(ic.Action))
   317  		return
   318  	}
   319  
   320  	gce, err := github.GeneralizeComment(ic)
   321  	if err != nil {
   322  		l.Errorln(err)
   323  		return
   324  	}
   325  
   326  	s.handleGenericComment(l, gce)
   327  }
   328  
   329  func (s *Server) handleStatusEvent(l *logrus.Entry, se github.StatusEvent) {
   330  	defer s.wg.Done()
   331  	l = l.WithFields(logrus.Fields{
   332  		github.OrgLogField:  se.Repo.Owner.Login,
   333  		github.RepoLogField: se.Repo.Name,
   334  		"context":           se.Context,
   335  		"sha":               se.SHA,
   336  		"state":             se.State,
   337  		"id":                se.ID,
   338  	})
   339  	l.Infof("Status description %s.", se.Description)
   340  	for p, h := range s.Plugins.StatusEventHandlers(se.Repo.Owner.Login, se.Repo.Name) {
   341  		s.wg.Add(1)
   342  		go func(p string, h plugins.StatusEventHandler) {
   343  			defer s.wg.Done()
   344  			agent := plugins.NewAgent(s.ConfigAgent, s.Plugins, s.ClientAgent, se.Repo.Owner.Login, s.Metrics.Metrics, l, p)
   345  			start := time.Now()
   346  			err := errorOnPanic(func() error { return h(agent, se) })
   347  			labels := prometheus.Labels{"event_type": l.Data[eventTypeField].(string), "action": "none", "plugin": p, "took_action": strconv.FormatBool(agent.TookAction())}
   348  			if err != nil {
   349  				agent.Logger.WithError(err).Error("Error handling StatusEvent.")
   350  				s.Metrics.PluginHandleErrors.With(labels).Inc()
   351  			}
   352  			s.Metrics.PluginHandleDuration.With(labels).Observe(time.Since(start).Seconds())
   353  		}(p, h)
   354  	}
   355  }
   356  
   357  func (s *Server) handleGenericComment(l *logrus.Entry, ce *github.GenericCommentEvent) {
   358  	for p, h := range s.Plugins.GenericCommentHandlers(ce.Repo.Owner.Login, ce.Repo.Name) {
   359  		s.wg.Add(1)
   360  		go func(p string, h plugins.GenericCommentHandler) {
   361  			defer s.wg.Done()
   362  			agent := plugins.NewAgent(s.ConfigAgent, s.Plugins, s.ClientAgent, ce.Repo.Owner.Login, s.Metrics.Metrics, l, p)
   363  			agent.InitializeCommentPruner(
   364  				ce.Repo.Owner.Login,
   365  				ce.Repo.Name,
   366  				ce.Number,
   367  			)
   368  			start := time.Now()
   369  			err := errorOnPanic(func() error { return h(agent, *ce) })
   370  			labels := prometheus.Labels{"event_type": l.Data[eventTypeField].(string), "action": string(ce.Action), "plugin": p, "took_action": strconv.FormatBool(agent.TookAction())}
   371  			if err != nil {
   372  				agent.Logger.WithError(err).Error("Error handling GenericCommentEvent.")
   373  				s.Metrics.PluginHandleErrors.With(labels).Inc()
   374  			}
   375  			s.Metrics.PluginHandleDuration.With(labels).Observe(time.Since(start).Seconds())
   376  		}(p, h)
   377  	}
   378  }
   379  
   380  func errorOnPanic(f func() error) (err error) {
   381  	defer func() {
   382  		if r := recover(); r != nil {
   383  			err = fmt.Errorf("panic caught: %v. stack is: %s", r, debug.Stack())
   384  		}
   385  	}()
   386  	return f()
   387  }