func New(maxRecordsPerKey int, opener io.Opener, path string) (*History, error) {
	hist := &History{
		logs:         map[string]*recordLog{},
		logSizeLimit: maxRecordsPerKey,
		opener:       opener,
		path:         path,
	}

	if path != "" {
		// Load existing history from GCS.
		var err error
		start := time.Now()
		hist.logs, err = readHistory(maxRecordsPerKey, hist.opener, hist.path)
		if err != nil {
			return nil, err
		}
		logrus.WithFields(logrus.Fields{
			"duration": time.Since(start).String(),
			"path":     hist.path,
		}).Debugf("Successfully read action history for %d pools.", len(hist.logs))
	}

	return hist, nil
}

func
New
(
maxRecordsPerKey
int
,
opener
io
.
Opener
,
path
string
)
(
*
History
,
error
)
{
hist
:=
&
History
{
logs
:
map
[
string
]
*
recordLog
{
}
,
logSizeLimit
:
maxRecordsPerKey
,
opener
:
opener
,
path
:
path
,
}
if
path
!=
"
"
{
// Load existing history from GCS.
var
err
error
start
:=
time
.
Now
(
)
hist
.
logs
,
err
=
readHistory
(
maxRecordsPerKey
,
hist
.
opener
,
hist
.
path
)
if
err
!=
{
return
,
err
}
logrus
.
WithFields
(
logrus
.
Fields
{
"
"
:
time
.
Since
(
start
)
.
String
(
)
,
"
"
:
hist
.
path
,
}
)
.
Debugf
(
"
"
,
len
(
hist
.
logs
)
)
}
return
hist
,
}

// New creates a new History struct with the specificed recordLog size limit.

New creates a new History struct with the specificed recordLog size limit .


func (h *History) Record(poolKey, action, baseSHA, err string, targets []prowapi.Pull) {
	t := now()
	sort.Sort(ByNum(targets))
	h.addRecord(
		poolKey,
		&Record{
			Time:    t,
			Action:  action,
			BaseSHA: baseSHA,
			Target:  targets,
			Err:     err,
		},
	)
}

func ( h * History ) Record ( poolKey , action , baseSHA , err string , targets [ ] prowapi . Pull ) { t := now ( ) sort . Sort ( ByNum ( targets ) ) h . addRecord ( poolKey , & Record { Time : t , Action : action , BaseSHA : baseSHA , Target : targets , Err : err , } , ) }

// Record appends an entry to the recordlog specified by the poolKey.

Record appends an entry to the recordlog specified by the poolKey .


func (h *History) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	b, err := json.Marshal(h.AllRecords())
	if err != nil {
		logrus.WithError(err).Error("Encoding JSON history.")
		b = []byte("{}")
	}
	if _, err = w.Write(b); err != nil {
		logrus.WithError(err).Error("Writing JSON history response.")
	}
}

func ( h * History ) ServeHTTP ( w http . ResponseWriter , r * http . Request ) { b , err := json . Marshal ( h . AllRecords ( ) ) if err != nil { logrus . WithError ( err ) . Error ( " " ) b = [ ] byte ( " " ) } if _ , err = w . Write ( b ) ; err != nil { logrus . WithError ( err ) . Error ( " " ) } }

// ServeHTTP serves a JSON mapping from pool key -> sorted records for the pool.

ServeHTTP serves a JSON mapping from pool key - > sorted records for the pool .


func (h *History) Flush() {
	if h.path == "" {
		return
	}
	records := h.AllRecords()
	start := time.Now()
	err := writeHistory(h.opener, h.path, records)
	log := logrus.WithFields(logrus.Fields{
		"duration": time.Since(start).String(),
		"path":     h.path,
	})
	if err != nil {
		log.WithError(err).Error("Error flushing action history to GCS.")
	} else {
		log.Debugf("Successfully flushed action history for %d pools.", len(h.logs))
	}
}

func ( h * History ) Flush ( ) { if h . path == " " { return } records := h . AllRecords ( ) start := time . Now ( ) err := writeHistory ( h . opener , h . path , records ) log := logrus . WithFields ( logrus . Fields { " " : time . Since ( start ) . String ( ) , " " : h . path , } ) if err != nil { log . WithError ( err ) . Error ( " " ) } else { log . Debugf ( " " , len ( h . logs ) ) } }

// Flush writes the action history to persistent storage if configured to do so.

Flush writes the action history to persistent storage if configured to do so .


func (h *History) AllRecords() map[string][]*Record {
	h.Lock()
	defer h.Unlock()

	res := make(map[string][]*Record, len(h.logs))
	for key, log := range h.logs {
		res[key] = log.toSlice()
	}
	return res
}

func ( h * History ) AllRecords ( ) map [ string ] [ ] * Record { h . Lock ( ) defer h . Unlock ( ) res := make ( map [ string ] [ ] * Record , len ( h . logs ) ) for key , log := range h . logs { res [ key ] = log . toSlice ( ) } return res }

// AllRecords generates a map from pool key -> sorted records for the pool.

AllRecords generates a map from pool key - > sorted records for the pool .


func MakeCommand() *cobra.Command {
	flags := &flags{}
	cmd := &cobra.Command{
		Use:   "download [bucket] [prowjob]",
		Short: "Finds and downloads the coverage profile file from the latest healthy build",
		Long: `Finds and downloads the coverage profile file from the latest healthy build
stored in given gcs directory.`,
		Run: func(cmd *cobra.Command, args []string) {
			run(flags, cmd, args)
		},
	}
	cmd.Flags().StringVarP(&flags.outputFile, "output", "o", "-", "output file")
	cmd.Flags().StringVarP(&flags.artifactsDirName, "artifactsDir", "a", "artifacts", "artifact directory name in GCS")
	cmd.Flags().StringVarP(&flags.profileName, "profile", "p", "coverage-profile", "code coverage profile file name in GCS")
	return cmd
}

// MakeCommand returns a `download` command.

MakeCommand returns a download command .


func (c *CommentCounterPlugin) CheckFlags() error {
	for _, pattern := range c.pattern {
		matcher, err := regexp.Compile(pattern)
		if err != nil {
			return err
		}
		c.matcher = append(c.matcher, matcher)
	}
	return nil
}

func ( c * CommentCounterPlugin ) CheckFlags ( ) error { for _ , pattern := range c . pattern { matcher , err := regexp . Compile ( pattern ) if err != nil { return err } c . matcher = append ( c . matcher , matcher ) } return nil }

// CheckFlags looks for comments matching regexes


func (c *CommentCounterPlugin) ReceiveComment(comment sql.Comment) []Point {
	points := []Point{}
	for _, matcher := range c.matcher {
		if matcher.MatchString(comment.Body) {
			points = append(points, Point{
				Values: map[string]interface{}{
					"comment": 1,
				},
				Date: comment.CommentCreatedAt,
			})
		}
	}
	return points
}

func ( c * CommentCounterPlugin ) ReceiveComment ( comment sql . Comment ) [ ] Point { points := [ ] Point { } for _ , matcher := range c . matcher { if matcher . MatchString ( comment . Body ) { points = append ( points , Point { Values : map [ string ] interface { } { " " : 1 , } , Date : comment . CommentCreatedAt , } ) } } return points }

// ReceiveComment adds matching comments to InfluxDB


func NewController(
	pjclientset clientset.Interface,
	queue workqueue.RateLimitingInterface,
	informer pjinformers.ProwJobInformer,
	reporter reportClient,
	numWorkers int,
	wg *sync.WaitGroup) *Controller {
	return &Controller{
		pjclientset: pjclientset,
		queue:       queue,
		informer:    informer,
		reporter:    reporter,
		numWorkers:  numWorkers,
		wg:          wg,
	}
}

func NewController ( pjclientset clientset . Interface , queue workqueue . RateLimitingInterface , informer pjinformers . ProwJobInformer , reporter reportClient , numWorkers int , wg * sync . WaitGroup ) * Controller { return & Controller { pjclientset : pjclientset , queue : queue , informer : informer , reporter : reporter , numWorkers : numWorkers , wg : wg , } }

// NewController constructs a new instance of the crier controller.

NewController constructs a new instance of the crier controller .


func (c *Controller) Run(stopCh <-chan struct{}) {
	// handle a panic with logging and exiting
	defer utilruntime.HandleCrash()
	// ignore new items in the queue but when all goroutines
	// have completed existing items then shutdown
	defer c.queue.ShutDown()

	logrus.Info("Initiating controller")
	c.informer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
		AddFunc: func(obj interface{}) {
			key, err := cache.MetaNamespaceKeyFunc(obj)
			logrus.WithField("prowjob", key).Infof("Add prowjob")
			if err != nil {
				logrus.WithError(err).Error("Cannot get key from object meta")
				return
			}
			c.queue.AddRateLimited(key)
		},
		UpdateFunc: func(oldObj, newObj interface{}) {
			key, err := cache.MetaNamespaceKeyFunc(newObj)
			logrus.WithField("prowjob", key).Infof("Update prowjob")
			if err != nil {
				logrus.WithError(err).Error("Cannot get key from object meta")
				return
			}
			c.queue.AddRateLimited(key)
		},
	})

	// run the informer to start listing and watching resources
	go c.informer.Informer().Run(stopCh)

	// do the initial synchronization (one time) to populate resources
	if !cache.WaitForCacheSync(stopCh, c.HasSynced) {
		utilruntime.HandleError(fmt.Errorf("Error syncing cache"))
		return
	}
	logrus.Info("Controller.Run: cache sync complete")

	// run the runWorker method every second with a stop channel
	for i := 0; i < c.numWorkers; i++ {
		go wait.Until(c.runWorker, time.Second, stopCh)
	}

	logrus.Infof("Started %d workers", c.numWorkers)
	<-stopCh
	logrus.Info("Shutting down workers")
}

// Run is the main path of execution for the controller loop.

Run is the main path of execution for the controller loop .


func (c *Controller) runWorker() {
	c.wg.Add(1)
	for c.processNextItem() {
	}
	c.wg.Done()
}

func ( c * Controller ) runWorker ( ) { c . wg . Add ( 1 ) for c . processNextItem ( ) { } c . wg . Done ( ) }

// runWorker executes the loop to process new items added to the queue.

runWorker executes the loop to process new items added to the queue .


func New() (*LocalGit, *git.Client, error) {
	g, err := exec.LookPath("git")
	if err != nil {
		return nil, nil, err
	}
	t, err := ioutil.TempDir("", "localgit")
	if err != nil {
		return nil, nil, err
	}
	c, err := git.NewClient()
	if err != nil {
		os.RemoveAll(t)
		return nil, nil, err
	}

	getSecret := func() []byte {
		return []byte("")
	}

	c.SetCredentials("", getSecret)

	c.SetRemote(t)
	return &LocalGit{
		Dir: t,
		Git: g,
	}, c, nil
}

func New ( ) ( * LocalGit , * git . Client , error ) { g , err := exec . LookPath ( " " ) if err != nil { return nil , nil , err } t , err := ioutil . TempDir ( " " , " " ) if err != nil { return nil , nil , err } c , err := git . NewClient ( ) if err != nil { os . RemoveAll ( t ) return nil , nil , err } getSecret := func ( ) [ ] byte { return [ ] byte ( " " ) } c . SetCredentials ( " " , getSecret ) c . SetRemote ( t ) return & LocalGit { Dir : t , Git : g , } , c , nil }

// New creates a LocalGit and a git.Client pointing at it.

New creates a LocalGit and a git . Client pointing at it .


func (lg *LocalGit) MakeFakeRepo(org, repo string) error {
	rdir := filepath.Join(lg.Dir, org, repo)
	if err := os.MkdirAll(rdir, os.ModePerm); err != nil {
		return err
	}

	if err := runCmd(lg.Git, rdir, "init"); err != nil {
		return err
	}
	if err := runCmd(lg.Git, rdir, "config", "user.email", "test@test.test"); err != nil {
		return err
	}
	if err := runCmd(lg.Git, rdir, "config", "user.name", "test test"); err != nil {
		return err
	}
	if err := runCmd(lg.Git, rdir, "config", "commit.gpgsign", "false"); err != nil {
		return err
	}
	if err := lg.AddCommit(org, repo, map[string][]byte{"initial": {}}); err != nil {
		return err
	}

	return nil
}

func ( lg * LocalGit ) MakeFakeRepo ( org , repo string ) error { rdir := filepath . Join ( lg . Dir , org , repo ) if err := os . MkdirAll ( rdir , os . ModePerm ) ; err != nil { return err } if err := runCmd ( lg . Git , rdir , " " ) ; err != nil { return err } if err := runCmd ( lg . Git , rdir , " " , " " , " " ) ; err != nil { return err } if err := runCmd ( lg . Git , rdir , " " , " " , " " ) ; err != nil { return err } if err := runCmd ( lg . Git , rdir , " " , " " , " " ) ; err != nil { return err } if err := lg . AddCommit ( org , repo , map [ string ] [ ] byte { " " : { } } ) ; err != nil { return err } return nil }

// MakeFakeRepo creates the given repo and makes an initial commit.

MakeFakeRepo creates the given repo and makes an initial commit .


func (lg *LocalGit) AddCommit(org, repo string, files map[string][]byte) error {
	rdir := filepath.Join(lg.Dir, org, repo)
	for f, b := range files {
		path := filepath.Join(rdir, f)
		if err := os.MkdirAll(filepath.Dir(path), os.ModePerm); err != nil {
			return err
		}
		if err := ioutil.WriteFile(path, b, os.ModePerm); err != nil {
			return err
		}
		if err := runCmd(lg.Git, rdir, "add", f); err != nil {
			return err
		}
	}
	return runCmd(lg.Git, rdir, "commit", "-m", "wow")
}

