github.com/munnerz/test-infra@v0.0.0-20190108210205-ce3d181dc989/prow/gerrit/reporter/reporter.go (about)

     1  /*
     2  Copyright 2018 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 reporter implements a reporter interface for gerrit
    18  package reporter
    19  
    20  import (
    21  	"fmt"
    22  
    23  	"github.com/sirupsen/logrus"
    24  	"k8s.io/apimachinery/pkg/labels"
    25  
    26  	"k8s.io/test-infra/prow/apis/prowjobs/v1"
    27  	pjlister "k8s.io/test-infra/prow/client/listers/prowjobs/v1"
    28  	"k8s.io/test-infra/prow/gerrit/client"
    29  	"k8s.io/test-infra/prow/kube"
    30  )
    31  
    32  type gerritClient interface {
    33  	SetReview(instance, id, revision, message string, labels map[string]string) error
    34  }
    35  
    36  // Client is a gerrit reporter client
    37  type Client struct {
    38  	gc     gerritClient
    39  	lister pjlister.ProwJobLister
    40  }
    41  
    42  // NewReporter returns a reporter client
    43  func NewReporter(cookiefilePath string, projects map[string][]string, lister pjlister.ProwJobLister) (*Client, error) {
    44  	gc, err := client.NewClient(projects)
    45  	if err != nil {
    46  		return nil, err
    47  	}
    48  	gc.Start(cookiefilePath)
    49  	return &Client{
    50  		gc:     gc,
    51  		lister: lister,
    52  	}, nil
    53  }
    54  
    55  // GetName returns the name of the reporter
    56  func (c *Client) GetName() string {
    57  	return "gerrit-reporter"
    58  }
    59  
    60  // ShouldReport returns if this prowjob should be reported by the gerrit reporter
    61  func (c *Client) ShouldReport(pj *v1.ProwJob) bool {
    62  
    63  	if pj.Status.State == v1.TriggeredState || pj.Status.State == v1.PendingState {
    64  		// not done yet
    65  		logrus.WithField("prowjob", pj.ObjectMeta.Name).Info("PJ not finished")
    66  		return false
    67  	}
    68  
    69  	// has gerrit metadata (scheduled by gerrit adapter)
    70  	if pj.ObjectMeta.Annotations[client.GerritID] == "" ||
    71  		pj.ObjectMeta.Annotations[client.GerritInstance] == "" ||
    72  		pj.ObjectMeta.Labels[client.GerritRevision] == "" {
    73  		logrus.WithField("prowjob", pj.ObjectMeta.Name).Info("Not a gerrit job")
    74  		return false
    75  	}
    76  
    77  	// Only report when all jobs of the same type on the same revision finished
    78  	selector := labels.Set{
    79  		client.GerritRevision: pj.ObjectMeta.Labels[client.GerritRevision],
    80  		kube.ProwJobTypeLabel: pj.ObjectMeta.Labels[kube.ProwJobTypeLabel],
    81  	}
    82  	pjs, err := c.lister.List(selector.AsSelector())
    83  	if err != nil {
    84  		logrus.WithError(err).Errorf("Cannot list prowjob with selector %v", selector)
    85  		return false
    86  	}
    87  
    88  	for _, pj := range pjs {
    89  		if pj.Status.State == v1.TriggeredState || pj.Status.State == v1.PendingState {
    90  			// other jobs are still running on this revision, skip report
    91  			logrus.WithField("prowjob", pj.ObjectMeta.Name).Info("Other jobs are still running on this revision")
    92  			return false
    93  		}
    94  	}
    95  
    96  	return true
    97  }
    98  
    99  // Report will send the current prowjob status as a gerrit review
   100  func (c *Client) Report(pj *v1.ProwJob) error {
   101  	// If you are hitting here, which means the entire patchset has been finished :-)
   102  
   103  	clientGerritRevision := client.GerritRevision
   104  	clientGerritID := client.GerritID
   105  	clientGerritInstance := client.GerritInstance
   106  	pjTypeLabel := kube.ProwJobTypeLabel
   107  
   108  	// list all prowjobs in the patchset matching pj's type (pre- or post-submit)
   109  	selector := labels.Set{
   110  		clientGerritRevision: pj.ObjectMeta.Labels[clientGerritRevision],
   111  		pjTypeLabel:          pj.ObjectMeta.Labels[pjTypeLabel],
   112  	}
   113  	pjsOnRevision, err := c.lister.List(selector.AsSelector())
   114  	if err != nil {
   115  		logrus.WithError(err).Errorf("Cannot list prowjob with selector %v", selector)
   116  		return err
   117  	}
   118  
   119  	// generate an aggregated report:
   120  	total := len(pjsOnRevision)
   121  	success := 0
   122  	message := ""
   123  
   124  	for _, pjOnRevision := range pjsOnRevision {
   125  		if pjOnRevision.Status.PrevReportStates[c.GetName()] == pjOnRevision.Status.State {
   126  			logrus.Infof("Revision %s has been reported already", pj.ObjectMeta.Labels[clientGerritRevision])
   127  			return nil
   128  		}
   129  
   130  		if pjOnRevision.Status.State == v1.SuccessState {
   131  			success++
   132  		}
   133  
   134  		message = fmt.Sprintf("%s\nJob %s finished with %s -- URL: %s", message, pjOnRevision.Spec.Job, pjOnRevision.Status.State, pjOnRevision.Status.URL)
   135  	}
   136  
   137  	message = fmt.Sprintf("%d out of %d jobs passed!\n%s", success, total, message)
   138  
   139  	// report back
   140  	gerritID := pj.ObjectMeta.Annotations[clientGerritID]
   141  	gerritInstance := pj.ObjectMeta.Annotations[clientGerritInstance]
   142  	gerritRevision := pj.ObjectMeta.Labels[clientGerritRevision]
   143  	reportLabel := client.CodeReview
   144  	if val, ok := pj.ObjectMeta.Labels[client.GerritReportLabel]; ok {
   145  		reportLabel = val
   146  	}
   147  
   148  	vote := client.LBTM
   149  	if success == total {
   150  		vote = client.LGTM
   151  	}
   152  	labels := map[string]string{reportLabel: vote}
   153  
   154  	logrus.Infof("Reporting to instance %s on id %s with message %s", gerritInstance, gerritID, message)
   155  	if err := c.gc.SetReview(gerritInstance, gerritID, gerritRevision, message, labels); err != nil {
   156  		logrus.WithError(err).Errorf("fail to set review with %s label on change ID %s", reportLabel, gerritID)
   157  
   158  		// possibly don't have label permissions, try without labels
   159  		message = fmt.Sprintf("[NOTICE]: Prow Bot cannot access %s label!\n%s", reportLabel, message)
   160  		if err := c.gc.SetReview(gerritInstance, gerritID, gerritRevision, message, nil); err != nil {
   161  			logrus.WithError(err).Errorf("fail to set plain review on change ID %s", gerritID)
   162  			return err
   163  		}
   164  	}
   165  	logrus.Infof("Review Complete")
   166  
   167  	return nil
   168  }