func ( lg * LocalGit ) AddCommit ( org , repo string , files map [ string ] [ ] byte ) error { rdir := filepath . Join ( lg . Dir , org , repo ) for f , b := range files { path := filepath . Join ( rdir , f ) if err := os . MkdirAll ( filepath . Dir ( path ) , os . ModePerm ) ; err != nil { return err } if err := ioutil . WriteFile ( path , b , os . ModePerm ) ; err != nil { return err } if err := runCmd ( lg . Git , rdir , " " , f ) ; err != nil { return err } } return runCmd ( lg . Git , rdir , " " , " " , " " ) }

// AddCommit adds the files to a new commit in the repo.

AddCommit adds the files to a new commit in the repo .


func (lg *LocalGit) CheckoutNewBranch(org, repo, branch string) error {
	rdir := filepath.Join(lg.Dir, org, repo)
	return runCmd(lg.Git, rdir, "checkout", "-b", branch)
}

func ( lg * LocalGit ) CheckoutNewBranch ( org , repo , branch string ) error { rdir := filepath . Join ( lg . Dir , org , repo ) return runCmd ( lg . Git , rdir , " " , " " , branch ) }

// CheckoutNewBranch does git checkout -b.

CheckoutNewBranch does git checkout - b .


func (lg *LocalGit) Checkout(org, repo, commitlike string) error {
	rdir := filepath.Join(lg.Dir, org, repo)
	return runCmd(lg.Git, rdir, "checkout", commitlike)
}

func ( lg * LocalGit ) Checkout ( org , repo , commitlike string ) error { rdir := filepath . Join ( lg . Dir , org , repo ) return runCmd ( lg . Git , rdir , " " , commitlike ) }


func (lg *LocalGit) RevParse(org, repo, commitlike string) (string, error) {
	rdir := filepath.Join(lg.Dir, org, repo)
	return runCmdOutput(lg.Git, rdir, "rev-parse", commitlike)
}

func ( lg * LocalGit ) RevParse ( org , repo , commitlike string ) ( string , error ) { rdir := filepath . Join ( lg . Dir , org , repo ) return runCmdOutput ( lg . Git , rdir , " " , commitlike ) }


func CleanAll(sess *session.Session, region string) error {
	acct, err := account.GetAccount(sess, regions.Default)
	if err != nil {
		return errors.Wrap(err, "Failed to retrieve account")
	}
	klog.V(1).Infof("Account: %s", acct)

	var regionList []string
	if region == "" {
		regionList, err = regions.GetAll(sess)
		if err != nil {
			return errors.Wrap(err, "Couldn't retrieve list of regions")
		}
	} else {
		regionList = []string{region}
	}
	klog.Infof("Regions: %+v", regionList)

	for _, r := range regionList {
		for _, typ := range RegionalTypeList {
			set, err := typ.ListAll(sess, acct, r)
			if err != nil {
				return errors.Wrapf(err, "Failed to list resources of type %T", typ)
			}
			if err := typ.MarkAndSweep(sess, acct, r, set); err != nil {
				return errors.Wrapf(err, "Couldn't sweep resources of type %T", typ)
			}
		}
	}

	for _, typ := range GlobalTypeList {
		set, err := typ.ListAll(sess, acct, regions.Default)
		if err != nil {
			return errors.Wrapf(err, "Failed to list resources of type %T", typ)
		}
		if err := typ.MarkAndSweep(sess, acct, regions.Default, set); err != nil {
			return errors.Wrapf(err, "Couldn't sweep resources of type %T", typ)
		}
	}

	return nil
}

func CleanAll ( sess * session . Session , region string ) error { acct , err := account . GetAccount ( sess , regions . Default ) if err != nil { return errors . Wrap ( err , " " ) } klog . V ( 1 ) . Infof ( " " , acct ) var regionList [ ] string if region == " " { regionList , err = regions . GetAll ( sess ) if err != nil { return errors . Wrap ( err , " " ) } } else { regionList = [ ] string { region } } klog . Infof ( " " , regionList ) for _ , r := range regionList { for _ , typ := range RegionalTypeList { set , err := typ . ListAll ( sess , acct , r ) if err != nil { return errors . Wrapf ( err , " " , typ ) } if err := typ . MarkAndSweep ( sess , acct , r , set ) ; err != nil { return errors . Wrapf ( err , " " , typ ) } } } for _ , typ := range GlobalTypeList { set , err := typ . ListAll ( sess , acct , regions . Default ) if err != nil { return errors . Wrapf ( err , " " , typ ) } if err := typ . MarkAndSweep ( sess , acct , regions . Default , set ) ; err != nil { return errors . Wrapf ( err , " " , typ ) } } return nil }

// CleanAll cleans all of the resources for all of the regions visible to
// the provided AWS session.

CleanAll cleans all of the resources for all of the regions visible to the provided AWS session .


func optionsForRepo(config *plugins.Configuration, org, repo string) *plugins.Lgtm {
	fullName := fmt.Sprintf("%s/%s", org, repo)
	for i := range config.Lgtm {
		if !strInSlice(org, config.Lgtm[i].Repos) && !strInSlice(fullName, config.Lgtm[i].Repos) {
			continue
		}
		return &config.Lgtm[i]
	}
	return &plugins.Lgtm{}
}

func optionsForRepo ( config * plugins . Configuration , org , repo string ) * plugins . Lgtm { fullName := fmt . Sprintf ( " " , org , repo ) for i := range config . Lgtm { if ! strInSlice ( org , config . Lgtm [ i ] . Repos ) && ! strInSlice ( fullName , config . Lgtm [ i ] . Repos ) { continue } return & config . Lgtm [ i ] } return & plugins . Lgtm { } }

// optionsForRepo gets the plugins.Lgtm struct that is applicable to the indicated repo.

optionsForRepo gets the plugins . Lgtm struct that is applicable to the indicated repo .


func getChangedFiles(gc githubClient, org, repo string, number int) ([]string, error) {
	changes, err := gc.GetPullRequestChanges(org, repo, number)
	if err != nil {
		return nil, fmt.Errorf("cannot get PR changes for %s/%s#%d", org, repo, number)
	}
	var filenames []string
	for _, change := range changes {
		filenames = append(filenames, change.Filename)
	}
	return filenames, nil
}

func getChangedFiles ( gc githubClient , org , repo string , number int ) ( [ ] string , error ) { changes , err := gc . GetPullRequestChanges ( org , repo , number ) if err != nil { return nil , fmt . Errorf ( " " , org , repo , number ) } var filenames [ ] string for _ , change := range changes { filenames = append ( filenames , change . Filename ) } return filenames , nil }

// getChangedFiles returns all the changed files for the provided pull request.

getChangedFiles returns all the changed files for the provided pull request .


func loadReviewers(ro repoowners.RepoOwner, filenames []string) sets.String {
	reviewers := sets.String{}
	for _, filename := range filenames {
		reviewers = reviewers.Union(ro.Approvers(filename)).Union(ro.Reviewers(filename))
	}
	return reviewers
}

func loadReviewers ( ro repoowners . RepoOwner , filenames [ ] string ) sets . String { reviewers := sets . String { } for _ , filename := range filenames { reviewers = reviewers . Union ( ro . Approvers ( filename ) ) . Union ( ro . Reviewers ( filename ) ) } return reviewers }

// loadReviewers returns all reviewers and approvers from all OWNERS files that
// cover the provided filenames.

loadReviewers returns all reviewers and approvers from all OWNERS files that cover the provided filenames .


func NewController(lastSyncFallback, cookiefilePath string, projects map[string][]string, kc *kube.Client, cfg config.Getter) (*Controller, error) {
	if lastSyncFallback == "" {
		return nil, errors.New("empty lastSyncFallback")
	}

	var lastUpdate time.Time
	if buf, err := ioutil.ReadFile(lastSyncFallback); err == nil {
		unix, err := strconv.ParseInt(string(buf), 10, 64)
		if err != nil {
			return nil, err
		}
		lastUpdate = time.Unix(unix, 0)
	} else if err != nil && !os.IsNotExist(err) {
		return nil, fmt.Errorf("failed to read lastSyncFallback: %v", err)
	} else {
		logrus.Warnf("lastSyncFallback not found: %s", lastSyncFallback)
		lastUpdate = time.Now()
	}

	c, err := client.NewClient(projects)
	if err != nil {
		return nil, err
	}
	c.Start(cookiefilePath)

	return &Controller{
		kc:               kc,
		config:           cfg,
		gc:               c,
		lastUpdate:       lastUpdate,
		lastSyncFallback: lastSyncFallback,
	}, nil
}

func NewController ( lastSyncFallback , cookiefilePath string , projects map [ string ] [ ] string , kc * kube . Client , cfg config . Getter ) ( * Controller , error ) { if lastSyncFallback == " " { return nil , errors . New ( " " ) } var lastUpdate time . Time if buf , err := ioutil . ReadFile ( lastSyncFallback ) ; err == nil { unix , err := strconv . ParseInt ( string ( buf ) , 10 , 64 ) if err != nil { return nil , err } lastUpdate = time . Unix ( unix , 0 ) } else if err != nil && ! os . IsNotExist ( err ) { return nil , fmt . Errorf ( " " , err ) } else { logrus . Warnf ( " " , lastSyncFallback ) lastUpdate = time . Now ( ) } c , err := client . NewClient ( projects ) if err != nil { return nil , err } c . Start ( cookiefilePath ) return & Controller { kc : kc , config : cfg , gc : c , lastUpdate : lastUpdate , lastSyncFallback : lastSyncFallback , } , nil }

// NewController returns a new gerrit controller client


func (c *Controller) SaveLastSync(lastSync time.Time) error {
	if c.lastSyncFallback == "" {
		return nil
	}

	lastSyncUnix := strconv.FormatInt(lastSync.Unix(), 10)
	logrus.Infof("Writing last sync: %s", lastSyncUnix)

	tempFile, err := ioutil.TempFile(filepath.Dir(c.lastSyncFallback), "temp")
	if err != nil {
		return err
	}
	defer os.Remove(tempFile.Name())

	err = ioutil.WriteFile(tempFile.Name(), []byte(lastSyncUnix), 0644)
	if err != nil {
		return err
	}

	err = os.Rename(tempFile.Name(), c.lastSyncFallback)
	if err != nil {
		logrus.WithError(err).Info("Rename failed, fallback to copyfile")
		return copyFile(tempFile.Name(), c.lastSyncFallback)
	}
	return nil
}

func ( c * Controller ) SaveLastSync ( lastSync time . Time ) error { if c . lastSyncFallback == " " { return nil } lastSyncUnix := strconv . FormatInt ( lastSync . Unix ( ) , 10 ) logrus . Infof ( " " , lastSyncUnix ) tempFile , err := ioutil . TempFile ( filepath . Dir ( c . lastSyncFallback ) , " " ) if err != nil { return err } defer os . Remove ( tempFile . Name ( ) ) err = ioutil . WriteFile ( tempFile . Name ( ) , [ ] byte ( lastSyncUnix ) , 0644 ) if err != nil { return err } err = os . Rename ( tempFile . Name ( ) , c . lastSyncFallback ) if err != nil { logrus . WithError ( err ) . Info ( " " ) return copyFile ( tempFile . Name ( ) , c . lastSyncFallback ) } return nil }

// SaveLastSync saves last sync time in Unix to a volume


func (c *Controller) Sync() error {
	syncTime := c.lastUpdate

	for instance, changes := range c.gc.QueryChanges(c.lastUpdate, c.config().Gerrit.RateLimit) {
		for _, change := range changes {
			if err := c.ProcessChange(instance, change); err != nil {
				logrus.WithError(err).Errorf("Failed process change %v", change.CurrentRevision)
			}
			if syncTime.Before(change.Updated.Time) {
				syncTime = change.Updated.Time
			}
		}

		logrus.Infof("Processed %d changes for instance %s", len(changes), instance)
	}

	c.lastUpdate = syncTime
	if err := c.SaveLastSync(syncTime); err != nil {
		logrus.WithError(err).Errorf("last sync %v, cannot save to path %v", syncTime, c.lastSyncFallback)
	}

	return nil
}

func ( c * Controller ) Sync ( ) error { syncTime := c . lastUpdate for instance , changes := range c . gc . QueryChanges ( c . lastUpdate , c . config ( ) . Gerrit . RateLimit ) { for _ , change := range changes { if err := c . ProcessChange ( instance , change ) ; err != nil { logrus . WithError ( err ) . Errorf ( " " , change . CurrentRevision ) } if syncTime . Before ( change . Updated . Time ) { syncTime = change . Updated . Time } } logrus . Infof ( " " , len ( changes ) , instance ) } c . lastUpdate = syncTime if err := c . SaveLastSync ( syncTime ) ; err != nil { logrus . WithError ( err ) . Errorf ( " " , syncTime , c . lastSyncFallback ) } return nil }

// Sync looks for newly made gerrit changes
// and creates prowjobs according to specs

Sync looks for newly made gerrit changes and creates prowjobs according to specs


func (e *EventCounterPlugin) AddFlags(cmd *cobra.Command) {
	cmd.Flags().StringVar(&e.desc, "event", "", "Match event (eg: `opened`)")
}

func ( e * EventCounterPlugin ) AddFlags ( cmd * cobra . Command ) { cmd . Flags ( ) . StringVar ( & e . desc , " " , " " , " " ) }

// AddFlags adds "event" to the command help


func (e *EventCounterPlugin) CheckFlags() error {
	e.matcher = NewEventMatcher(e.desc)
	return nil
}

func ( e * EventCounterPlugin ) CheckFlags ( ) error { e . matcher = NewEventMatcher ( e . desc ) return nil }

// CheckFlags is delegated to EventMatcher


func (e *EventCounterPlugin) ReceiveIssueEvent(event sql.IssueEvent) []Point {
	var label string
	if event.Label != nil {
		label = *event.Label
	}

	if !e.matcher.Match(event.Event, label) {
		return nil
	}
	return []Point{
		{
			Values: map[string]interface{}{"event": 1},
			Date:   event.EventCreatedAt,
		},
	}
}

func ( e * EventCounterPlugin ) ReceiveIssueEvent ( event sql . IssueEvent ) [ ] Point { var label string if event . Label != nil { label = * event . Label } if ! e . matcher . Match ( event . Event , label ) { return nil } return [ ] Point { { Values : map [ string ] interface { } { " " : 1 } , Date : event . EventCreatedAt , } , } }

// ReceiveIssueEvent adds issue events to InfluxDB


func Upload(bucket *storage.BucketHandle, uploadTargets map[string]UploadFunc) error {
	errCh := make(chan error, len(uploadTargets))
	group := &sync.WaitGroup{}
	group.Add(len(uploadTargets))
	for dest, upload := range uploadTargets {
		obj := bucket.Object(dest)
		logrus.WithField("dest", dest).Info("Queued for upload")
		go func(f UploadFunc, obj *storage.ObjectHandle, name string) {
			defer group.Done()
			if err := f(obj); err != nil {
				errCh <- err
			}
			logrus.WithField("dest", name).Info("Finished upload")
		}(upload, obj, dest)
	}
	group.Wait()
	close(errCh)
	if len(errCh) != 0 {
		var uploadErrors []error
		for err := range errCh {
			uploadErrors = append(uploadErrors, err)
		}
		return fmt.Errorf("encountered errors during upload: %v", uploadErrors)
	}

	return nil
}

func Upload ( bucket * storage . BucketHandle , uploadTargets map [ string ] UploadFunc ) error { errCh := make ( chan error , len ( uploadTargets ) ) group := & sync . WaitGroup { } group . Add ( len ( uploadTargets ) ) for dest , upload := range uploadTargets { obj := bucket . Object ( dest ) logrus . WithField ( " " , dest ) . Info ( " " ) go func ( f UploadFunc , obj * storage . ObjectHandle , name string ) { defer group . Done ( ) if err := f ( obj ) ; err != nil { errCh <- err } logrus . WithField ( " " , name ) . Info ( " " ) } ( upload , obj , dest ) } group . Wait ( ) close ( errCh ) if len ( errCh ) != 0 { var uploadErrors [ ] error for err := range errCh { uploadErrors = append ( uploadErrors , err ) } return fmt . Errorf ( " " , uploadErrors ) } return nil }

// Upload uploads all of the data in the
// uploadTargets map to GCS in parallel. The map is
// keyed on GCS path under the bucket

Upload uploads all of the data in the uploadTargets map to GCS in parallel . The map is keyed on GCS path under the bucket


func FileUploadWithMetadata(file string, metadata map[string]string) UploadFunc {
	return func(obj *storage.ObjectHandle) error {
		reader, err := os.Open(file)
		if err != nil {
			return err
		}

		uploadErr := DataUploadWithMetadata(reader, metadata)(obj)
		closeErr := reader.Close()

		return errorutil.NewAggregate(uploadErr, closeErr)
	}
}

func FileUploadWithMetadata ( file string , metadata map [ string ] string ) UploadFunc { return func ( obj * storage . ObjectHandle ) error { reader , err := os . Open ( file ) if err != nil { return err } uploadErr := DataUploadWithMetadata ( reader , metadata ) ( obj ) closeErr := reader . Close ( ) return errorutil . NewAggregate ( uploadErr , closeErr ) } }

// FileUploadWithMetadata returns an UploadFunc which copies all
// data from the file on disk into GCS object and also sets the provided
// metadata fields on the object.

FileUploadWithMetadata returns an UploadFunc which copies all data from the file on disk into GCS object and also sets the provided metadata fields on the object .


func DataUploadWithMetadata(src io.Reader, metadata map[string]string) UploadFunc {
	return func(obj *storage.ObjectHandle) error {
		writer := obj.NewWriter(context.Background())
		writer.Metadata = metadata
		_, copyErr := io.Copy(writer, src)
		closeErr := writer.Close()

		return errorutil.NewAggregate(copyErr, closeErr)
	}
}

func DataUploadWithMetadata ( src io . Reader , metadata map [ string ] string ) UploadFunc { return func ( obj * storage . ObjectHandle ) error { writer := obj . NewWriter ( context . Background ( ) ) writer . Metadata = metadata _ , copyErr := io . Copy ( writer , src ) closeErr := writer . Close ( ) return errorutil . NewAggregate ( copyErr , closeErr ) } }

// DataUploadWithMetadata returns an UploadFunc which copies all
// data from src reader into GCS and also sets the provided metadata
// fields onto the object.

DataUploadWithMetadata returns an UploadFunc which copies all data from src reader into GCS and also sets the provided metadata fields onto the object .


func HasLabel(label string, issueLabels []Label) bool {
	for _, l := range issueLabels {
		if strings.ToLower(l.Name) == strings.ToLower(label) {
			return true
		}
	}
	return false
}

func HasLabel ( label string , issueLabels [ ] Label ) bool { for _ , l := range issueLabels { if strings . ToLower ( l . Name ) == strings . ToLower ( label ) { return true } } return false }

// HasLabel checks if label is in the label set "issueLabels".

HasLabel checks if label is in the label set issueLabels .


func ImageTooBig(url string) (bool, error) {
	// limit is 10MB
	limit := 10000000
	// try to get the image size from Content-Length header
	resp, err := http.Head(url)
	if err != nil {
		return true, fmt.Errorf("HEAD error: %v", err)
	}
	if sc := resp.StatusCode; sc != http.StatusOK {
		return true, fmt.Errorf("failing %d response", sc)
	}
	size, _ := strconv.Atoi(resp.Header.Get("Content-Length"))
	if size > limit {
		return true, nil
	}
	return false, nil
}

// ImageTooBig checks if image is bigger than github limits


func LevelFromPermissions(permissions RepoPermissions) RepoPermissionLevel {
	if permissions.Admin {
		return Admin
	} else if permissions.Push {
		return Write
	} else if permissions.Pull {
		return Read
	} else {
		return None
	}
}

func LevelFromPermissions ( permissions RepoPermissions ) RepoPermissionLevel { if permissions . Admin { return Admin } else if permissions . Push { return Write } else if permissions . Pull { return Read } else { return None } }

// LevelFromPermissions adapts a repo permissions struct to the
// appropriate permission level used elsewhere

LevelFromPermissions adapts a repo permissions struct to the appropriate permission level used elsewhere


func PermissionsFromLevel(permission RepoPermissionLevel) RepoPermissions {
	switch permission {
	case None:
		return RepoPermissions{}
	case Read:
		return RepoPermissions{Pull: true}
	case Write:
		return RepoPermissions{Pull: true, Push: true}
	case Admin:
		return RepoPermissions{Pull: true, Push: true, Admin: true}
	default:
		return RepoPermissions{}
	}
}

func PermissionsFromLevel ( permission RepoPermissionLevel ) RepoPermissions { switch permission { case None : return RepoPermissions { } case Read : return RepoPermissions { Pull : true } case Write : return RepoPermissions { Pull : true , Push : true } case Admin : return RepoPermissions { Pull : true , Push : true , Admin : true } default : return RepoPermissions { } } }

// PermissionsFromLevel adapts a repo permission level to the
// appropriate permissions struct used elsewhere

PermissionsFromLevel adapts a repo permission level to the appropriate permissions struct used elsewhere


func newProwJobs(c *ProwV1Client, namespace string) *prowJobs {
	return &prowJobs{
		client: c.RESTClient(),
		ns:     namespace,
	}
}

func newProwJobs ( c * ProwV1Client , namespace string ) * prowJobs { return & prowJobs { client : c . RESTClient ( ) , ns : namespace , } }


func (b Blockers) GetApplicable(org, repo, branch string) []Blocker {
	var res []Blocker
	res = append(res, b.Repo[orgRepo{org: org, repo: repo}]...)
	res = append(res, b.Branch[orgRepoBranch{org: org, repo: repo, branch: branch}]...)

	sort.Slice(res, func(i, j int) bool {
		return res[i].Number < res[j].Number
	})
	return res
}

func ( b Blockers ) GetApplicable ( org , repo , branch string ) [ ] Blocker { var res [ ] Blocker res = append ( res , b . Repo [ orgRepo { org : org , repo : repo } ] ... ) res = append ( res , b . Branch [ orgRepoBranch { org : org , repo : repo , branch : branch } ] ... ) sort . Slice ( res , func ( i , j int ) bool { return res [ i ] . Number < res [ j ] . Number } ) return res }

// GetApplicable returns the subset of blockers applicable to the specified branch.

GetApplicable returns the subset of blockers applicable to the specified branch .


func serve(jc *jenkins.Client) {
	http.Handle("/", gziphandler.GzipHandler(handleLog(jc)))
	http.Handle("/metrics", promhttp.Handler())
	logrus.WithError(http.ListenAndServe(":8080", nil)).Fatal("ListenAndServe returned.")
}

func serve ( jc * jenkins . Client ) { http . Handle ( " " , gziphandler . GzipHandler ( handleLog ( jc ) ) ) http . Handle ( " " , promhttp . Handler ( ) ) logrus . WithError ( http . ListenAndServe ( " " , nil ) ) . Fatal ( " " ) }

// serve starts a http server and serves Jenkins logs
// and prometheus metrics. Meant to be called inside
// a goroutine.

serve starts a http server and serves Jenkins logs and prometheus metrics . Meant to be called inside a goroutine .


func NewCountPlugin(runner func(Plugin) error) *cobra.Command {
	stateCounter := &StatePlugin{}
	eventCounter := &EventCounterPlugin{}
	commentsAsEvents := NewFakeCommentPluginWrapper(eventCounter)
	commentCounter := &CommentCounterPlugin{}
	authorLoggable := NewMultiplexerPluginWrapper(
		commentsAsEvents,
		commentCounter,
	)
	authorLogged := NewAuthorLoggerPluginWrapper(authorLoggable)
	fullMultiplex := NewMultiplexerPluginWrapper(authorLogged, stateCounter)

	fakeOpen := NewFakeOpenPluginWrapper(fullMultiplex)
	typeFilter := NewTypeFilterWrapperPlugin(fakeOpen)
	authorFilter := NewAuthorFilterPluginWrapper(typeFilter)

	cmd := &cobra.Command{
		Use:   "count",
		Short: "Count events and number of issues in given state, and for how long",
		RunE: func(cmd *cobra.Command, args []string) error {
			if err := eventCounter.CheckFlags(); err != nil {
				return err
			}
			if err := stateCounter.CheckFlags(); err != nil {
				return err
			}
			if err := typeFilter.CheckFlags(); err != nil {
				return err
			}
			if err := commentCounter.CheckFlags(); err != nil {
				return err
			}
			return runner(authorFilter)
		},
	}

	eventCounter.AddFlags(cmd)
	stateCounter.AddFlags(cmd)
	commentCounter.AddFlags(cmd)
	typeFilter.AddFlags(cmd)
	authorFilter.AddFlags(cmd)
	authorLogged.AddFlags(cmd)

	return cmd
}

func NewCountPlugin ( runner func ( Plugin ) error ) * cobra . Command { stateCounter := & StatePlugin { } eventCounter := & EventCounterPlugin { } commentsAsEvents := NewFakeCommentPluginWrapper ( eventCounter ) commentCounter := & CommentCounterPlugin { } authorLoggable := NewMultiplexerPluginWrapper ( commentsAsEvents , commentCounter , ) authorLogged := NewAuthorLoggerPluginWrapper ( authorLoggable ) fullMultiplex := NewMultiplexerPluginWrapper ( authorLogged , stateCounter ) fakeOpen := NewFakeOpenPluginWrapper ( fullMultiplex ) typeFilter := NewTypeFilterWrapperPlugin ( fakeOpen ) authorFilter := NewAuthorFilterPluginWrapper ( typeFilter ) cmd := & cobra . Command { Use : " " , Short : " " , RunE : func ( cmd * cobra . Command , args [ ] string ) error { if err := eventCounter . CheckFlags ( ) ; err != nil { return err } if err := stateCounter . CheckFlags ( ) ; err != nil { return err } if err := typeFilter . CheckFlags ( ) ; err != nil { return err } if err := commentCounter . CheckFlags ( ) ; err != nil { return err } return runner ( authorFilter ) } , } eventCounter . AddFlags ( cmd ) stateCounter . AddFlags ( cmd ) commentCounter . AddFlags ( cmd ) typeFilter . AddFlags ( cmd ) authorFilter . AddFlags ( cmd ) authorLogged . AddFlags ( cmd ) return cmd }

// NewCountPlugin counts events and number of issues in given state, and for how long.

NewCountPlugin counts events and number of issues in given state and for how long .


func (o *FakeCommentPluginWrapper) ReceiveComment(comment sql.Comment) []Point {
	// Create a fake "commented" event for every comment we receive.
	fakeEvent := sql.IssueEvent{
		IssueID:        comment.IssueID,
		Event:          "commented",
		EventCreatedAt: comment.CommentCreatedAt,
		Actor:          &comment.User,
	}

	return append(
		o.plugin.ReceiveComment(comment),
		o.plugin.ReceiveIssueEvent(fakeEvent)...,
	)
}

// ReceiveComment creates a fake "commented" event

ReceiveComment creates a fake commented event


func updateMetrics(interval time.Duration, diskRoot string) {
	logger := logrus.WithField("sync-loop", "updateMetrics")
	ticker := time.NewTicker(interval)
	for ; true; <-ticker.C {
		logger.Info("tick")
		_, bytesFree, bytesUsed, err := diskutil.GetDiskUsage(diskRoot)
		if err != nil {
			logger.WithError(err).Error("Failed to get disk metrics")
		} else {
			promMetrics.DiskFree.Set(float64(bytesFree) / 1e9)
			promMetrics.DiskUsed.Set(float64(bytesUsed) / 1e9)
			promMetrics.DiskTotal.Set(float64(bytesFree+bytesUsed) / 1e9)
		}
	}
}

func updateMetrics ( interval time . Duration , diskRoot string ) { logger := logrus . WithField ( " " , " " ) ticker := time . NewTicker ( interval ) for ; true ; <- ticker . C { logger . Info ( " " ) _ , bytesFree , bytesUsed , err := diskutil . GetDiskUsage ( diskRoot ) if err != nil { logger . WithError ( err ) . Error ( " " ) } else { promMetrics . DiskFree . Set ( float64 ( bytesFree ) / 1e9 ) promMetrics . DiskUsed . Set ( float64 ( bytesUsed ) / 1e9 ) promMetrics . DiskTotal . Set ( float64 ( bytesFree + bytesUsed ) / 1e9 ) } } }


func (r *Ranch) LogStatus() {
	resources, err := r.Storage.GetResources()

	if err != nil {
		return
	}

	resJSON, err := json.Marshal(resources)
	if err != nil {
		logrus.WithError(err).Errorf("Fail to marshal Resources. %v", resources)
	}
	logrus.Infof("Current Resources : %v", string(resJSON))
}

func ( r * Ranch ) LogStatus ( ) { resources , err := r . Storage . GetResources ( ) if err != nil { return } resJSON , err := json . Marshal ( resources ) if err != nil { logrus . WithError ( err ) . Errorf ( " " , resources ) } logrus . Infof ( " " , string ( resJSON ) ) }

// LogStatus outputs current status of all resources


func (r *Ranch) SyncConfig(config string) error {
	resources, err := ParseConfig(config)
	if err != nil {
		return err
	}
	if err := r.Storage.SyncResources(resources); err != nil {
		return err
	}
	return nil
}

func ( r * Ranch ) SyncConfig ( config string ) error { resources , err := ParseConfig ( config ) if err != nil { return err } if err := r . Storage . SyncResources ( resources ) ; err != nil { return err } return nil }

// SyncConfig updates resource list from a file


func (r *Ranch) Metric(rtype string) (common.Metric, error) {
	metric := common.Metric{
		Type:    rtype,
		Current: map[string]int{},
		Owners:  map[string]int{},
	}

	resources, err := r.Storage.GetResources()
	if err != nil {
		logrus.WithError(err).Error("cannot find resources")
		return metric, &ResourceNotFound{rtype}
	}

	for _, res := range resources {
		if res.Type != rtype {
			continue
		}

		if _, ok := metric.Current[res.State]; !ok {
			metric.Current[res.State] = 0
		}

		if _, ok := metric.Owners[res.Owner]; !ok {
			metric.Owners[res.Owner] = 0
		}

		metric.Current[res.State]++
		metric.Owners[res.Owner]++
	}

	if len(metric.Current) == 0 && len(metric.Owners) == 0 {
		return metric, &ResourceNotFound{rtype}
	}

	return metric, nil
}

func ( r * Ranch ) Metric ( rtype string ) ( common . Metric , error ) { metric := common . Metric { Type : rtype , Current : map [ string ] int { } , Owners : map [ string ] int { } , } resources , err := r . Storage . GetResources ( ) if err != nil { logrus . WithError ( err ) . Error ( " " ) return metric , & ResourceNotFound { rtype } } for _ , res := range resources { if res . Type != rtype { continue } if _ , ok := metric . Current [ res . State ] ; ! ok { metric . Current [ res . State ] = 0 } if _ , ok := metric . Owners [ res . Owner ] ; ! ok { metric . Owners [ res . Owner ] = 0 } metric . Current [ res . State ] ++ metric . Owners [ res . Owner ] ++ } if len ( metric . Current ) == 0 && len ( metric . Owners ) == 0 { return metric , & ResourceNotFound { rtype } } return metric , nil }

// Metric returns a metric object with metrics filled in


func FormatURL(dogURL string) (string, error) {
	if dogURL == "" {
		return "", errors.New("empty url")
	}
	src, err := url.ParseRequestURI(dogURL)
	if err != nil {
		return "", fmt.Errorf("invalid url %s: %v", dogURL, err)
	}
	return fmt.Sprintf("[![dog image](%s)](%s)", src, src), nil
}

func FormatURL ( dogURL string ) ( string , error ) { if dogURL == " " { return " " , errors . New ( " " ) } src , err := url . ParseRequestURI ( dogURL ) if err != nil { return " " , fmt . Errorf ( " " , dogURL , err ) } return fmt . Sprintf ( " " , src , src ) , nil }

// FormatURL will return the GH markdown to show the image for a specific dogURL.

FormatURL will return the GH markdown to show the image for a specific dogURL .


func runAndSkipJobs(c Client, pr *github.PullRequest, requestedJobs []config.Presubmit, skippedJobs []config.Presubmit, eventGUID string, elideSkippedContexts bool) error {
	if err := validateContextOverlap(requestedJobs, skippedJobs); err != nil {
		c.Logger.WithError(err).Warn("Could not run or skip requested jobs, overlapping contexts.")
		return err
	}
	runErr := RunRequested(c, pr, requestedJobs, eventGUID)
	var skipErr error
	if !elideSkippedContexts {
		skipErr = skipRequested(c, pr, skippedJobs)
	}

	return errorutil.NewAggregate(runErr, skipErr)
}

func runAndSkipJobs ( c Client , pr * github . PullRequest , requestedJobs [ ] config . Presubmit , skippedJobs [ ] config . Presubmit , eventGUID string , elideSkippedContexts bool ) error { if err := validateContextOverlap ( requestedJobs , skippedJobs ) ; err != nil { c . Logger . WithError ( err ) . Warn ( " " ) return err } runErr := RunRequested ( c , pr , requestedJobs , eventGUID ) var skipErr error if ! elideSkippedContexts { skipErr = skipRequested ( c , pr , skippedJobs ) } return errorutil . NewAggregate ( runErr , skipErr ) }

// runAndSkipJobs executes the config.Presubmits that are requested and posts skipped statuses
// for the reporting jobs that are skipped

runAndSkipJobs executes the config . Presubmits that are requested and posts skipped statuses for the reporting jobs that are skipped


func validateContextOverlap(toRun, toSkip []config.Presubmit) error {
	requestedContexts := sets.NewString()
	for _, job := range toRun {
		requestedContexts.Insert(job.Context)
	}
	skippedContexts := sets.NewString()
	for _, job := range toSkip {
		skippedContexts.Insert(job.Context)
	}
	if overlap := requestedContexts.Intersection(skippedContexts).List(); len(overlap) > 0 {
		return fmt.Errorf("the following contexts are both triggered and skipped: %s", strings.Join(overlap, ", "))
	}

	return nil
}

func validateContextOverlap ( toRun , toSkip [ ] config . Presubmit ) error { requestedContexts := sets . NewString ( ) for _ , job := range toRun { requestedContexts . Insert ( job . Context ) } skippedContexts := sets . NewString ( ) for _ , job := range toSkip { skippedContexts . Insert ( job . Context ) } if overlap := requestedContexts . Intersection ( skippedContexts ) . List ( ) ; len ( overlap ) > 0 { return fmt . Errorf ( " " , strings . Join ( overlap , " " ) ) } return nil }

// validateContextOverlap ensures that there will be no overlap in contexts between a set of jobs running and a set to skip


func RunRequested(c Client, pr *github.PullRequest, requestedJobs []config.Presubmit, eventGUID string) error {
	baseSHA, err := c.GitHubClient.GetRef(pr.Base.Repo.Owner.Login, pr.Base.Repo.Name, "heads/"+pr.Base.Ref)
	if err != nil {
		return err
	}

	var errors []error
	for _, job := range requestedJobs {
		c.Logger.Infof("Starting %s build.", job.Name)
		pj := pjutil.NewPresubmit(*pr, baseSHA, job, eventGUID)
		c.Logger.WithFields(pjutil.ProwJobFields(&pj)).Info("Creating a new prowjob.")
		if _, err := c.ProwJobClient.Create(&pj); err != nil {
			c.Logger.WithError(err).Error("Failed to create prowjob.")
			errors = append(errors, err)
		}
	}
	return errorutil.NewAggregate(errors...)
}

func RunRequested ( c Client , pr * github . PullRequest , requestedJobs [ ] config . Presubmit , eventGUID string ) error { baseSHA , err := c . GitHubClient . GetRef ( pr . Base . Repo . Owner . Login , pr . Base . Repo . Name , " " + pr . Base . Ref ) if err != nil { return err } var errors [ ] error for _ , job := range requestedJobs { c . Logger . Infof ( " " , job . Name ) pj := pjutil . NewPresubmit ( * pr , baseSHA , job , eventGUID ) c . Logger . WithFields ( pjutil . ProwJobFields ( & pj ) ) . Info ( " " ) if _ , err := c . ProwJobClient . Create ( & pj ) ; err != nil { c . Logger . WithError ( err ) . Error ( " " ) errors = append ( errors , err ) } } return errorutil . NewAggregate ( errors ... ) }

// RunRequested executes the config.Presubmits that are requested

RunRequested executes the config . Presubmits that are requested


func skipRequested(c Client, pr *github.PullRequest, skippedJobs []config.Presubmit) error {
	var errors []error
	for _, job := range skippedJobs {
		if job.SkipReport {
			continue
		}
		c.Logger.Infof("Skipping %s build.", job.Name)
		if err := c.GitHubClient.CreateStatus(pr.Base.Repo.Owner.Login, pr.Base.Repo.Name, pr.Head.SHA, skippedStatusFor(job.Context)); err != nil {
			errors = append(errors, err)
		}
	}
	return errorutil.NewAggregate(errors...)
}

func skipRequested ( c Client , pr * github . PullRequest , skippedJobs [ ] config . Presubmit ) error { var errors [ ] error for _ , job := range skippedJobs { if job . SkipReport { continue } c . Logger . Infof ( " " , job . Name ) if err := c . GitHubClient . CreateStatus ( pr . Base . Repo . Owner . Login , pr . Base . Repo . Name , pr . Head . SHA , skippedStatusFor ( job . Context ) ) ; err != nil { errors = append ( errors , err ) } } return errorutil . NewAggregate ( errors ... ) }

// skipRequested posts skipped statuses for the config.Presubmits that are requested

skipRequested posts skipped statuses for the config . Presubmits that are requested


func (l LabelEvent) Match(eventName, label string) bool {
	return eventName == "labeled" && label == l.Label
}

func ( l LabelEvent ) Match ( eventName , label string ) bool { return eventName == " " && label == l . Label }


func (u UnlabelEvent) Match(eventName, label string) bool {
	return eventName == "unlabeled" && label == u.Label
}

func ( u UnlabelEvent ) Match ( eventName , label string ) bool { return eventName == " " && label == u . Label }


func (o *GitHubOptions) AddFlags(fs *flag.FlagSet) {
	o.addFlags(true, fs)
}

func ( o * GitHubOptions ) AddFlags ( fs * flag . FlagSet ) { o . addFlags ( true , fs ) }

// AddFlags injects GitHub options into the given FlagSet.

AddFlags injects GitHub options into the given FlagSet .


func (o *GitHubOptions) AddFlagsWithoutDefaultGitHubTokenPath(fs *flag.FlagSet) {
	o.addFlags(false, fs)
}

func ( o * GitHubOptions ) AddFlagsWithoutDefaultGitHubTokenPath ( fs * flag . FlagSet ) { o . addFlags ( false , fs ) }

// AddFlagsWithoutDefaultGitHubTokenPath injects GitHub options into the given
// Flagset without setting a default for for the githubTokenPath, allowing to
// use an anonymous GitHub client

AddFlagsWithoutDefaultGitHubTokenPath injects GitHub options into the given Flagset without setting a default for for the githubTokenPath allowing to use an anonymous GitHub client


func (o *GitHubOptions) Validate(dryRun bool) error {
	for _, uri := range o.endpoint.Strings() {
		if uri == "" {
			uri = github.DefaultAPIEndpoint
		} else if _, err := url.ParseRequestURI(uri); err != nil {
			return fmt.Errorf("invalid -github-endpoint URI: %q", uri)
		}
	}

	if o.graphqlEndpoint == "" {
		o.graphqlEndpoint = github.DefaultGraphQLEndpoint
	} else if _, err := url.Parse(o.graphqlEndpoint); err != nil {
		return fmt.Errorf("invalid -github-graphql-endpoint URI: %q", o.graphqlEndpoint)
	}

	if o.deprecatedTokenFile != "" {
		o.TokenPath = o.deprecatedTokenFile
		logrus.Error("-github-token-file is deprecated and may be removed anytime after 2019-01-01.  Use -github-token-path instead.")
	}

	if o.TokenPath == "" {
		logrus.Warn("empty -github-token-path, will use anonymous github client")
	}

	return nil
}

func ( o * GitHubOptions ) Validate ( dryRun bool ) error { for _ , uri := range o . endpoint . Strings ( ) { if uri == " " { uri = github . DefaultAPIEndpoint } else if _ , err := url . ParseRequestURI ( uri ) ; err != nil { return fmt . Errorf ( " " , uri ) } } if o . graphqlEndpoint == " " { o . graphqlEndpoint = github . DefaultGraphQLEndpoint } else if _ , err := url . Parse ( o . graphqlEndpoint ) ; err != nil { return fmt . Errorf ( " " , o . graphqlEndpoint ) } if o . deprecatedTokenFile != " " { o . TokenPath = o . deprecatedTokenFile logrus . Error ( " " ) } if o . TokenPath == " " { logrus . Warn ( " " ) } return nil }


func (o *GitHubOptions) GitHubClientWithLogFields(secretAgent *secret.Agent, dryRun bool, fields logrus.Fields) (client *github.Client, err error) {
	var generator *func() []byte
	if o.TokenPath == "" {
		generatorFunc := func() []byte {
			return []byte{}
		}
		generator = &generatorFunc
	} else {
		if secretAgent == nil {
			return nil, fmt.Errorf("cannot store token from %q without a secret agent", o.TokenPath)
		}
		generatorFunc := secretAgent.GetTokenGenerator(o.TokenPath)
		generator = &generatorFunc
	}

	if dryRun {
		return github.NewDryRunClientWithFields(fields, *generator, o.graphqlEndpoint, o.endpoint.Strings()...), nil
	}
	return github.NewClientWithFields(fields, *generator, o.graphqlEndpoint, o.endpoint.Strings()...), nil
}

func ( o * GitHubOptions ) GitHubClientWithLogFields ( secretAgent * secret . Agent , dryRun bool , fields logrus . Fields ) ( client * github . Client , err error ) { var generator * func ( ) [ ] byte if o . TokenPath == " " { generatorFunc := func ( ) [ ] byte { return [ ] byte { } } generator = & generatorFunc } else { if secretAgent == nil { return nil , fmt . Errorf ( " " , o . TokenPath ) } generatorFunc := secretAgent . GetTokenGenerator ( o . TokenPath ) generator = & generatorFunc } if dryRun { return github . NewDryRunClientWithFields ( fields , * generator , o . graphqlEndpoint , o . endpoint . Strings ( ) ... ) , nil } return github . NewClientWithFields ( fields , * generator , o . graphqlEndpoint , o . endpoint . Strings ( ) ... ) , nil }

// GitHubClientWithLogFields returns a GitHub client with extra logging fields


func (o *GitHubOptions) GitHubClient(secretAgent *secret.Agent, dryRun bool) (client *github.Client, err error) {
	return o.GitHubClientWithLogFields(secretAgent, dryRun, logrus.Fields{})
}

func ( o * GitHubOptions ) GitHubClient ( secretAgent * secret . Agent , dryRun bool ) ( client * github . Client , err error ) { return o . GitHubClientWithLogFields ( secretAgent , dryRun , logrus . Fields { } ) }

// GitHubClient returns a GitHub client.


func (o *GitHubOptions) GitClient(secretAgent *secret.Agent, dryRun bool) (client *git.Client, err error) {
	client, err = git.NewClient()
	if err != nil {
		return nil, err
	}

	// We must capture the value of client here to prevent issues related
	// to the use of named return values when an error is encountered.
	// Without this, we risk a nil pointer dereference.
	defer func(client *git.Client) {
		if err != nil {
			client.Clean()
		}
	}(client)

	// Get the bot's name in order to set credentials for the Git client.
	githubClient, err := o.GitHubClient(secretAgent, dryRun)
	if err != nil {
		return nil, fmt.Errorf("error getting GitHub client: %v", err)
	}
	botName, err := githubClient.BotName()
	if err != nil {
		return nil, fmt.Errorf("error getting bot name: %v", err)
	}
	client.SetCredentials(botName, secretAgent.GetTokenGenerator(o.TokenPath))

	return client, nil
}


func toMap(g *calculation.CoverageList) map[string]calculation.Coverage {
	m := make(map[string]calculation.Coverage)
	for _, cov := range g.Group {
		m[cov.Name] = cov
	}
	return m
}

func toMap ( g * calculation . CoverageList ) map [ string ] calculation . Coverage { m := make ( map [ string ] calculation . Coverage ) for _ , cov := range g . Group { m [ cov . Name ] = cov } return m }

// toMap returns maps the file name to its coverage for faster retrieval
// & membership check

toMap returns maps the file name to its coverage for faster retrieval & membership check


func findChanges(baseList *calculation.CoverageList, newList *calculation.CoverageList) []*coverageChange {
	var changes []*coverageChange
	baseFilesMap := toMap(baseList)
	for _, newCov := range newList.Group {
		baseCov, ok := baseFilesMap[newCov.Name]
		var baseRatio float32
		if !ok {
			baseRatio = -1
		} else {
			baseRatio = baseCov.Ratio()
		}
		newRatio := newCov.Ratio()
		if isChangeSignificant(baseRatio, newRatio) {
			changes = append(changes, &coverageChange{
				name:      newCov.Name,
				baseRatio: baseRatio,
				newRatio:  newRatio,
			})
		}
	}
	return changes
}

func findChanges ( baseList * calculation . CoverageList , newList * calculation . CoverageList ) [ ] * coverageChange { var changes [ ] * coverageChange baseFilesMap := toMap ( baseList ) for _ , newCov := range newList . Group { baseCov , ok := baseFilesMap [ newCov . Name ] var baseRatio float32 if ! ok { baseRatio = - 1 } else { baseRatio = baseCov . Ratio ( ) } newRatio := newCov . Ratio ( ) if isChangeSignificant ( baseRatio , newRatio ) { changes = append ( changes , & coverageChange { name : newCov . Name , baseRatio : baseRatio , newRatio : newRatio , } ) } } return changes }

// findChanges compares the newList of coverage against the base list and returns the result


func (config *MySQLConfig) CreateDatabase() (*gorm.DB, error) {
	db, err := gorm.Open("mysql", config.getDSN(""))
	if err != nil {
		return nil, err
	}

	db.Exec(fmt.Sprintf("CREATE DATABASE IF NOT EXISTS %v;", config.Db))
	db.Close()

	db, err = gorm.Open("mysql", config.getDSN(config.Db))
	err = db.AutoMigrate(&Assignee{}, &Issue{}, &IssueEvent{}, &Label{}, &Comment{}).Error
	if err != nil {
		return nil, err
	}

	return db, nil
}

func ( config * MySQLConfig ) CreateDatabase ( ) ( * gorm . DB , error ) { db , err := gorm . Open ( " " , config . getDSN ( " " ) ) if err != nil { return nil , err } db . Exec ( fmt . Sprintf ( " " , config . Db ) ) db . Close ( ) db , err = gorm . Open ( " " , config . getDSN ( config . Db ) ) err = db . AutoMigrate ( & Assignee { } , & Issue { } , & IssueEvent { } , & Label { } , & Comment { } ) . Error if err != nil { return nil , err } return db , nil }


func (c *Client) ShouldReport(pj *v1.ProwJob) bool {

	if !pj.Spec.Report {
		// Respect report field
		return false
	}

	if pj.Spec.Type != v1.PresubmitJob && pj.Spec.Type != v1.PostsubmitJob {
		// Report presubmit and postsubmit github jobs for github reporter
		return false
	}

	if c.reportAgent != "" && pj.Spec.Agent != c.reportAgent {
		// Only report for specified agent
		return false
	}

	return true
}

// ShouldReport returns if this prowjob should be reported by the github reporter


func (c *Client) Report(pj *v1.ProwJob) ([]*v1.ProwJob, error) {
	// TODO(krzyzacy): ditch ReportTemplate, and we can drop reference to config.Getter
	return []*v1.ProwJob{pj}, report.Report(c.gc, c.config().Plank.ReportTemplate, *pj, c.config().GitHubReporter.JobTypesToReport)
}


func (s *Set) MarkComplete() int {
	var gone []string
	for key := range s.firstSeen {
		if !s.marked[key] {
			gone = append(gone, key)
		}
	}

	for _, key := range gone {
		klog.V(1).Infof("%s: deleted since last run", key)
		delete(s.firstSeen, key)
	}

	if len(s.swept) > 0 {
		klog.Errorf("%d resources swept: %v", len(s.swept), s.swept)
	}

	return len(s.swept)
}

func ( s * Set ) MarkComplete ( ) int { var gone [ ] string for key := range s . firstSeen { if ! s . marked [ key ] { gone = append ( gone , key ) } } for _ , key := range gone { klog . V ( 1 ) . Infof ( " " , key ) delete ( s . firstSeen , key ) } if len ( s . swept ) > 0 { klog . Errorf ( " " , len ( s . swept ) , s . swept ) } return len ( s . swept ) }

// MarkComplete figures out which ARNs were in previous passes but not
// this one, and eliminates them. It should only be run after all
// resources have been marked.

MarkComplete figures out which ARNs were in previous passes but not this one and eliminates them . It should only be run after all resources have been marked .


func NewJobAgent(kc serviceClusterClient, plClients map[string]PodLogClient, cfg config.Getter) *JobAgent {
	return &JobAgent{
		kc:     kc,
		pkcs:   plClients,
		config: cfg,
	}
}

func NewJobAgent ( kc serviceClusterClient , plClients map [ string ] PodLogClient , cfg config . Getter ) * JobAgent { return & JobAgent { kc : kc , pkcs : plClients , config : cfg , } }

// NewJobAgent is a JobAgent constructor.


func (ja *JobAgent) Start() {
	ja.tryUpdate()
	go func() {
		t := time.Tick(period)
		for range t {
			ja.tryUpdate()
		}
	}()
}

func ( ja * JobAgent ) Start ( ) { ja . tryUpdate ( ) go func ( ) { t := time . Tick ( period ) for range t { ja . tryUpdate ( ) } } ( ) }

// Start will start the job and periodically update it.

Start will start the job and periodically update it .


func (ja *JobAgent) Jobs() []Job {
	ja.mut.Lock()
	defer ja.mut.Unlock()
	res := make([]Job, len(ja.jobs))
	copy(res, ja.jobs)
	return res
}

func ( ja * JobAgent ) Jobs ( ) [ ] Job { ja . mut . Lock ( ) defer ja . mut . Unlock ( ) res := make ( [ ] Job , len ( ja . jobs ) ) copy ( res , ja . jobs ) return res }

// Jobs returns a thread-safe snapshot of the current job state.

Jobs returns a thread - safe snapshot of the current job state .


func (ja *JobAgent) ProwJobs() []prowapi.ProwJob {
	ja.mut.Lock()
	defer ja.mut.Unlock()
	res := make([]prowapi.ProwJob, len(ja.prowJobs))
	copy(res, ja.prowJobs)
	return res
}

func ( ja * JobAgent ) ProwJobs ( ) [ ] prowapi . ProwJob { ja . mut . Lock ( ) defer ja . mut . Unlock ( ) res := make ( [ ] prowapi . ProwJob , len ( ja . prowJobs ) ) copy ( res , ja . prowJobs ) return res }

// ProwJobs returns a thread-safe snapshot of the current prow jobs.

ProwJobs returns a thread - safe snapshot of the current prow jobs .


func (ja *JobAgent) GetProwJob(job, id string) (prowapi.ProwJob, error) {
	if ja == nil {
		return prowapi.ProwJob{}, fmt.Errorf("Prow job agent doesn't exist (are you running locally?)")
	}
	var j prowapi.ProwJob
	ja.mut.Lock()
	idMap, ok := ja.jobsIDMap[job]
	if ok {
		j, ok = idMap[id]
	}
	ja.mut.Unlock()
	if !ok {
		return prowapi.ProwJob{}, errProwjobNotFound
	}
	return j, nil
}

func ( ja * JobAgent ) GetProwJob ( job , id string ) ( prowapi . ProwJob , error ) { if ja == nil { return prowapi . ProwJob { } , fmt . Errorf ( " " ) } var j prowapi . ProwJob ja . mut . Lock ( ) idMap , ok := ja . jobsIDMap [ job ] if ok { j , ok = idMap [ id ] } ja . mut . Unlock ( ) if ! ok { return prowapi . ProwJob { } , errProwjobNotFound } return j , nil }

// GetProwJob finds the corresponding Prowjob resource from the provided job name and build ID


func (ja *JobAgent) GetJobLog(job, id string) ([]byte, error) {
	j, err := ja.GetProwJob(job, id)
	if err != nil {
		return nil, fmt.Errorf("error getting prowjob: %v", err)
	}
	if j.Spec.Agent == prowapi.KubernetesAgent {
		client, ok := ja.pkcs[j.ClusterAlias()]
		if !ok {
			return nil, fmt.Errorf("cannot get logs for prowjob %q with agent %q: unknown cluster alias %q", j.ObjectMeta.Name, j.Spec.Agent, j.ClusterAlias())
		}
		return client.GetLogs(j.Status.PodName, &coreapi.PodLogOptions{Container: kube.TestContainerName})
	}
	for _, agentToTmpl := range ja.config().Deck.ExternalAgentLogs {
		if agentToTmpl.Agent != string(j.Spec.Agent) {
			continue
		}
		if !agentToTmpl.Selector.Matches(labels.Set(j.ObjectMeta.Labels)) {
			continue
		}
		var b bytes.Buffer
		if err := agentToTmpl.URLTemplate.Execute(&b, &j); err != nil {
			return nil, fmt.Errorf("cannot execute URL template for prowjob %q with agent %q: %v", j.ObjectMeta.Name, j.Spec.Agent, err)
		}
		resp, err := http.Get(b.String())
		if err != nil {
			return nil, err
		}
		defer resp.Body.Close()
		return ioutil.ReadAll(resp.Body)

	}
	return nil, fmt.Errorf("cannot get logs for prowjob %q with agent %q: the agent is missing from the prow config file", j.ObjectMeta.Name, j.Spec.Agent)
}

func ( ja * JobAgent ) GetJobLog ( job , id string ) ( [ ] byte , error ) { j , err := ja . GetProwJob ( job , id ) if err != nil { return nil , fmt . Errorf ( " " , err ) } if j . Spec . Agent == prowapi . KubernetesAgent { client , ok := ja . pkcs [ j . ClusterAlias ( ) ] if ! ok { return nil , fmt . Errorf ( " " , j . ObjectMeta . Name , j . Spec . Agent , j . ClusterAlias ( ) ) } return client . GetLogs ( j . Status . PodName , & coreapi . PodLogOptions { Container : kube . TestContainerName } ) } for _ , agentToTmpl := range ja . config ( ) . Deck . ExternalAgentLogs { if agentToTmpl . Agent != string ( j . Spec . Agent ) { continue } if ! agentToTmpl . Selector . Matches ( labels . Set ( j . ObjectMeta . Labels ) ) { continue } var b bytes . Buffer if err := agentToTmpl . URLTemplate . Execute ( & b , & j ) ; err != nil { return nil , fmt . Errorf ( " " , j . ObjectMeta . Name , j . Spec . Agent , err ) } resp , err := http . Get ( b . String ( ) ) if err != nil { return nil , err } defer resp . Body . Close ( ) return ioutil . ReadAll ( resp . Body ) } return nil , fmt . Errorf ( " " , j . ObjectMeta . Name , j . Spec . Agent ) }

// GetJobLog returns the job logs, works for both kubernetes and jenkins agent types.

GetJobLog returns the job logs works for both kubernetes and jenkins agent types .


func unionStrings(parent, child []string) []string {
	if child == nil {
		return parent
	}
	if parent == nil {
		return child
	}
	s := sets.NewString(parent...)
	s.Insert(child...)
	return s.List()
}

func unionStrings ( parent , child [ ] string ) [ ] string { if child == nil { return parent } if parent == nil { return child } s := sets . NewString ( parent ... ) s . Insert ( child ... ) return s . List ( ) }

// unionStrings merges the parent and child items together


func (p Policy) Apply(child Policy) Policy {
	return Policy{
		Protect:                    selectBool(p.Protect, child.Protect),
		RequiredStatusChecks:       mergeContextPolicy(p.RequiredStatusChecks, child.RequiredStatusChecks),
		Admins:                     selectBool(p.Admins, child.Admins),
		Restrictions:               mergeRestrictions(p.Restrictions, child.Restrictions),
		RequiredPullRequestReviews: mergeReviewPolicy(p.RequiredPullRequestReviews, child.RequiredPullRequestReviews),
	}
}

func ( p Policy ) Apply ( child Policy ) Policy { return Policy { Protect : selectBool ( p . Protect , child . Protect ) , RequiredStatusChecks : mergeContextPolicy ( p . RequiredStatusChecks , child . RequiredStatusChecks ) , Admins : selectBool ( p . Admins , child . Admins ) , Restrictions : mergeRestrictions ( p . Restrictions , child . Restrictions ) , RequiredPullRequestReviews : mergeReviewPolicy ( p . RequiredPullRequestReviews , child . RequiredPullRequestReviews ) , } }

// Apply returns a policy that merges the child into the parent


func (bp BranchProtection) GetOrg(name string) *Org {
	o, ok := bp.Orgs[name]
	if ok {
		o.Policy = bp.Apply(o.Policy)
	} else {
		o.Policy = bp.Policy
	}
	return &o
}

func ( bp BranchProtection ) GetOrg ( name string ) * Org { o , ok := bp . Orgs [ name ] if ok { o . Policy = bp . Apply ( o . Policy ) } else { o . Policy = bp . Policy } return & o }

// GetOrg returns the org config after merging in any global policies.

GetOrg returns the org config after merging in any global policies .


func (o Org) GetRepo(name string) *Repo {
	r, ok := o.Repos[name]
	if ok {
		r.Policy = o.Apply(r.Policy)
	} else {
		r.Policy = o.Policy
	}
	return &r
}

func ( o Org ) GetRepo ( name string ) * Repo { r , ok := o . Repos [ name ] if ok { r . Policy = o . Apply ( r . Policy ) } else { r . Policy = o . Policy } return & r }

// GetRepo returns the repo config after merging in any org policies.

GetRepo returns the repo config after merging in any org policies .


func (r Repo) GetBranch(name string) (*Branch, error) {
	b, ok := r.Branches[name]
	if ok {
		b.Policy = r.Apply(b.Policy)
		if b.Protect == nil {
			return nil, errors.New("defined branch policies must set protect")
		}
	} else {
		b.Policy = r.Policy
	}
	return &b, nil
}

func ( r Repo ) GetBranch ( name string ) ( * Branch , error ) { b , ok := r . Branches [ name ] if ok { b . Policy = r . Apply ( b . Policy ) if b . Protect == nil { return nil , errors . New ( " " ) } } else { b . Policy = r . Policy } return & b , nil }

// GetBranch returns the branch config after merging in any repo policies.

GetBranch returns the branch config after merging in any repo policies .


func (c *Config) GetPolicy(org, repo, branch string, b Branch) (*Policy, error) {
	policy := b.Policy

	// Automatically require contexts from prow which must always be present
	if prowContexts, _, _ := BranchRequirements(org, repo, branch, c.Presubmits); len(prowContexts) > 0 {
		// Error if protection is disabled
		if policy.Protect != nil && !*policy.Protect {
			return nil, fmt.Errorf("required prow jobs require branch protection")
		}
		ps := Policy{
			RequiredStatusChecks: &ContextPolicy{
				Contexts: prowContexts,
			},
		}
		// Require protection by default if ProtectTested is true
		if c.BranchProtection.ProtectTested {
			yes := true
			ps.Protect = &yes
		}
		policy = policy.Apply(ps)
	}

	if policy.Protect != nil && !*policy.Protect {
		// Ensure that protection is false => no protection settings
		var old *bool
		old, policy.Protect = policy.Protect, old
		switch {
		case policy.defined() && c.BranchProtection.AllowDisabledPolicies:
			logrus.Warnf("%s/%s=%s defines a policy but has protect: false", org, repo, branch)
			policy = Policy{
				Protect: policy.Protect,
			}
		case policy.defined():
			return nil, fmt.Errorf("%s/%s=%s defines a policy, which requires protect: true", org, repo, branch)
		}
		policy.Protect = old
	}

	if !policy.defined() {
		return nil, nil
	}
	return &policy, nil
}

// GetPolicy returns the protection policy for the branch, after merging in presubmits.

GetPolicy returns the protection policy for the branch after merging in presubmits .


func UpdateIssueEvents(issueID int, db *gorm.DB, client ClientInterface) {
	latest, err := findLatestEvent(issueID, db, client.RepositoryName())
	if err != nil {
		glog.Error("Failed to find last event: ", err)
		return
	}
	c := make(chan *github.IssueEvent, 500)

	go client.FetchIssueEvents(issueID, latest, c)
	for event := range c {
		eventOrm, err := NewIssueEvent(event, issueID, client.RepositoryName())
		if err != nil {
			glog.Error("Failed to create issue-event", err)
		}
		db.Create(eventOrm)
	}
}

func UpdateIssueEvents ( issueID int , db * gorm . DB , client ClientInterface ) { latest , err := findLatestEvent ( issueID , db , client . RepositoryName ( ) ) if err != nil { glog . Error ( " " , err ) return } c := make ( chan * github . IssueEvent , 500 ) go client . FetchIssueEvents ( issueID , latest , c ) for event := range c { eventOrm , err := NewIssueEvent ( event , issueID , client . RepositoryName ( ) ) if err != nil { glog . Error ( " " , err ) } db . Create ( eventOrm ) } }

// UpdateIssueEvents fetches all events until we find the most recent we
// have in db, and saves everything in database

UpdateIssueEvents fetches all events until we find the most recent we have in db and saves everything in database


func (c *controller) enqueueKey(ctx string, obj interface{}) {
	switch o := obj.(type) {
	case *prowjobv1.ProwJob:
		c.workqueue.AddRateLimited(toKey(ctx, o.Spec.Namespace, o.Name))
	case *buildv1alpha1.Build:
		c.workqueue.AddRateLimited(toKey(ctx, o.Namespace, o.Name))
	default:
		logrus.Warnf("cannot enqueue unknown type %T: %v", o, obj)
		return
	}
}

func ( c * controller ) enqueueKey ( ctx string , obj interface { } ) { switch o := obj . ( type ) { case * prowjobv1 . ProwJob : c . workqueue . AddRateLimited ( toKey ( ctx , o . Spec . Namespace , o . Name ) ) case * buildv1alpha1 . Build : c . workqueue . AddRateLimited ( toKey ( ctx , o . Namespace , o . Name ) ) default : logrus . Warnf ( " " , o , obj ) return } }

// enqueueKey schedules an item for reconciliation.

enqueueKey schedules an item for reconciliation .


func prowJobStatus(bs buildv1alpha1.BuildStatus) (prowjobv1.ProwJobState, string) {
	started := bs.StartTime
	finished := bs.CompletionTime
	pcond := bs.GetCondition(buildv1alpha1.BuildSucceeded)
	if pcond == nil {
		if !finished.IsZero() {
			return prowjobv1.ErrorState, descMissingCondition
		}
		return prowjobv1.TriggeredState, descScheduling
	}
	cond := *pcond
	switch {
	case cond.Status == coreapi.ConditionTrue:
		return prowjobv1.SuccessState, description(cond, descSucceeded)
	case cond.Status == coreapi.ConditionFalse:
		return prowjobv1.FailureState, description(cond, descFailed)
	case started.IsZero():
		return prowjobv1.TriggeredState, description(cond, descInitializing)
	case cond.Status == coreapi.ConditionUnknown, finished.IsZero():
		return prowjobv1.PendingState, description(cond, descRunning)
	}
	logrus.Warnf("Unknown condition %#v", cond)
	return prowjobv1.ErrorState, description(cond, descUnknown) // shouldn't happen
}

func
prowJobStatus
(
bs
buildv1alpha1
.
BuildStatus
)
(
prowjobv1
.
ProwJobState
,
string
)
{
started
:=
bs
.
StartTime
finished
:=
bs
.
CompletionTime
pcond
:=
bs
.
GetCondition
(
buildv1alpha1
.
BuildSucceeded
)
if
pcond
==
{
if
!
finished
.
IsZero
(
)
{
return
prowjobv1
.
ErrorState
,
descMissingCondition
}
return
prowjobv1
.
TriggeredState
,
descScheduling
}
cond
:=
*
pcond
switch
{
case
cond
.
Status
==
coreapi
.
ConditionTrue
:
return
prowjobv1
.
SuccessState
,
description
(
cond
,
descSucceeded
)
case
cond
.
Status
==
coreapi
.
ConditionFalse
:
return
prowjobv1
.
FailureState
,
description
(
cond
,
descFailed
)
case
started
.
IsZero
(
)
:
return
prowjobv1
.
TriggeredState
,
description
(
cond
,
descInitializing
)
case
cond
.
Status
==
coreapi
.
ConditionUnknown
,
finished
.
IsZero
(
)
:
return
prowjobv1
.
PendingState
,
description
(
cond
,
descRunning
)
}
logrus
.
Warnf
(
"
"
,
cond
)
return
prowjobv1
.
ErrorState
,
description
(
cond
,
descUnknown
)
// shouldn't happen
}

// prowJobStatus returns the desired state and description based on the build status.

prowJobStatus returns the desired state and description based on the build status .


func buildEnv(pj prowjobv1.ProwJob, buildID string) (map[string]string, error) {
	return downwardapi.EnvForSpec(downwardapi.NewJobSpec(pj.Spec, buildID, pj.Name))
}

func buildEnv ( pj prowjobv1 . ProwJob , buildID string ) ( map [ string ] string , error ) { return downwardapi . EnvForSpec ( downwardapi . NewJobSpec ( pj . Spec , buildID , pj . Name ) ) }

// buildEnv constructs the environment map for the job


func defaultArguments(t *buildv1alpha1.TemplateInstantiationSpec, rawEnv map[string]string) {
	keys := sets.String{}
	for _, arg := range t.Arguments {
		keys.Insert(arg.Name)
	}
	for _, k := range sets.StringKeySet(rawEnv).List() { // deterministic ordering
		if keys.Has(k) {
			continue
		}
		t.Arguments = append(t.Arguments, buildv1alpha1.ArgumentSpec{Name: k, Value: rawEnv[k]})
	}
}

func
defaultArguments
(
t
*
buildv1alpha1
.
TemplateInstantiationSpec
,
rawEnv
map
[
string
]
string
)
{
keys
:=
sets
.
String
{
}
for
_
,
arg
:=
range
t
.
Arguments
{
keys
.
Insert
(
arg
.
Name
)
}
for
_
,
k
:=
range
sets
.
StringKeySet
(
rawEnv
)
.
List
(
)
{
// deterministic ordering
if
keys
.
Has
(
k
)
{
continue
}
t
.
Arguments
=
append
(
t
.
Arguments
,
buildv1alpha1
.
ArgumentSpec
{
Name
:
k
,
Value
:
rawEnv
[
k
]
}
)
}
}

// defaultArguments will append each arg to the template, except where the argument name is already defined.

defaultArguments will append each arg to the template except where the argument name is already defined .


func defaultEnv(c *coreapi.Container, rawEnv map[string]string) {
	keys := sets.String{}
	for _, arg := range c.Env {
		keys.Insert(arg.Name)
	}
	for _, k := range sets.StringKeySet(rawEnv).List() { // deterministic ordering
		if keys.Has(k) {
			continue
		}
		c.Env = append(c.Env, coreapi.EnvVar{Name: k, Value: rawEnv[k]})
	}
}

func
defaultEnv
(
c
*
coreapi
.
Container
,
rawEnv
map
[
string
]
string
)
{
keys
:=
sets
.
String
{
}
for
_
,
arg
:=
range
c
.
Env
{
keys
.
Insert
(
arg
.
Name
)
}
for
_
,
k
:=
range
sets
.
StringKeySet
(
rawEnv
)
.
List
(
)
{
// deterministic ordering
if
keys
.
Has
(
k
)
{
continue
}
c
.
Env
=
append
(
c
.
Env
,
coreapi
.
EnvVar
{
Name
:
k
,
Value
:
rawEnv
[
k
]
}
)
}
}

// defaultEnv adds the map of environment variables to the container, except keys already defined.

defaultEnv adds the map of environment variables to the container except keys already defined .


func injectSource(b *buildv1alpha1.Build, pj prowjobv1.ProwJob) (bool, error) {
	if b.Spec.Source != nil {
		return false, nil
	}
	srcContainer, refs, cloneVolumes, err := decorate.CloneRefs(pj, codeMount, logMount)
	if err != nil {
		return false, fmt.Errorf("clone source error: %v", err)
	}
	if srcContainer == nil {
		return false, nil
	} else {
		srcContainer.Name = "" // knative-build requirement
	}

	b.Spec.Source = &buildv1alpha1.SourceSpec{
		Custom: srcContainer,
	}
	b.Spec.Volumes = append(b.Spec.Volumes, cloneVolumes...)

	wd := workDir(refs[0])
	// Inject correct working directory
	for i := range b.Spec.Steps {
		if b.Spec.Steps[i].WorkingDir != "" {
			continue
		}
		b.Spec.Steps[i].WorkingDir = wd.Value
	}
	if b.Spec.Template != nil {
		// Best we can do for a template is to set WORKDIR
		b.Spec.Template.Arguments = append(b.Spec.Template.Arguments, wd)
	}

	return true, nil
}

// injectSource adds the custom source container to call clonerefs correctly.
//
// Returns true if it added this container
//
// Does nothing if the build spec predefines Source

injectSource adds the custom source container to call clonerefs correctly . Returns true if it added this container Does nothing if the build spec predefines Source


func injectedSteps(encodedJobSpec string, dc prowjobv1.DecorationConfig, injectedSource bool, toolsMount coreapi.VolumeMount, entries []wrapper.Options) ([]coreapi.Container, *coreapi.Container, *coreapi.Volume, error) {
	gcsVol, gcsMount, gcsOptions := decorate.GCSOptions(dc)

	sidecar, err := decorate.Sidecar(dc.UtilityImages.Sidecar, gcsOptions, gcsMount, logMount, encodedJobSpec, decorate.RequirePassingEntries, entries...)
	if err != nil {
		return nil, nil, nil, fmt.Errorf("inject sidecar: %v", err)
	}

	var cloneLogMount *coreapi.VolumeMount
	if injectedSource {
		cloneLogMount = &logMount
	}
	initUpload, err := decorate.InitUpload(dc.UtilityImages.InitUpload, gcsOptions, gcsMount, cloneLogMount, encodedJobSpec)
	if err != nil {
		return nil, nil, nil, fmt.Errorf("inject initupload: %v", err)
	}

	placer := decorate.PlaceEntrypoint(dc.UtilityImages.Entrypoint, toolsMount)

	return []coreapi.Container{placer, *initUpload}, sidecar, &gcsVol, nil
}

func injectedSteps ( encodedJobSpec string , dc prowjobv1 . DecorationConfig , injectedSource bool , toolsMount coreapi . VolumeMount , entries [ ] wrapper . Options ) ( [ ] coreapi . Container , * coreapi . Container , * coreapi . Volume , error ) { gcsVol , gcsMount , gcsOptions := decorate . GCSOptions ( dc ) sidecar , err := decorate . Sidecar ( dc . UtilityImages . Sidecar , gcsOptions , gcsMount , logMount , encodedJobSpec , decorate . RequirePassingEntries , entries ... ) if err != nil { return nil , nil , nil , fmt . Errorf ( " " , err ) } var cloneLogMount * coreapi . VolumeMount if injectedSource { cloneLogMount = & logMount } initUpload , err := decorate . InitUpload ( dc . UtilityImages . InitUpload , gcsOptions , gcsMount , cloneLogMount , encodedJobSpec ) if err != nil { return nil , nil , nil , fmt . Errorf ( " " , err ) } placer := decorate . PlaceEntrypoint ( dc . UtilityImages . Entrypoint , toolsMount ) return [ ] coreapi . Container { placer , * initUpload } , sidecar , & gcsVol , nil }

// injectedSteps returns initial containers, a final container and an additional volume.

injectedSteps returns initial containers a final container and an additional volume .


func determineTimeout(spec *buildv1alpha1.BuildSpec, dc *prowjobv1.DecorationConfig, defaultTimeout time.Duration) time.Duration {
	switch {
	case spec.Timeout != nil:
		return spec.Timeout.Duration
	case dc != nil && dc.Timeout.Duration > 0:
		return dc.Timeout.Duration
	default:
		return defaultTimeout
	}
}

func determineTimeout ( spec * buildv1alpha1 . BuildSpec , dc * prowjobv1 . DecorationConfig , defaultTimeout time . Duration ) time . Duration { switch { case spec . Timeout != nil : return spec . Timeout . Duration case dc != nil && dc . Timeout . Duration > 0 : return dc . Timeout . Duration default : return defaultTimeout } }

// determineTimeout decides the timeout value used for build


func makeBuild(pj prowjobv1.ProwJob, defaultTimeout time.Duration) (*buildv1alpha1.Build, error) {
	if pj.Spec.BuildSpec == nil {
		return nil, errors.New("nil BuildSpec in spec")
	}
	buildID := pj.Status.BuildID
	if buildID == "" {
		return nil, errors.New("empty BuildID in status")
	}
	b := buildv1alpha1.Build{
		ObjectMeta: buildMeta(pj),
		Spec:       *pj.Spec.BuildSpec.DeepCopy(),
	}
	rawEnv, err := buildEnv(pj, buildID)
	if err != nil {
		return nil, fmt.Errorf("environment error: %v", err)
	}
	injectEnvironment(&b, rawEnv)
	injectedSource, err := injectSource(&b, pj)
	if err != nil {
		return nil, fmt.Errorf("inject source: %v", err)
	}
	injectTimeout(&b.Spec, pj.Spec.DecorationConfig, defaultTimeout)
	if pj.Spec.DecorationConfig != nil {
		encodedJobSpec := rawEnv[downwardapi.JobSpecEnv]
		err = decorateBuild(&b.Spec, encodedJobSpec, *pj.Spec.DecorationConfig, injectedSource)
		if err != nil {
			return nil, fmt.Errorf("decorate build: %v", err)
		}
	}

	return &b, nil
}

func makeBuild ( pj prowjobv1 . ProwJob , defaultTimeout time . Duration ) ( * buildv1alpha1 . Build , error ) { if pj . Spec . BuildSpec == nil { return nil , errors . New ( " " ) } buildID := pj . Status . BuildID if buildID == " " { return nil , errors . New ( " " ) } b := buildv1alpha1 . Build { ObjectMeta : buildMeta ( pj ) , Spec : * pj . Spec . BuildSpec . DeepCopy ( ) , } rawEnv , err := buildEnv ( pj , buildID ) if err != nil { return nil , fmt . Errorf ( " " , err ) } injectEnvironment ( & b , rawEnv ) injectedSource , err := injectSource ( & b , pj ) if err != nil { return nil , fmt . Errorf ( " " , err ) } injectTimeout ( & b . Spec , pj . Spec . DecorationConfig , defaultTimeout ) if pj . Spec . DecorationConfig != nil { encodedJobSpec := rawEnv [ downwardapi . JobSpecEnv ] err = decorateBuild ( & b . Spec , encodedJobSpec , * pj . Spec . DecorationConfig , injectedSource ) if err != nil { return nil , fmt . Errorf ( " " , err ) } } return & b , nil }

// makeBuild creates a build from the prowjob, using the prowjob's buildspec.

makeBuild creates a build from the prowjob using the prowjob s buildspec .


func newLabels(issueID int, gLabels []github.Label, repository string) ([]sql.Label, error) {
	labels := []sql.Label{}
	repository = strings.ToLower(repository)

	for _, label := range gLabels {
		if label.Name == nil {
			return nil, fmt.Errorf("Label is missing name field")
		}
		labels = append(labels, sql.Label{
			IssueID:    strconv.Itoa(issueID),
			Name:       *label.Name,
			Repository: repository,
		})
	}

	return labels, nil
}

func newLabels ( issueID int , gLabels [ ] github . Label , repository string ) ( [ ] sql . Label , error ) { labels := [ ] sql . Label { } repository = strings . ToLower ( repository ) for _ , label := range gLabels { if label . Name == nil { return nil , fmt . Errorf ( " " ) } labels = append ( labels , sql . Label { IssueID : strconv . Itoa ( issueID ) , Name : * label . Name , Repository : repository , } ) } return labels , nil }

// newLabels creates a new Label for each label in the issue


func newAssignees(issueID int, gAssignees []*github.User, repository string) ([]sql.Assignee, error) {
	assignees := []sql.Assignee{}
	repository = strings.ToLower(repository)

	for _, assignee := range gAssignees {
		if assignee != nil && assignee.Login == nil {
			return nil, fmt.Errorf("Assignee is missing Login field")
		}
		assignees = append(assignees, sql.Assignee{
			IssueID:    strconv.Itoa(issueID),
			Name:       *assignee.Login,
			Repository: repository,
		})
	}

	return assignees, nil
}

func newAssignees ( issueID int , gAssignees [ ] * github . User , repository string ) ( [ ] sql . Assignee , error ) { assignees := [ ] sql . Assignee { } repository = strings . ToLower ( repository ) for _ , assignee := range gAssignees { if assignee != nil && assignee . Login == nil { return nil , fmt . Errorf ( " " ) } assignees = append ( assignees , sql . Assignee { IssueID : strconv . Itoa ( issueID ) , Name : * assignee . Login , Repository : repository , } ) } return assignees , nil }

// newAssignees creates a new Label for each label in the issue


func NewIssueComment(issueID int, gComment *github.IssueComment, repository string) (*sql.Comment, error) {
	if gComment.ID == nil ||
		gComment.Body == nil ||
		gComment.CreatedAt == nil ||
		gComment.UpdatedAt == nil {
		return nil, fmt.Errorf("IssueComment is missing mandatory field: %s", gComment)
	}

	var login string
	if gComment.User != nil && gComment.User.Login != nil {
		login = *gComment.User.Login
	}

	return &sql.Comment{
		ID:               itoa(*gComment.ID),
		IssueID:          strconv.Itoa(issueID),
		Body:             *gComment.Body,
		User:             login,
		CommentCreatedAt: *gComment.CreatedAt,
		CommentUpdatedAt: *gComment.UpdatedAt,
		PullRequest:      false,
		Repository:       strings.ToLower(repository),
	}, nil
}

func NewIssueComment ( issueID int , gComment * github . IssueComment , repository string ) ( * sql . Comment , error ) { if gComment . ID == nil || gComment . Body == nil || gComment . CreatedAt == nil || gComment . UpdatedAt == nil { return nil , fmt . Errorf ( " " , gComment ) } var login string if gComment . User != nil && gComment . User . Login != nil { login = * gComment . User . Login } return & sql . Comment { ID : itoa ( * gComment . ID ) , IssueID : strconv . Itoa ( issueID ) , Body : * gComment . Body , User : login , CommentCreatedAt : * gComment . CreatedAt , CommentUpdatedAt : * gComment . UpdatedAt , PullRequest : false , Repository : strings . ToLower ( repository ) , } , nil }

// NewIssueComment creates a Comment from a github.IssueComment

NewIssueComment creates a Comment from a github . IssueComment


func messageFilter(lastUpdate time.Time, change client.ChangeInfo, presubmits []config.Presubmit) (pjutil.Filter, error) {
	var filters []pjutil.Filter
	currentRevision := change.Revisions[change.CurrentRevision].Number
	for _, message := range change.Messages {
		messageTime := message.Date.Time
		if message.RevisionNumber != currentRevision || !messageTime.After(lastUpdate) {
			continue
		}

		if !pjutil.TestAllRe.MatchString(message.Message) {
			for _, presubmit := range presubmits {
				if presubmit.TriggerMatches(message.Message) {
					logrus.Infof("Change %d: Comment %s matches triggering regex, for %s.", change.Number, message.Message, presubmit.Name)
					filters = append(filters, pjutil.CommandFilter(message.Message))
				}
			}
		} else {
			filters = append(filters, pjutil.TestAllFilter())
		}

	}
	return pjutil.AggregateFilter(filters), nil
}

func messageFilter ( lastUpdate time . Time , change client . ChangeInfo , presubmits [ ] config . Presubmit ) ( pjutil . Filter , error ) { var filters [ ] pjutil . Filter currentRevision := change . Revisions [ change . CurrentRevision ] . Number for _ , message := range change . Messages { messageTime := message . Date . Time if message . RevisionNumber != currentRevision || ! messageTime . After ( lastUpdate ) { continue } if ! pjutil . TestAllRe . MatchString ( message . Message ) { for _ , presubmit := range presubmits { if presubmit . TriggerMatches ( message . Message ) { logrus . Infof ( " " , change . Number , message . Message , presubmit . Name ) filters = append ( filters , pjutil . CommandFilter ( message . Message ) ) } } } else { filters = append ( filters , pjutil . TestAllFilter ( ) ) } } return pjutil . AggregateFilter ( filters ) , nil }

// messageFilter builds a filter for jobs based on the messageBody matching the trigger regex of the jobs.

messageFilter builds a filter for jobs based on the messageBody matching the trigger regex of the jobs .


func (jb *Build) IsSuccess() bool {
	return jb.Result != nil && *jb.Result == success
}

func ( jb * Build ) IsSuccess ( ) bool { return jb . Result != nil && * jb . Result == success }


func (jb *Build) IsFailure() bool {
	return jb.Result != nil && (*jb.Result == failure || *jb.Result == unstable)
}

func ( jb * Build ) IsFailure ( ) bool { return jb . Result != nil && ( * jb . Result == failure || * jb . Result == unstable ) }

// IsFailure means the job completed with problems.

IsFailure means the job completed with problems .


func (jb *Build) IsAborted() bool {
	return jb.Result != nil && *jb.Result == aborted
}

func ( jb * Build ) IsAborted ( ) bool { return jb . Result != nil && * jb . Result == aborted }

// IsAborted means something stopped the job before it could finish.

IsAborted means something stopped the job before it could finish .


func (jb *Build) ProwJobID() string {
	for _, action := range jb.Actions {
		for _, p := range action.Parameters {
			if p.Name == prowJobID {
				value, ok := p.Value.(string)
				if !ok {
					logrus.Errorf("Cannot determine %s value for %#v", p.Name, jb)
					continue
				}
				return value
			}
		}
	}
	return ""
}

func ( jb * Build ) ProwJobID ( ) string { for _ , action := range jb . Actions { for _ , p := range action . Parameters { if p . Name == prowJobID { value , ok := p . Value . ( string ) if ! ok { logrus . Errorf ( " " , p . Name , jb ) continue } return value } } } return " " }

// ProwJobID extracts the ProwJob identifier for the
// Jenkins build in order to correlate the build with
// a ProwJob. If the build has an empty PROW_JOB_ID
// it didn't start by prow.

ProwJobID extracts the ProwJob identifier for the Jenkins build in order to correlate the build with a ProwJob . If the build has an empty PROW_JOB_ID it didn t start by prow .


func (jb *Build) BuildID() string {
	var buildID string
	hasProwJobID := false
	for _, action := range jb.Actions {
		for _, p := range action.Parameters {
			hasProwJobID = hasProwJobID || p.Name == prowJobID
			if p.Name == statusBuildID {
				value, ok := p.Value.(string)
				if !ok {
					logrus.Errorf("Cannot determine %s value for %#v", p.Name, jb)
					continue
				}
				buildID = value
			}
		}
	}

	if !hasProwJobID {
		return ""
	}
	return buildID
}

func ( jb * Build ) BuildID ( ) string { var buildID string hasProwJobID := false for _ , action := range jb . Actions { for _ , p := range action . Parameters { hasProwJobID = hasProwJobID || p . Name == prowJobID if p . Name == statusBuildID { value , ok := p . Value . ( string ) if ! ok { logrus . Errorf ( " " , p . Name , jb ) continue } buildID = value } } } if ! hasProwJobID { return " " } return buildID }

// BuildID extracts the build identifier used for
// placing and discovering build artifacts.
// This identifier can either originate from tot
// or the snowflake library, depending on how the
// Jenkins operator is configured to run.
// We return an empty string if we are dealing with
// a build that does not have the ProwJobID set
// explicitly, as in that case the Jenkins build has
// not started by prow.

BuildID extracts the build identifier used for placing and discovering build artifacts . This identifier can either originate from tot or the snowflake library depending on how the Jenkins operator is configured to run . We return an empty string if we are dealing with a build that does not have the ProwJobID set explicitly as in that case the Jenkins build has not started by prow .


func (c *Client) CrumbRequest() error {
	if c.authConfig.csrfToken != "" && c.authConfig.csrfRequestField != "" {
		return nil
	}
	c.logger.Debug("CrumbRequest")
	data, err := c.GetSkipMetrics("/crumbIssuer/api/json")
	if err != nil {
		return err
	}
	crumbResp := struct {
		Crumb             string `json:"crumb"`
		CrumbRequestField string `json:"crumbRequestField"`
	}{}
	if err := json.Unmarshal(data, &crumbResp); err != nil {
		return fmt.Errorf("cannot unmarshal crumb response: %v", err)
	}
	c.authConfig.csrfToken = crumbResp.Crumb
	c.authConfig.csrfRequestField = crumbResp.CrumbRequestField
	return nil
}

func ( c * Client ) CrumbRequest ( ) error { if c . authConfig . csrfToken != " " && c . authConfig . csrfRequestField != " " { return nil } c . logger . Debug ( " " ) data , err := c . GetSkipMetrics ( " " ) if err != nil { return err } crumbResp := struct { Crumb string `json:"crumb"` CrumbRequestField string `json:"crumbRequestField"` } { } if err := json . Unmarshal ( data , & crumbResp ) ; err != nil { return fmt . Errorf ( " " , err ) } c . authConfig . csrfToken = crumbResp . Crumb c . authConfig . csrfRequestField = crumbResp . CrumbRequestField return nil }

// CrumbRequest requests a CSRF protection token from Jenkins to
// use it in subsequent requests. Required for Jenkins masters that
// prevent cross site request forgery exploits.

CrumbRequest requests a CSRF protection token from Jenkins to use it in subsequent requests . Required for Jenkins masters that prevent cross site request forgery exploits .


func (c *Client) measure(method, path string, code int, start time.Time) {
	if c.metrics == nil {
		return
	}
	c.metrics.RequestLatency.WithLabelValues(method, path).Observe(time.Since(start).Seconds())
	c.metrics.Requests.WithLabelValues(method, path, fmt.Sprintf("%d", code)).Inc()
}

func ( c * Client ) measure ( method , path string , code int , start time . Time ) { if c . metrics == nil { return } c . metrics . RequestLatency . WithLabelValues ( method , path ) . Observe ( time . Since ( start ) . Seconds ( ) ) c . metrics . Requests . WithLabelValues ( method , path , fmt . Sprintf ( " " , code ) ) . Inc ( ) }

// measure records metrics about the provided method, path, and code.
// start needs to be recorded before doing the request.

measure records metrics about the provided method path and code . start needs to be recorded before doing the request .


func (c *Client) GetSkipMetrics(path string) ([]byte, error) {
	resp, err := c.request(http.MethodGet, path, nil, false)
	if err != nil {
		return nil, err
	}
	return readResp(resp)
}

func ( c * Client ) GetSkipMetrics ( path string ) ( [ ] byte , error ) { resp , err := c . request ( http . MethodGet , path , nil , false ) if err != nil { return nil , err } return readResp ( resp ) }

// GetSkipMetrics fetches the data found in the provided path. It returns the
// content of the response or any errors that occurred during the request or
// http errors. Metrics will not be gathered for this request.

GetSkipMetrics fetches the data found in the provided path . It returns the content of the response or any errors that occurred during the request or http errors . Metrics will not be gathered for this request .


func (c *Client) Get(path string) ([]byte, error) {
	resp, err := c.request(http.MethodGet, path, nil, true)
	if err != nil {
		return nil, err
	}
	return readResp(resp)
}

func ( c * Client ) Get ( path string ) ( [ ] byte , error ) { resp , err := c . request ( http . MethodGet , path , nil , true ) if err != nil { return nil , err } return readResp ( resp ) }

// Get fetches the data found in the provided path. It returns the
// content of the response or any errors that occurred during the
// request or http errors.

Get fetches the data found in the provided path . It returns the content of the response or any errors that occurred during the request or http errors .


func (c *Client) request(method, path string, params url.Values, measure bool) (*http.Response, error) {
	var resp *http.Response
	var err error
	backoff := retryDelay

	urlPath := fmt.Sprintf("%s%s", c.baseURL, path)
	if params != nil {
		urlPath = fmt.Sprintf("%s?%s", urlPath, params.Encode())
	}

	start := time.Now()
	for retries := 0; retries < maxRetries; retries++ {
		resp, err = c.doRequest(method, urlPath)
		if err == nil && resp.StatusCode < 500 {
			break
		} else if err == nil && retries+1 < maxRetries {
			resp.Body.Close()
		}
		// Capture the retry in a metric.
		if measure && c.metrics != nil {
			c.metrics.RequestRetries.Inc()
		}
		time.Sleep(backoff)
		backoff *= 2
	}
	if measure && resp != nil {
		c.measure(method, path, resp.StatusCode, start)
	}
	return resp, err
}

func
(
c
*
Client
)
request
(
method
,
path
string
,
params
url
.
Values
,
measure
bool
)
(
*
http
.
Response
,
error
)
{
var
resp
*
http
.
Response
var
err
error
backoff
:=
retryDelay
urlPath
:=
fmt
.
Sprintf
(
"
"
,
c
.
baseURL
,
path
)
if
params
!=
{
urlPath
=
fmt
.
Sprintf
(
"
"
,
urlPath
,
params
.
Encode
(
)
)
}
start
:=
time
.
Now
(
)
for
retries
:=
0
;
retries
<
maxRetries
;
retries
++
{
resp
,
err
=
c
.
doRequest
(
method
,
urlPath
)
if
err
==
&&
resp
.
StatusCode
<
500
{
break
}
else
if
err
==
&&
retries
+
1
<
maxRetries
{
resp
.
Body
.
Close
(
)
}
// Capture the retry in a metric.
if
measure
&&
c
.
metrics
!=
{
c
.
metrics
.
RequestRetries
.
Inc
(
)
}
time
.
Sleep
(
backoff
)
backoff
*=
2
}
if
measure
&&
resp
!=
{
c
.
measure
(
method
,
path
,
resp
.
StatusCode
,
start
)
}
return
resp
,
err
}

// request executes a request with the provided method and path.
// It retries on transport failures and 500s. measure is provided
// to enable or disable gathering metrics for specific requests
// to avoid high-cardinality metrics.

request executes a request with the provided method and path . It retries on transport failures and 500s . measure is provided to enable or disable gathering metrics for specific requests to avoid high - cardinality metrics .


func (c *Client) doRequest(method, path string) (*http.Response, error) {
	req, err := http.NewRequest(method, path, nil)
	if err != nil {
		return nil, err
	}
	if c.authConfig != nil {
		if c.authConfig.Basic != nil {
			req.SetBasicAuth(c.authConfig.Basic.User, string(c.authConfig.Basic.GetToken()))
		}
		if c.authConfig.BearerToken != nil {
			req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", c.authConfig.BearerToken.GetToken()))
		}
		if c.authConfig.CSRFProtect && c.authConfig.csrfRequestField != "" && c.authConfig.csrfToken != "" {
			req.Header.Set(c.authConfig.csrfRequestField, c.authConfig.csrfToken)
		}
	}
	return c.client.Do(req)
}

func ( c * Client ) doRequest ( method , path string ) ( * http . Response , error ) { req , err := http . NewRequest ( method , path , nil ) if err != nil { return nil , err } if c . authConfig != nil { if c . authConfig . Basic != nil { req . SetBasicAuth ( c . authConfig . Basic . User , string ( c . authConfig . Basic . GetToken ( ) ) ) } if c . authConfig . BearerToken != nil { req . Header . Set ( " " , fmt . Sprintf ( " " , c . authConfig . BearerToken . GetToken ( ) ) ) } if c . authConfig . CSRFProtect && c . authConfig . csrfRequestField != " " && c . authConfig . csrfToken != " " { req . Header . Set ( c . authConfig . csrfRequestField , c . authConfig . csrfToken ) } } return c . client . Do ( req ) }

// doRequest executes a request with the provided method and path
// exactly once. It sets up authentication if the jenkins client
// is configured accordingly. It's up to callers of this function
// to build retries and error handling.

doRequest executes a request with the provided method and path exactly once . It sets up authentication if the jenkins client is configured accordingly . It s up to callers of this function to build retries and error handling .


func getJobName(spec *prowapi.ProwJobSpec) string {
	if spec.JenkinsSpec != nil && spec.JenkinsSpec.GitHubBranchSourceJob && spec.Refs != nil {
		if len(spec.Refs.Pulls) > 0 {
			return fmt.Sprintf("%s/view/change-requests/job/PR-%d", spec.Job, spec.Refs.Pulls[0].Number)
		}

		return fmt.Sprintf("%s/job/%s", spec.Job, spec.Refs.BaseRef)
	}

	return spec.Job
}

func getJobName ( spec * prowapi . ProwJobSpec ) string { if spec . JenkinsSpec != nil && spec . JenkinsSpec . GitHubBranchSourceJob && spec . Refs != nil { if len ( spec . Refs . Pulls ) > 0 { return fmt . Sprintf ( " " , spec . Job , spec . Refs . Pulls [ 0 ] . Number ) } return fmt . Sprintf ( " " , spec . Job , spec . Refs . BaseRef ) } return spec . Job }

// getJobName generates the correct job name for this job type


func getBuildPath(spec *prowapi.ProwJobSpec) string {
	jenkinsJobName := getJobName(spec)
	jenkinsPath := fmt.Sprintf("/job/%s/build", jenkinsJobName)

	return jenkinsPath
}

func getBuildPath ( spec * prowapi . ProwJobSpec ) string { jenkinsJobName := getJobName ( spec ) jenkinsPath := fmt . Sprintf ( " " , jenkinsJobName ) return jenkinsPath }

// getBuildPath builds a path to trigger a regular build for this job


func (c *Client) GetJobInfo(spec *prowapi.ProwJobSpec) (*JobInfo, error) {
	path := getJobInfoPath(spec)
	c.logger.Debugf("getJobInfoPath: %s", path)

	data, err := c.Get(path)

	if err != nil {
		c.logger.Errorf("Failed to get job info: %v", err)
		return nil, err
	}

	var jobInfo JobInfo

	if err := json.Unmarshal(data, &jobInfo); err != nil {
		return nil, fmt.Errorf("Cannot unmarshal job info from API: %v", err)
	}

	c.logger.Tracef("JobInfo: %+v", jobInfo)

	return &jobInfo, nil
}

func ( c * Client ) GetJobInfo ( spec * prowapi . ProwJobSpec ) ( * JobInfo , error ) { path := getJobInfoPath ( spec ) c . logger . Debugf ( " " , path ) data , err := c . Get ( path ) if err != nil { c . logger . Errorf ( " " , err ) return nil , err } var jobInfo JobInfo if err := json . Unmarshal ( data , & jobInfo ) ; err != nil { return nil , fmt . Errorf ( " " , err ) } c . logger . Tracef ( " " , jobInfo ) return & jobInfo , nil }

// GetJobInfo retrieves Jenkins job information


func (c *Client) JobParameterized(jobInfo *JobInfo) bool {
	for _, prop := range jobInfo.Property {
		if prop.ParameterDefinitions != nil && len(prop.ParameterDefinitions) > 0 {
			return true
		}
	}

	return false
}

func ( c * Client ) JobParameterized ( jobInfo * JobInfo ) bool { for _ , prop := range jobInfo . Property { if prop . ParameterDefinitions != nil && len ( prop . ParameterDefinitions ) > 0 { return true } } return false }

// JobParameterized tells us if the Jenkins job for this ProwJob is parameterized